blob: 81dec240e32f2052ef6279ca4e3268c5c4299c56 [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.enterprise.adaptor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.concurrent.atomic.AtomicLong;
/**
* Test cases for {@link DocIdSender}.
*/
public class DocIdSenderTest {
private MockGsaFeedFileMaker fileMaker = new MockGsaFeedFileMaker();
private MockGsaFeedFileSender fileSender = new MockGsaFeedFileSender();
private MockFeedArchiver fileArchiver = new MockFeedArchiver();
private Journal journal = new Journal(new MockTimeProvider());
private Config config = new Config();
private DocIdsMockAdaptor adaptor = new DocIdsMockAdaptor();
private DocIdSender docIdSender = new DocIdSender(fileMaker, fileSender,
fileArchiver, journal, config, adaptor);
private ExceptionHandler runtimeExceptionHandler
= new RuntimeExceptionExceptionHandler();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setup() {
config.setValue("gsa.hostname", "localhost");
config.setValue("gsa.version", "7.2.0-8");
}
@Test
public void testPushDocIdsFromAdaptorNormal() throws Exception {
adaptor.pushItems = new ArrayList<List<DocIdPusher.Record>>();
DocIdPusher.Record[] records = new DocIdPusher.Record[6];
for (int i = 0; i < records.length; i++) {
DocId id = new DocId("test" + i);
records[i] = new DocIdPusher.Record.Builder(id).build();
}
List<DocIdPusher.Record> infos = new ArrayList<DocIdPusher.Record>();
infos.add(records[0]);
infos.add(records[1]);
infos.add(records[2]);
infos.add(records[3]);
infos.add(records[4]);
adaptor.pushItems.add(infos);
infos = new ArrayList<DocIdPusher.Record>();
infos.add(records[5]);
adaptor.pushItems.add(infos);
config.setValue("feed.maxUrls", "2");
config.setValue("feed.name", "testing");
docIdSender.pushFullDocIdsFromAdaptor(runtimeExceptionHandler);
assertEquals(4, fileMaker.i);
assertEquals(Arrays.asList(new String[] {
"testing", "testing", "testing", "testing",
}), fileMaker.names);
assertEquals(Arrays.asList(new List[] {
Arrays.asList(new DocIdPusher.Record[] {records[0], records[1]}),
Arrays.asList(new DocIdPusher.Record[] {records[2], records[3]}),
Arrays.asList(new DocIdPusher.Record[] {records[4]}),
Arrays.asList(new DocIdPusher.Record[] {records[5]}),
}), fileMaker.recordses);
assertEquals(Arrays.asList(new String[] {
"testing", "testing", "testing", "testing",
}), fileSender.datasources);
assertEquals(Arrays.asList(new String[] {
"0", "1", "2", "3",
}), fileSender.xmlStrings);
assertEquals(Arrays.asList(new String[] {
"0", "1", "2", "3",
}), fileArchiver.feeds);
assertTrue(fileArchiver.failedFeeds.isEmpty());
}
@Test
public void testPushDocIdsNoHandler() throws Exception {
// Don't send anything.
adaptor.pushItems = new ArrayList<List<DocIdPusher.Record>>();
thrown.expect(NullPointerException.class);
docIdSender.pushFullDocIdsFromAdaptor(null);
}
@Test
public void testPushDocIdsIterrupted() throws Exception {
MockAdaptor adaptor = new MockAdaptor() {
@Override
public void getDocIds(DocIdPusher pusher) throws InterruptedException {
throw new InterruptedException();
}
};
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
thrown.expect(InterruptedException.class);
docIdSender.pushFullDocIdsFromAdaptor(runtimeExceptionHandler);
}
@Test
public void testPushDocIdsFailure() throws Exception {
class FailureAdaptor extends MockAdaptor {
private int times;
@Override
public void getDocIds(DocIdPusher pusher) throws IOException {
times++;
throw new IOException();
}
}
class TryTwiceExceptionHandler implements ExceptionHandler {
@Override
public boolean handleException(Exception ex, int ntries) {
return ntries < 2;
}
}
FailureAdaptor adaptor = new FailureAdaptor();
ExceptionHandler errorHandler = new TryTwiceExceptionHandler();
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
docIdSender.pushFullDocIdsFromAdaptor(errorHandler);
assertEquals(2, adaptor.times);
}
@Test
public void testPushSizedBatchFailed() throws Exception {
fileSender = new MockGsaFeedFileSender() {
@Override
public void sendMetadataAndUrl(String datasource,
String xmlString, boolean useCompression)
throws IOException {
throw new IOException();
}
};
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
List<DocId> ids = Arrays.asList(new DocId[] {new DocId("test")});
NeverRetryExceptionHandler errorHandler = new NeverRetryExceptionHandler();
docIdSender.pushDocIds(ids, errorHandler);
assertEquals(1, errorHandler.failed);
assertTrue(fileArchiver.feeds.isEmpty());
}
public void testPushSizedBatchRetrying() throws Exception {
fileSender = new MockGsaFeedFileSender() {
@Override
public void sendMetadataAndUrl(String datasource,
String xmlString, boolean useCompression)
throws IOException {
throw new IOException();
}
};
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
List<DocId> ids = Arrays.asList(new DocId[] {new DocId("test")});
NeverRetryExceptionHandler errorHandler = new NeverRetryExceptionHandler() {
@Override
public boolean handleException(Exception ex, int ntries) {
super.handleException(ex, ntries);
return ntries < 2;
}
};
docIdSender.pushDocIds(ids, errorHandler);
assertEquals(2, errorHandler.failed);
assertTrue(fileArchiver.feeds.isEmpty());
}
@Test
public void testPushInterruptedFirstBatch() throws Exception {
fileSender = new MockGsaFeedFileSender() {
@Override
public void sendMetadataAndUrl(String datasource,
String xmlString, boolean useCompression)
throws IOException {
throw new IOException();
}
};
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
List<DocId> ids = Arrays.asList(new DocId("test"), new DocId("test2"));
Thread.currentThread().interrupt();
thrown.expect(InterruptedException.class);
docIdSender.pushDocIds(ids);
assertTrue(fileArchiver.feeds.isEmpty());
}
@Test
public void testPushInterruptedLaterBatch() throws Exception {
final AtomicLong batchCount = new AtomicLong();
fileSender = new MockGsaFeedFileSender() {
@Override
public void sendMetadataAndUrl(String datasource,
String xmlString, boolean useCompression)
throws IOException {
long count = batchCount.incrementAndGet();
if (count >= 2) {
throw new IOException();
}
}
};
config.setValue("feed.maxUrls", "1");
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
List<DocId> ids = Arrays.asList(new DocId("test"), new DocId("test2"));
Thread.currentThread().interrupt();
assertEquals(new DocId("test2"), docIdSender.pushDocIds(ids));
assertTrue(Thread.currentThread().isInterrupted());
}
@Test
public void testPushGroupsNormal() throws Exception {
// Order of iteration matters
Map<GroupPrincipal, Collection<Principal>> groups
= new TreeMap<GroupPrincipal, Collection<Principal>>();
groups.put(new GroupPrincipal("g1"),
Arrays.asList(new UserPrincipal("u1"), new GroupPrincipal("g2")));
groups.put(new GroupPrincipal("g2"),
Arrays.asList(new UserPrincipal("u2"), new GroupPrincipal("g3")));
groups.put(new GroupPrincipal("g3"),
Arrays.asList(new UserPrincipal("u3"), new GroupPrincipal("g4")));
groups = Collections.unmodifiableMap(groups);
// I'm sorry.
List<List<Map.Entry<GroupPrincipal, Collection<Principal>>>> goldenGroups
= new ArrayList<List<Map.Entry<GroupPrincipal,
Collection<Principal>>>>();
{
List<Map.Entry<GroupPrincipal, Collection<Principal>>> tmp
= new ArrayList<Map.Entry<GroupPrincipal, Collection<Principal>>>();
tmp.add(new SimpleImmutableEntry<GroupPrincipal, Collection<Principal>>(
new GroupPrincipal("g1"), groups.get(new GroupPrincipal("g1"))));
tmp.add(new SimpleImmutableEntry<GroupPrincipal, Collection<Principal>>(
new GroupPrincipal("g2"), groups.get(new GroupPrincipal("g2"))));
goldenGroups.add(Collections.unmodifiableList(tmp));
goldenGroups.add(Collections.
<Map.Entry<GroupPrincipal, Collection<Principal>>>singletonList(
new SimpleImmutableEntry<GroupPrincipal, Collection<Principal>>(
new GroupPrincipal("g3"), groups.get(new GroupPrincipal("g3")))));
goldenGroups = Collections.unmodifiableList(goldenGroups);
}
config.setValue("feed.maxUrls", "2");
docIdSender.pushGroupDefinitions(groups, false, null);
assertEquals(2, fileMaker.i);
assertEquals(goldenGroups, fileMaker.groupses);
assertEquals(Arrays.asList(new String[] {
"0", "1",
}), fileSender.xmlStrings);
assertEquals(Arrays.asList(new String[] {
"0", "1",
}), fileArchiver.feeds);
assertTrue(fileArchiver.failedFeeds.isEmpty());
}
@Test
public void testNamedResources() throws Exception {
config.setValue("feed.name", "testing");
assertNull(docIdSender.pushNamedResources(
Collections.singletonMap(new DocId("test"), Acl.EMPTY),
new NeverRetryExceptionHandler()));
}
@Test
public void testNamedResourcesFailed() throws Exception {
fileSender = new MockGsaFeedFileSender() {
@Override
public void sendMetadataAndUrl(String datasource,
String xmlString, boolean useCompression)
throws IOException {
throw new IOException();
}
};
docIdSender = new DocIdSender(fileMaker, fileSender, fileArchiver, journal,
config, adaptor);
Map<DocId, Acl> resources = new TreeMap<DocId, Acl>();
resources.put(new DocId("aaa"), Acl.EMPTY);
resources.put(new DocId("bbb"), Acl.EMPTY);
assertEquals(new DocId("aaa"), docIdSender.pushNamedResources(resources,
new NeverRetryExceptionHandler()));
}
@Test
public void testNullNamedResource() throws Exception {
thrown.expect(NullPointerException.class);
docIdSender.pushNamedResources(
Collections.singletonMap(new DocId("test"), (Acl) null));
}
@Test
public void testNullNamedResourceAcl() throws Exception {
thrown.expect(NullPointerException.class);
docIdSender.pushNamedResources(
Collections.singletonMap((DocId) null, Acl.EMPTY));
}
@Test
public void testAclItemToString() {
DocId id = new DocId("foxtrot");
Acl acl = Acl.EMPTY;
String golden = "AclItem(" + id + ",null," + acl + ")";
assertEquals(golden, "" + new DocIdSender.AclItem(id, acl));
}
private static class MockGsaFeedFileMaker extends GsaFeedFileMaker {
List<String> names = new ArrayList<String>();
List<List<? extends DocIdSender.Item>> recordses
= new ArrayList<List<? extends DocIdSender.Item>>();
// Don't use generics because of limitations in Java
List<Object> groupses = new ArrayList<Object>();
int i;
public MockGsaFeedFileMaker() {
super(null, new AclTransform(Arrays.<AclTransform.Rule>asList()));
}
@Override
public String makeMetadataAndUrlXml(String name,
List<? extends DocIdSender.Item> items) {
names.add(name);
recordses.add(items);
return "" + i++;
}
@Override
public <T extends Collection<Principal>> String makeGroupDefinitionsXml(
Collection<Map.Entry<GroupPrincipal, T>> items,
boolean caseSensitiveMembers) {
groupses.add(new ArrayList<Map.Entry<GroupPrincipal, T>>(items));
return "" + i++;
}
}
private static class MockGsaFeedFileSender extends GsaFeedFileSender {
List<String> datasources = new ArrayList<String>();
List<String> groupsources = new ArrayList<String>();
List<String> xmlStrings = new ArrayList<String>();
public MockGsaFeedFileSender() {
super("localhost", /*secure=*/ false, Charset.forName("UTF-8"));
}
@Override
public void sendMetadataAndUrl(String datasource,
String xmlString, boolean useCompression)
throws IOException {
datasources.add(datasource);
xmlStrings.add(xmlString);
}
@Override
public void sendGroups(String groupsource, String xmlString,
boolean useCompression) throws IOException {
groupsources.add(groupsource);
xmlStrings.add(xmlString);
}
}
private static class MockFeedArchiver implements FeedArchiver {
List<String> feeds = new ArrayList<String>();
List<String> failedFeeds = new ArrayList<String>();
@Override
public void saveFeed(String feedName, String feedXml) {
feeds.add(feedXml);
}
@Override
public void saveFailedFeed(String feedName, String feedXml) {
failedFeeds.add(feedXml);
}
}
private static class RuntimeExceptionExceptionHandler
implements ExceptionHandler {
@Override
public boolean handleException(Exception ex, int ntries) {
throw new TriggeredException(ex);
}
public static class TriggeredException extends RuntimeException {
public TriggeredException(Throwable cause) {
super(cause);
}
}
}
private static class NeverRetryExceptionHandler implements ExceptionHandler {
private int failed;
@Override
public boolean handleException(Exception ex, int ntries) {
failed++;
return false;
}
}
private static class DocIdsMockAdaptor extends MockAdaptor {
public List<List<DocIdPusher.Record>> pushItems;
public int timesGetDocIdsCalled;
/**
* Throws a {@link NullPointerException} unless {@link #pushItems} has been
* set.
*/
@Override
public void getDocIds(DocIdPusher pusher) throws InterruptedException,
IOException {
timesGetDocIdsCalled++;
for (List<DocIdPusher.Record> infos : pushItems) {
pusher.pushRecords(infos);
}
}
}
}