Compare commits

..

1 Commits

10 changed files with 73 additions and 93 deletions

View File

@ -22,7 +22,6 @@
*/ */
package com.iluwatar.ambassador; package com.iluwatar.ambassador;
import com.iluwatar.ambassador.util.RandomProvider;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -32,10 +31,9 @@ import static java.lang.Thread.sleep;
* A remote legacy application represented by a Singleton implementation. * A remote legacy application represented by a Singleton implementation.
*/ */
public class RemoteService implements RemoteServiceInterface { public class RemoteService implements RemoteServiceInterface {
static final int THRESHOLD = 200;
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class); private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class);
private static RemoteService service = null; private static RemoteService service = null;
private final RandomProvider randomProvider;
static synchronized RemoteService getRemoteService() { static synchronized RemoteService getRemoteService() {
if (service == null) { if (service == null) {
@ -44,33 +42,24 @@ public class RemoteService implements RemoteServiceInterface {
return service; return service;
} }
private RemoteService() { private RemoteService() {}
this(Math::random);
}
/**
* This constuctor is used for testing purposes only.
*/
RemoteService(RandomProvider randomProvider) {
this.randomProvider = randomProvider;
}
/** /**
* Remote function takes a value and multiplies it by 10 taking a random amount of time. * Remote function takes a value and multiplies it by 10 taking a random amount of time.
* Will sometimes return -1. This imitates connectivity issues a client might have to account for. * Will sometimes return -1. This imitates connectivity issues a client might have to account for.
* @param value integer value to be multiplied. * @param value integer value to be multiplied.
* @return if waitTime is less than {@link RemoteService#THRESHOLD}, it returns value * 10, * @return if waitTime is more than 200ms, it returns value * 10, otherwise -1.
* otherwise {@link RemoteServiceInterface#FAILURE}.
*/ */
@Override @Override
public long doRemoteFunction(int value) { public long doRemoteFunction(int value) {
long waitTime = (long) Math.floor(randomProvider.random() * 1000); long waitTime = (long) Math.floor(Math.random() * 1000);
try { try {
sleep(waitTime); sleep(waitTime);
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOGGER.error("Thread sleep state interrupted", e); LOGGER.error("Thread sleep state interrupted", e);
} }
return waitTime <= THRESHOLD ? value * 10 : FAILURE; return waitTime >= 200 ? value * 10 : -1;
} }
} }

View File

@ -26,7 +26,6 @@ package com.iluwatar.ambassador;
* Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). * Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}).
*/ */
interface RemoteServiceInterface { interface RemoteServiceInterface {
int FAILURE = -1;
long doRemoteFunction(int value) throws Exception; long doRemoteFunction(int value) throws Exception;
} }

View File

@ -59,15 +59,15 @@ public class ServiceAmbassador implements RemoteServiceInterface {
private long safeCall(int value) { private long safeCall(int value) {
int retries = 0; int retries = 0;
long result = FAILURE; long result = -1;
for (int i = 0; i < RETRIES; i++) { for (int i = 0; i < RETRIES; i++) {
if (retries >= RETRIES) { if (retries >= RETRIES) {
return FAILURE; return -1;
} }
if ((result = checkLatency(value)) == FAILURE) { if ((result = checkLatency(value)) == -1) {
LOGGER.info("Failed to reach remote: (" + (i + 1) + ")"); LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
retries++; retries++;
try { try {

View File

@ -1,8 +0,0 @@
package com.iluwatar.ambassador.util;
/**
* An interface for randomness. Useful for testing purposes.
*/
public interface RandomProvider {
double random();
}

View File

@ -24,8 +24,6 @@ package com.iluwatar.ambassador;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Test for {@link Client} * Test for {@link Client}
*/ */
@ -37,6 +35,6 @@ public class ClientTest {
Client client = new Client(); Client client = new Client();
long result = client.useService(10); long result = client.useService(10);
assertTrue(result == 100 || result == RemoteService.FAILURE); assert result == 100 || result == -1;
} }
} }

View File

@ -22,43 +22,16 @@
*/ */
package com.iluwatar.ambassador; package com.iluwatar.ambassador;
import com.iluwatar.ambassador.util.RandomProvider;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Test for {@link RemoteService} * Test for {@link RemoteService}
*/ */
public class RemoteServiceTest { public class RemoteServiceTest {
@Test @Test
public void testFailedCall() { public void test() {
RemoteService remoteService = new RemoteService( long result = RemoteService.getRemoteService().doRemoteFunction(10);
new StaticRandomProvider(0.21)); assert result == 100 || result == -1;
long result = remoteService.doRemoteFunction(10);
assertEquals(RemoteServiceInterface.FAILURE, result);
}
@Test
public void testSuccessfulCall() {
RemoteService remoteService = new RemoteService(
new StaticRandomProvider(0.2));
long result = remoteService.doRemoteFunction(10);
assertEquals(100, result);
}
private class StaticRandomProvider implements RandomProvider {
private double value;
StaticRandomProvider(double value) {
this.value = value;
}
@Override
public double random() {
return value;
}
} }
} }

View File

@ -24,8 +24,6 @@ package com.iluwatar.ambassador;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Test for {@link ServiceAmbassador} * Test for {@link ServiceAmbassador}
*/ */
@ -34,6 +32,6 @@ public class ServiceAmbassadorTest {
@Test @Test
public void test() { public void test() {
long result = new ServiceAmbassador().doRemoteFunction(10); long result = new ServiceAmbassador().doRemoteFunction(10);
assertTrue(result == 100 || result == RemoteServiceInterface.FAILURE); assert result == 100 || result == -1;
} }
} }

View File

@ -0,0 +1,10 @@
package com.iluwatar.balking;
import java.util.concurrent.TimeUnit;
/**
* An interface to simulate delay while executing some work.
*/
public interface DelayProvider {
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
}

View File

@ -25,17 +25,38 @@ package com.iluwatar.balking;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/** /**
* Washing machine class * Washing machine class
*/ */
public class WashingMachine { public class WashingMachine {
private static final Logger LOGGER = LoggerFactory.getLogger(WashingMachine.class); private static final Logger LOGGER = LoggerFactory.getLogger(WashingMachine.class);
private final DelayProvider delayProvider;
private WashingMachineState washingMachineState; private WashingMachineState washingMachineState;
/**
* Creates a new instance of WashingMachine
*/
public WashingMachine() { public WashingMachine() {
washingMachineState = WashingMachineState.ENABLED; this((interval, timeUnit, task) -> {
try {
Thread.sleep(timeUnit.toMillis(interval));
} catch (InterruptedException ie) {
ie.printStackTrace();
}
task.run();
});
}
/**
* Creates a new instance of WashingMachine using provided delayProvider. This constructor is used only for
* unit testing purposes.
*/
public WashingMachine(DelayProvider delayProvider) {
this.delayProvider = delayProvider;
this.washingMachineState = WashingMachineState.ENABLED;
} }
public WashingMachineState getWashingMachineState() { public WashingMachineState getWashingMachineState() {
@ -56,12 +77,8 @@ public class WashingMachine {
washingMachineState = WashingMachineState.WASHING; washingMachineState = WashingMachineState.WASHING;
} }
LOGGER.info("{}: Doing the washing", Thread.currentThread().getName()); LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
try {
Thread.sleep(50); this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
endOfWashing();
} }
/** /**

View File

@ -22,11 +22,8 @@
*/ */
package com.iluwatar.balking; package com.iluwatar.balking;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -36,32 +33,39 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
*/ */
public class WashingMachineTest { public class WashingMachineTest {
private volatile WashingMachineState machineStateGlobal; private FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
@Disabled
@Test @Test
public void wash() throws Exception { public void wash() {
WashingMachine washingMachine = new WashingMachine(); WashingMachine washingMachine = new WashingMachine(fakeDelayProvider);
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(washingMachine::wash); washingMachine.wash();
executorService.execute(() -> { washingMachine.wash();
washingMachine.wash();
machineStateGlobal = washingMachine.getWashingMachineState(); WashingMachineState machineStateGlobal = washingMachine.getWashingMachineState();
});
executorService.shutdown(); fakeDelayProvider.task.run();
try {
executorService.awaitTermination(10, TimeUnit.SECONDS); // washing machine remains in washing state
} catch (InterruptedException ie) {
ie.printStackTrace();
}
assertEquals(WashingMachineState.WASHING, machineStateGlobal); assertEquals(WashingMachineState.WASHING, machineStateGlobal);
// washing machine goes back to enabled state
assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
} }
@Test @Test
public void endOfWashing() throws Exception { public void endOfWashing() {
WashingMachine washingMachine = new WashingMachine(); WashingMachine washingMachine = new WashingMachine();
washingMachine.wash(); washingMachine.wash();
assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState()); assertEquals(WashingMachineState.ENABLED, washingMachine.getWashingMachineState());
} }
private class FakeDelayProvider implements DelayProvider {
private Runnable task;
@Override
public void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task) {
this.task = task;
}
}
} }