/* * Copyright 2013-2015 EMC Corporation. 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. * A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * or in the "license" file accompanying this file. This file 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.emc.ecs.sync; import com.emc.ecs.sync.config.SyncConfig; import com.emc.ecs.sync.config.SyncOptions; import com.emc.ecs.sync.config.annotation.FilterConfig; import com.emc.ecs.sync.filter.AbstractFilter; import com.emc.ecs.sync.filter.InternalFilter; import com.emc.ecs.sync.model.ObjectContext; import com.emc.ecs.sync.model.ObjectStatus; import com.emc.ecs.sync.model.ObjectSummary; import com.emc.ecs.sync.model.SyncObject; import com.emc.ecs.sync.service.DbService; import com.emc.ecs.sync.service.SqliteDbService; import com.emc.ecs.sync.service.SyncRecord; import com.emc.ecs.sync.test.TestUtil; import org.junit.Assert; import org.junit.Test; import java.io.File; import java.util.Collections; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; public class SyncProcessTest { @Test public void testSourceObjectNotFound() throws Exception { com.emc.ecs.sync.config.storage.TestConfig testConfig = new com.emc.ecs.sync.config.storage.TestConfig(); testConfig.withObjectCount(0).withReadData(true).withDiscardData(false); File sourceIdList = TestUtil.writeTempFile("this-id-does-not-exist"); SyncOptions options = new SyncOptions().withRememberFailed(true).withSourceListFile(sourceIdList.getAbsolutePath()); SyncConfig syncConfig = new SyncConfig().withOptions(options).withSource(testConfig).withTarget(testConfig); DbService dbService = new SqliteDbService(":memory:"); EcsSync sync = new EcsSync(); sync.setSyncConfig(syncConfig); sync.setDbService(dbService); sync.run(); Assert.assertEquals(0, sync.getStats().getObjectsComplete()); Assert.assertEquals(1, sync.getStats().getObjectsFailed()); Assert.assertEquals(1, sync.getStats().getFailedObjects().size()); String failedId = sync.getStats().getFailedObjects().iterator().next(); Assert.assertNotNull(failedId); ObjectContext context = new ObjectContext().withOptions(options).withSourceSummary(new ObjectSummary(failedId, false, 0)); SyncRecord syncRecord = dbService.getSyncRecord(context); Assert.assertNotNull(syncRecord); Assert.assertEquals(ObjectStatus.Error, syncRecord.getStatus()); } @Test public void testRetryQueue() throws Exception { int retries = 3; com.emc.ecs.sync.config.storage.TestConfig testConfig = new com.emc.ecs.sync.config.storage.TestConfig(); testConfig.withObjectCount(500).withMaxSize(1024).withObjectOwner("Boo Radley").withReadData(true).withDiscardData(false); ErrorThrowingConfig filterConfig = new ErrorThrowingConfig().withRetriesExpected(retries); SyncOptions options = new SyncOptions().withThreadCount(8).withRetryAttempts(retries); SyncConfig syncConfig = new SyncConfig().withOptions(options).withSource(testConfig).withTarget(testConfig); syncConfig.withFilters(Collections.singletonList(filterConfig)); final EcsSync sync = new EcsSync(); sync.setSyncConfig(syncConfig); ExecutorService service = Executors.newSingleThreadExecutor(); Future future = service.submit(new Runnable() { @Override public void run() { sync.run(); } }); service.shutdown(); while (!sync.isRunning()) Thread.sleep(200); // wait for threads to kick off Thread.sleep(200); // wait for retries to queue Assert.assertTrue(sync.getObjectsAwaitingRetry() > 0); future.get(); // wait for sync to finish Assert.assertEquals(0, sync.getStats().getObjectsFailed()); Assert.assertEquals(sync.getStats().getObjectsComplete(), sync.getEstimatedTotalObjects()); Assert.assertEquals((retries + 1) * sync.getStats().getObjectsComplete(), filterConfig.getTotalAttempts().get()); } @FilterConfig(cliName = "98s76df8s7d6fs87d6f") @InternalFilter public static class ErrorThrowingConfig { private int retriesExpected; private AtomicInteger totalAttempts = new AtomicInteger(); public int getRetriesExpected() { return retriesExpected; } public void setRetriesExpected(int retriesExpected) { this.retriesExpected = retriesExpected; } public ErrorThrowingConfig withRetriesExpected(int retriesExpected) { setRetriesExpected(retriesExpected); return this; } public AtomicInteger getTotalAttempts() { return totalAttempts; } public void setTotalAttempts(AtomicInteger totalAttempts) { this.totalAttempts = totalAttempts; } public ErrorThrowingConfig withTotalTransfers(AtomicInteger totalTransfers) { setTotalAttempts(totalTransfers); return this; } } public static class ErrorThrowingFilter extends AbstractFilter<ErrorThrowingConfig> { @Override public void filter(ObjectContext objectContext) { config.getTotalAttempts().incrementAndGet(); if (objectContext.getFailures() == config.getRetriesExpected()) getNext().filter(objectContext); else throw new RuntimeException("Nope, not yet (" + objectContext.getFailures() + ")"); } @Override public SyncObject reverseFilter(ObjectContext objectContext) { return getNext().reverseFilter(objectContext); } } }