Refactored and fixed NetworkVirtualPrinter

android-scanner
Tal Moran 2017-06-25 17:51:25 +03:00
parent 6f35f105c5
commit 324a079a90
7 changed files with 149 additions and 154 deletions

View File

@ -26,7 +26,7 @@ import static org.junit.Assert.*;
public class PollingStationScannerTest { public class PollingStationScannerTest {
final Logger logger = LoggerFactory.getLogger(getClass()); final Logger logger = LoggerFactory.getLogger(getClass());
private PollingStationScanner.PollingStationServer scanner; private PollingStationScanner.PollingStationServer scanner;
private static final String CONTEXT_PATH = "/scan"; private static final String SCAN_CONTEXT_PATH = "/scan";
/** /**
* Waiting time for transmission in ms. * Waiting time for transmission in ms.
@ -39,9 +39,6 @@ public class PollingStationScannerTest {
BlockingQueue<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1); BlockingQueue<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1);
private Throwable thrown;
private class ScanHandler implements FutureCallback<ScannedData> { private class ScanHandler implements FutureCallback<ScannedData> {
@Override @Override
public void onSuccess(ScannedData result) { public void onSuccess(ScannedData result) {
@ -49,9 +46,7 @@ public class PollingStationScannerTest {
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) { }
thrown = t;
}
} }
@ -63,11 +58,9 @@ public class PollingStationScannerTest {
logger.debug("Setting up Scanner WebApp!"); logger.debug("Setting up Scanner WebApp!");
scanner = new PollingStationWebScanner(0, CONTEXT_PATH); scanner = new PollingStationWebScanner(0, SCAN_CONTEXT_PATH);
scanner.subscribe(new ScanHandler()); scanner.subscribe(new ScanHandler());
thrown = null;
try { try {
serverData = scanner.start(true); serverData = scanner.start(true);
} catch (Exception e) { } catch (Exception e) {
@ -94,13 +87,13 @@ public class PollingStationScannerTest {
void checkResult(PollingStation.ScannedBallot ballot, long timeout) throws Exception { void checkResult(PollingStation.ScannedBallot ballot, long timeout) throws Exception {
ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS); ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS);
assertEquals("Expecting ballot in received scan", ScannedData.DataCase.BALLOT, arrivedData.getDataCase()); assertEquals("Expecting ballot in received scan", ScannedData.DataCase.BALLOT, arrivedData.getDataCase());
assertEquals("Recieved scan does not match transmission", ballot, arrivedData.getBallot()); assertEquals("Received scan does not match transmission", ballot, arrivedData.getBallot());
} }
void checkResult(PollingStation.ScanError error, long timeout) throws Exception { void checkResult(PollingStation.ScanError error, long timeout) throws Exception {
ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS); ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS);
assertEquals("Expecting error in received scan", ScannedData.DataCase.ERROR, arrivedData.getDataCase()); assertEquals("Expecting error in received scan", ScannedData.DataCase.ERROR, arrivedData.getDataCase());
assertEquals("Recieved scan does not match transmission", error, arrivedData.getError()); assertEquals("Received scan does not match transmission", error, arrivedData.getError());
} }

View File

@ -4,6 +4,9 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.BoolValue; import com.google.protobuf.BoolValue;
import meerkat.protobuf.Crypto; import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation.*; import meerkat.protobuf.PollingStation.*;
import java.util.concurrent.Future;
/** /**
* Created by Arbel on 05/05/2016. * Created by Arbel on 05/05/2016.
* An interface for the scanner used by the Polling Station Committee * An interface for the scanner used by the Polling Station Committee

View File

@ -3,6 +3,7 @@ package meerkat.voting;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.Message; import com.google.protobuf.Message;
import meerkat.crypto.DigitalSignature; import meerkat.crypto.DigitalSignature;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Crypto.*; import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.Crypto.Signature; import meerkat.protobuf.Crypto.Signature;
@ -29,6 +30,11 @@ public class ToySignature implements DigitalSignature {
return signerID; return signerID;
} }
@Override
public SignatureVerificationKey getSignerPublicKey() {
return null;
}
@Override @Override
public void updateContent(Message msg) throws SignatureException { public void updateContent(Message msg) throws SignatureException {
msgByteString = msg.toByteString(); msgByteString = msg.toByteString();
@ -47,6 +53,16 @@ public class ToySignature implements DigitalSignature {
} }
@Override
public void loadVerificationCertificate(SignatureVerificationKey cert) throws CertificateException {
throw new UnsupportedOperationException();
}
@Override
public ByteString computeCertificateFingerprint(SignatureVerificationKey cert) throws CertificateException {
return null;
}
@Override @Override
public void loadVerificationCertificates(InputStream certStream) throws CertificateException { public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();

View File

@ -86,31 +86,27 @@ public abstract class AsyncRunnableOutputDevice implements BallotOutputDevice, R
@Override @Override
public void commitToBallot(PlaintextBallot plaintextBallot, public void commitToBallot(PlaintextBallot plaintextBallot,
SignedEncryptedBallot signedEncryptedBallot, SignedEncryptedBallot signedEncryptedBallot,
FutureCallback<Void> callback) { ControllerCallback<Void> callback) {
logger.debug("Output interface call to commit to ballot"); logger.debug("Output interface call to commit to ballot");
queue.clear(); queue.add(new CommitOutputCommand(plaintextBallot, signedEncryptedBallot, callback));
queue.add(new CommitOutputCommand(plaintextBallot, signedEncryptedBallot, (OutputDeviceCommitCallback)callback));
} }
@Override @Override
public void audit(BallotSecrets ballotSecrets, FutureCallback<Void> callback) { public void audit(BallotSecrets ballotSecrets, ControllerCallback<Void> callback) {
logger.debug("an interface call to audit"); logger.debug("an interface call to audit");
queue.clear(); queue.add(new AuditOutputCommand(ballotSecrets, callback));
queue.add(new AuditOutputCommand(ballotSecrets, (OutputDeviceFinalizeCallback)callback));
} }
@Override @Override
public void castBallot(FutureCallback<Void> callback) { public void castBallot(ControllerCallback<Void> callback) {
logger.debug("an interface call to cast ballot"); logger.debug("an interface call to cast ballot");
queue.clear(); queue.add(new CastOutputCommand(callback));
queue.add(new CastOutputCommand((OutputDeviceFinalizeCallback)callback));
} }
@Override @Override
public void cancelBallot(FutureCallback<Void> callback) { public void cancelBallot(ControllerCallback<Void> callback) {
logger.debug("an interface call to cancel the output"); logger.debug("an interface call to cancel the output");
queue.clear(); queue.add(new CancelOutputCommand(callback));
queue.add(new CancelOutputCommand((ControllerCallback<Void>)callback));
} }

View File

@ -2,6 +2,7 @@ package meerkat.voting.output;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Voting.*;
import meerkat.voting.controller.callbacks.ControllerCallback;
/** /**
* An interface for the device in which we output the ballots. * An interface for the device in which we output the ballots.
@ -16,26 +17,26 @@ public interface BallotOutputDevice {
*/ */
public void commitToBallot(PlaintextBallot plaintextBallot, public void commitToBallot(PlaintextBallot plaintextBallot,
SignedEncryptedBallot encryptedBallot, SignedEncryptedBallot encryptedBallot,
FutureCallback<Void> callback); ControllerCallback<Void> callback);
/** /**
* Voter chose 'audit'. Output the ballot secrets to prove correctness of the encryption. * Voter chose 'audit'. Output the ballot secrets to prove correctness of the encryption.
* @param ballotSecrets - the secrets of the encryption * @param ballotSecrets - the secrets of the encryption
* @param callback - a callback object which expects no return value * @param callback - a callback object which expects no return value
*/ */
public void audit(BallotSecrets ballotSecrets, FutureCallback<Void> callback); public void audit(BallotSecrets ballotSecrets, ControllerCallback<Void> callback);
/** /**
* Voter chose 'cast'. Finalize the ballot for use in the polling station * Voter chose 'cast'. Finalize the ballot for use in the polling station
* @param callback - a callback object which expects no return value * @param callback - a callback object which expects no return value
*/ */
public void castBallot(FutureCallback<Void> callback); public void castBallot(ControllerCallback<Void> callback);
/** /**
* Cancelling the current ballot. This clears the state of the OutputDevice if the implementation has any such state. * Cancelling the current ballot. This clears the state of the OutputDevice if the implementation has any such state.
* @param callback - a callback object which expects no return value * @param callback - a callback object which expects no return value
*/ */
public void cancelBallot(FutureCallback<Void> callback); public void cancelBallot(ControllerCallback<Void> callback);
/** /**
* A method for shutting down the Output Device * A method for shutting down the Output Device

View File

@ -2,6 +2,11 @@ package meerkat.voting.output;
import com.google.protobuf.BoolValue; import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignature;
import meerkat.crypto.DigitalSignatureGenerator;
import meerkat.pollingstation.PollingStationScanner;
import meerkat.pollingstation.ScannerClientAPI;
import meerkat.protobuf.PollingStation;
import meerkat.protobuf.PollingStation.ScannedData; import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.protobuf.Voting.SignedEncryptedBallot; import meerkat.protobuf.Voting.SignedEncryptedBallot;
import meerkat.rest.Constants; import meerkat.rest.Constants;
@ -25,15 +30,18 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {
private static final Logger logger = LoggerFactory.getLogger(NetworkVirtualPrinter.class); private static final Logger logger = LoggerFactory.getLogger(NetworkVirtualPrinter.class);
private ByteString channelIdentifier; private ByteString channelIdentifier;
private SignedEncryptedBallot signedEncryptedBallot; private SignedEncryptedBallot signedEncryptedBallot;
private final WebTarget successfulPrintTarget;
public NetworkVirtualPrinter(String address) { PollingStationScanner.ScannerClient scannerClient;
public NetworkVirtualPrinter(PollingStation.ConnectionServerData serverData, DigitalSignature printerSigner) {
super(); super();
logger.info("A NetworkVirtualPrinter is constructed"); logger.info("A NetworkVirtualPrinter is constructed");
Client client = ClientBuilder.newClient(); scannerClient = new ScannerClientAPI(printerSigner);
client.register(ProtobufMessageBodyReader.class); if (!scannerClient.connect(serverData)) {
client.register(ProtobufMessageBodyWriter.class); logger.error("Couldn't connect to polling station scan server!");
successfulPrintTarget = client.target(address).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH); // TODO: Use a checked exception?
throw new RuntimeException("Connection Error");
}
resetState(); resetState();
} }
@ -44,6 +52,7 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {
* When the voter chooses to Cast the ballot, these details are sent over the wire. * When the voter chooses to Cast the ballot, these details are sent over the wire.
* @param command a CommitOutputCommand with the signed encryption of the ballot * @param command a CommitOutputCommand with the signed encryption of the ballot
*/ */
@Override
public void doCommitToBallot(CommitOutputCommand command) { public void doCommitToBallot(CommitOutputCommand command) {
logger.debug("entered method doCommitToBallot"); logger.debug("entered method doCommitToBallot");
channelIdentifier = command.getChannelIdentifierByteString(); channelIdentifier = command.getChannelIdentifierByteString();
@ -56,6 +65,7 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {
* The NetworkVirtualPrinter actually does nothing for auditing. * The NetworkVirtualPrinter actually does nothing for auditing.
* @param command a AuditOutputCommand with the details and the callback * @param command a AuditOutputCommand with the details and the callback
*/ */
@Override
public void doAudit(AuditOutputCommand command) { public void doAudit(AuditOutputCommand command) {
logger.debug("entered method doAudit"); logger.debug("entered method doAudit");
resetState(); resetState();
@ -67,20 +77,19 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {
* This is where the magic happens. The signed encrypted ballot is transmitted over the wire * This is where the magic happens. The signed encrypted ballot is transmitted over the wire
* @param command a CastOutputCommand with the details and the callback * @param command a CastOutputCommand with the details and the callback
*/ */
@Override
public void doCastBallot(CastOutputCommand command) { public void doCastBallot(CastOutputCommand command) {
logger.debug("entered method doCastBallot"); logger.debug("entered method doCastBallot");
ScannedData scannedData = ScannedData.newBuilder() PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder()
.setChannel(channelIdentifier) .setChannel(channelIdentifier)
.setSignedEncryptedBallot(this.signedEncryptedBallot) .setSignedEncryptedBallot(this.signedEncryptedBallot)
.build(); .build();
Response response = successfulPrintTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF)); boolean b = scannerClient.newScan(scannedBallot);
BoolValue b = response.readEntity(BoolValue.class);
response.close();
resetState(); resetState();
if (b.getValue()) { if (b) {
command.getCallback().onSuccess(null); command.getCallback().onSuccess(null);
} }
else { else {
@ -93,6 +102,7 @@ public class NetworkVirtualPrinter extends AsyncRunnableOutputDevice {
* The NetworkVirtualPrinter actually does nothing for canceling. * The NetworkVirtualPrinter actually does nothing for canceling.
* @param command a CancelOutputCommand with the callback * @param command a CancelOutputCommand with the callback
*/ */
@Override
public void doCancel(CancelOutputCommand command) { public void doCancel(CancelOutputCommand command) {
logger.debug("entered method doCancel"); logger.debug("entered method doCancel");
resetState(); resetState();

View File

@ -2,26 +2,35 @@ package meerkat.voting;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.PollingStation.*;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import meerkat.crypto.DigitalSignatureGenerator;
import meerkat.crypto.concrete.ECDSADeterministicSignature;
import meerkat.pollingstation.PollingStationScanner; import meerkat.pollingstation.PollingStationScanner;
import meerkat.pollingstation.PollingStationWebScanner; import meerkat.pollingstation.PollingStationWebScanner;
import meerkat.protobuf.Crypto.RerandomizableEncryptedMessage;
import meerkat.protobuf.Voting.*; import meerkat.protobuf.Crypto.Signature;
import meerkat.voting.controller.callbacks.OutputDeviceCommitCallback; import meerkat.protobuf.Crypto.SignatureType;
import meerkat.voting.controller.callbacks.OutputDeviceFinalizeCallback; import meerkat.protobuf.PollingStation;
import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.protobuf.Voting.EncryptedBallot;
import meerkat.protobuf.Voting.PlaintextBallot;
import meerkat.protobuf.Voting.SignedEncryptedBallot;
import meerkat.voting.controller.callbacks.ControllerCallback;
import meerkat.voting.output.NetworkVirtualPrinter; import meerkat.voting.output.NetworkVirtualPrinter;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Semaphore; import java.math.BigInteger;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
import static org.hamcrest.MatcherAssert.assertThat;
/** /**
* A test for the NetworkVirtualPrinter * A test for the NetworkVirtualPrinter
@ -31,127 +40,104 @@ import static org.hamcrest.MatcherAssert.assertThat;
public class NetworkVirtualPrinterTest { public class NetworkVirtualPrinterTest {
final Logger logger = LoggerFactory.getLogger(getClass());
private static final String SCAN_CONTEXT_PATH = "/scan";
private PollingStationScanner.PollingStationServer scanner; /**
private static final String ADDRESS = "http://localhost"; * Waiting time for transmission in ms.
private static final String SUB_ADDRESS = ""; */
private static final int PORT = 8080; final static long TIMEOUT = 500;
private PollingStationScanner.PollingStationServer scannerServer;
private Semaphore semaphore0;
private Semaphore semaphore1;
private Semaphore semaphore2;
private Throwable thrown;
private boolean dataIsAsExpected;
private NetworkVirtualPrinter networkPrinter; private NetworkVirtualPrinter networkPrinter;
private class ScanHandler implements FutureCallback<ScannedData> { PollingStation.ConnectionServerData serverData = null;
DigitalSignatureGenerator clientSigner;
private final ScannedData expectedData;
public ScanHandler(ScannedData expectedData) { BlockingQueue<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1);
this.expectedData = expectedData;
class CommandCallbackHandler extends ControllerCallback<Void> {
boolean ok;
CountDownLatch isFlagSet;
public CommandCallbackHandler(int requestId, long serial) {
super(requestId, serial,null);
this.ok = false;
isFlagSet = new CountDownLatch(1);
} }
private void done(boolean isOk) {
ok = isOk;
isFlagSet.countDown();
}
public boolean isOk(long timeout) throws InterruptedException {
isFlagSet.await(timeout, TimeUnit.MILLISECONDS);
return ok;
}
@Override
public void onSuccess(Void result) { done(true); }
@Override
public void onFailure(Throwable t) {
logger.error("Commit to ballot failed " + t.getMessage());
done(false);
}
}
private class ScanHandler implements FutureCallback<ScannedData> {
@Override @Override
public void onSuccess(ScannedData result) { public void onSuccess(ScannedData result) {
dataIsAsExpected = result.equals(expectedData); results.add(result);
semaphore2.release();
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) { }
dataIsAsExpected = false;
thrown = t;
semaphore2.release();
}
} }
private class CommitHandler extends OutputDeviceCommitCallback { void checkScanResult(PollingStation.ScannedBallot ballot, long timeout) throws Exception {
ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS);
private boolean success; assertEquals("Expecting ballot in received scan", ScannedData.DataCase.BALLOT, arrivedData.getDataCase());
public String errorMessage; assertEquals("Received scan does not match transmission", ballot, arrivedData.getBallot());
public CommitHandler(int requestId, long serialNumber) {
super(requestId, serialNumber, null, null);
errorMessage = null;
success = false;
}
@Override
public void onSuccess(Void v) {
System.out.println("CommitHandler success");
success = true;
semaphore0.release();
}
@Override
public void onFailure(Throwable t) {
errorMessage = "Commit to ballot failed " + t.getMessage();
semaphore0.release();
}
public boolean gotSuccess() {
return success;
}
} }
private class CastHandler extends OutputDeviceFinalizeCallback { void checkSCanResult(PollingStation.ScanError error, long timeout) throws Exception {
ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS);
private boolean success; assertEquals("Expecting error in received scan", ScannedData.DataCase.ERROR, arrivedData.getDataCase());
public String errorMessage; assertEquals("Received scan does not match transmission", error, arrivedData.getError());
public CastHandler(int requestId, long serialNumber) {
super(requestId, serialNumber, null, null);
errorMessage = null;
success = false;
}
@Override
public void onSuccess(Void v) {
System.out.println("CastHandler success");
success = true;
semaphore1.release();
}
@Override
public void onFailure(Throwable t) {
errorMessage = "Cast to ballot failed " + t.getMessage();
semaphore1.release();
}
public boolean gotSuccess() {
return success;
}
} }
@Before @Before
public void init() { public void init() {
System.err.println("Setting up Scanner WebApp!"); clientSigner = new ECDSADeterministicSignature();
clientSigner.generateSigningCertificate(BigInteger.ONE, new Date(System.currentTimeMillis()),
new Date(System.currentTimeMillis() + 1000*60*60*24*365), "testing");
scanner = new PollingStationWebScanner(PORT, SUB_ADDRESS); logger.debug("Setting up Scanner WebApp!");
semaphore0 = new Semaphore(0); scannerServer = new PollingStationWebScanner(0, SCAN_CONTEXT_PATH);
semaphore1 = new Semaphore(0);
semaphore2 = new Semaphore(0); scannerServer.subscribe(new ScanHandler());
thrown = null;
try { try {
scanner.start(); serverData = scannerServer.start(true);
logger.info("Scanner server started at {}", serverData.getServerUrl());
} catch (Exception e) { } catch (Exception e) {
assertThat("Could not start server: " + e.getMessage(), false); fail("Could not start server: " + e.getMessage());
} }
networkPrinter = new NetworkVirtualPrinter(serverData, clientSigner);
networkPrinter = new NetworkVirtualPrinter(ADDRESS + ":" + PORT);
Thread outputThread = new Thread(networkPrinter); Thread outputThread = new Thread(networkPrinter);
outputThread.setName("Meerkat VB-Output Thread"); outputThread.setName("Meerkat VB-Output Thread");
outputThread.start(); outputThread.start();
} }
@Test @Test
public void testSuccessfulScan() throws InterruptedException { public void testSuccessfulScan() throws Exception {
// create scannedData // create scannedData
@ -186,47 +172,37 @@ public class NetworkVirtualPrinterTest {
.setSignature(signature) .setSignature(signature)
.build(); .build();
ScannedData scannedData = ScannedData.newBuilder() PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder()
.setChannel(ByteString.copyFrom(channel)) .setChannel(ByteString.copyFrom(channel))
.setSignedEncryptedBallot(signedEncryptedBallot) .setSignedEncryptedBallot(signedEncryptedBallot)
.build(); .build();
scanner.subscribe(new ScanHandler(scannedData));
//Send scan //Send scan
CommitHandler commitHandler = new CommitHandler(0, serialNumber); CommandCallbackHandler commitHandler = new CommandCallbackHandler(0, 1);
networkPrinter.commitToBallot(plaintextBallot, signedEncryptedBallot, commitHandler); networkPrinter.commitToBallot(plaintextBallot, signedEncryptedBallot, commitHandler);
semaphore0.acquire(); CommandCallbackHandler castHandler = new CommandCallbackHandler(1, 1);
CastHandler castHandler = new CastHandler(1, serialNumber);
networkPrinter.castBallot(castHandler); networkPrinter.castBallot(castHandler);
semaphore1.acquire();
semaphore2.acquire();
// Make sure the response was valid // Make sure the response was valid
assertTrue("Commit to ballot callback did not receive success", commitHandler.isOk(TIMEOUT));
assertTrue("Cast ballot callback did not receive success", castHandler.isOk(TIMEOUT));
assertThat("Commit to ballot callback did not receive success", commitHandler.gotSuccess()); checkScanResult(scannedBallot, TIMEOUT);
assertThat("Cast ballot callback did not receive success", castHandler.gotSuccess());
assertThat("Scanner has thrown an error", thrown == null);
assertThat("Scanned data received was incorrect", dataIsAsExpected);
} }
@After @After
public void close() { public void close() {
System.err.println("Scanner WebApp shutting down..."); logger.debug("Scanner WebApp shutting down...");
try { try {
scanner.stop(); scannerServer.stop();
} catch (Exception e) { } catch (Exception e) {
assertThat("Could not stop server: " + e.getMessage(), false); fail("Could not stop scan server: " + e.getMessage());
} }
networkPrinter.callShutDown(); networkPrinter.callShutDown();