Add some tests

This commit is contained in:
Paul Schaub 2018-11-18 20:25:22 +01:00
parent 8c2acbed79
commit a379e2ddbe
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
8 changed files with 168 additions and 15 deletions

View File

@ -26,4 +26,4 @@ Any valid study code can be entered. Some studies are preemptively hard-coded (`
Unknown studies will be newly created.
Records about existing studies are stored in the directory `./.imicodes/`. That directory contains text files which are named after studies.
Each study file contains the number of the last code which was generated, so new codes will have different numbers by incrementing the last code by one.
Each study file contains the number of the next code which will be generated, so new codes will have different numbers by incrementing the last code by one.

View File

@ -6,6 +6,7 @@ import java.util.List;
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
import de.vanitasvitae.imi.codes.persistence.FileRepository;
import de.vanitasvitae.imi.codes.persistence.Repository;
import de.vanitasvitae.imi.codes.types.SampleTubeCode;
import de.vanitasvitae.imi.codes.types.SampleType;
import de.vanitasvitae.imi.codes.types.StudyNumber;
@ -34,23 +35,27 @@ public class CodeGenerator {
* @throws InvalidOptionException if too many codes would be generated.
* @throws IOException if IO goes wrong reading or writing sample code records.
*/
public static List<SampleTubeCode> generateCodes(FileRepository repository, StudyNumber studyNumber, SampleType sampleType, int numberOfCodes)
public static List<SampleTubeCode> generateCodes(Repository repository, StudyNumber studyNumber, SampleType sampleType, int numberOfCodes)
throws InvalidOptionException, IOException {
if (numberOfCodes < 1) {
throw new InvalidOptionException("Number of codes cannot be less than 1.");
}
List<SampleTubeCode> codes = new ArrayList<>();
// Read the next sample code from file
int nextSampleCode = repository.nextSampleCode(studyNumber);
int nextSampleCode = repository.getNextSampleCode(studyNumber);
int nextTotal = nextSampleCode + numberOfCodes;
// Check, if we'd have an overflow of sample numbers
// We check like this to prevent integer overflows
if (nextSampleCode > 9999 || numberOfCodes > 9999 || nextTotal - 1 > 9999) {
throw new InvalidOptionException("Study " + studyNumber + "would have too many sample tubes" +
throw new InvalidOptionException("Study " + studyNumber + " would have too many sample tubes" +
" (" + (nextTotal - 1) + "). Aborting.");
}
// Write back the number of the next sample number that should be generated next time
repository.writeNextSampleCode(studyNumber, nextSampleCode + numberOfCodes);
repository.setNextSampleCode(studyNumber, nextSampleCode + numberOfCodes);
// Generate codes
for (int i = 0; i < numberOfCodes; i++) {

View File

@ -22,7 +22,7 @@ import de.vanitasvitae.imi.codes.types.StudyNumber;
* If the user tries to generate codes for those studies, the generated codes start at the hard coded values instead
* of 0.
*/
public class FileRepository {
public class FileRepository implements Repository {
private final File base;
private final HashMap<StudyNumber, Integer> hardCoded = new HashMap<>();
@ -65,7 +65,8 @@ public class FileRepository {
* @param study code for the study
* @return sample number
*/
public int nextSampleCode(StudyNumber study) {
@Override
public int getNextSampleCode(StudyNumber study) {
File studyNumberFile = new File(base, study.toString());
int next;
// Check, if we have hard-coded values available
@ -88,7 +89,8 @@ public class FileRepository {
*
* @throws IOException in case the file cannot be written
*/
public void writeNextSampleCode(StudyNumber study, int nextSampleCode) throws IOException {
@Override
public void setNextSampleCode(StudyNumber study, int nextSampleCode) throws IOException {
writeInt(new File(base, study.toString()), nextSampleCode);
}

View File

@ -0,0 +1,13 @@
package de.vanitasvitae.imi.codes.persistence;
import java.io.IOException;
import de.vanitasvitae.imi.codes.types.StudyNumber;
public interface Repository {
int getNextSampleCode(StudyNumber studyNumber);
void setNextSampleCode(StudyNumber studyNumber, int sampleCode) throws IOException;
}

View File

@ -0,0 +1,85 @@
package de.vanitasvitae.imi.codes;
import static junit.framework.TestCase.assertEquals;
import java.io.IOException;
import java.util.List;
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
import de.vanitasvitae.imi.codes.persistence.Repository;
import de.vanitasvitae.imi.codes.persistence.SimpleTestRepository;
import de.vanitasvitae.imi.codes.types.SampleTubeCode;
import de.vanitasvitae.imi.codes.types.SampleType;
import de.vanitasvitae.imi.codes.types.StudyNumber;
import org.junit.Before;
import org.junit.Test;
public class CodeGeneratorTest {
// Test Repository, which stores sample code records in an ephemeral HashMap, so no cleaning is needed :)
private static final Repository repository = new SimpleTestRepository();
@Before
public void populateRepository() throws InvalidOptionException, IOException {
// Put some dummy values into the repository.
repository.setNextSampleCode(new StudyNumber("AAA"), 42);
repository.setNextSampleCode(new StudyNumber("BBB"), 56);
repository.setNextSampleCode(new StudyNumber("ZZZ"), 9999);
}
/**
* Check, if generated codes match expected output.
*
* @throws InvalidOptionException NOT expected since the input should be valid.
* @throws IOException NOT expected, since we do no IO.
*/
@Test
public void testGenerateCodes() throws InvalidOptionException, IOException {
List<SampleTubeCode> codes = CodeGenerator.generateCodes(repository, new StudyNumber("AAA"), new SampleType("b"), 34);
assertEquals("The size of the list of generated codes must match.", 34, codes.size());
assertEquals("The first generated code must match the expected format.", "AAAb0042", codes.get(0).toString());
for (int i = 1; i < codes.size(); i++) {
assertEquals("All codes in the list must match the expected codes.",
String.format("AAAb%04d", (42 + i)), codes.get(i).toString());
}
}
/**
* Test whether the method throws an exception, when there would be too many codes generated for a study.
* This can happen, if a study has n codes already generated, and the user tries to generate m additional codes, so
* that n+m > 9999. This must not happen, since we only have 4 decimal letters to encode the sample number.
*
* @throws InvalidOptionException expected.
* @throws IOException NOT expected, since we do not IO.
*/
@Test (expected = InvalidOptionException.class)
public void testGenerateCodesFailsWhenOverflowing() throws InvalidOptionException, IOException {
CodeGenerator.generateCodes(repository, new StudyNumber("ZZZ"), new SampleType("b"), 2);
}
/**
* Test whether the method fails for too big numbers of codes to generate.
* This is done to prevent overflows similar as in {@link #testGenerateCodesFailsWhenOverflowing()}.
*
* @throws InvalidOptionException expected.
* @throws IOException NOT expected, since we do no IO.
*/
@Test (expected = InvalidOptionException.class)
public void testGenerateCodesFailsForTooHighNumberOfCodes() throws InvalidOptionException, IOException {
CodeGenerator.generateCodes(repository, new StudyNumber("abc"), new SampleType("x"), 10001);
}
/**
* Test whether the method throws an exception for too low number of codes to be generated.
* The number of codes to be generated must be at least 1.
*
* @throws InvalidOptionException expected.
* @throws IOException NOT expected (no IO).
*/
@Test (expected = InvalidOptionException.class)
public void testGenerateCodesFailsForTooLowNumberOfCodes() throws InvalidOptionException, IOException {
CodeGenerator.generateCodes(repository, new StudyNumber("abc"), new SampleType("x"), -4);
}
}

View File

@ -10,6 +10,7 @@ import de.vanitasvitae.imi.codes.input.InputValidator;
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
import de.vanitasvitae.imi.codes.types.SampleType;
import de.vanitasvitae.imi.codes.types.StudyNumber;
import jdk.internal.util.xml.impl.Input;
import org.junit.Test;
public class InputValidatorTest {
@ -99,6 +100,27 @@ public class InputValidatorTest {
}
}
/**
* {@link SampleType SampleTypes} must be of length 1. This test checks, whether
* {@link InputValidator#validateSampleType(String)} fails for inputs of length 2.
*
* @throws InvalidOptionException expected.
*/
@Test (expected = InvalidOptionException.class)
public void testValidateSampleTypeFailsForTooLongSampleTypes() throws InvalidOptionException {
InputValidator.validateSampleType("aa");
}
/**
* Test whether {@link InputValidator#validateSampleType(String)} fails for inputs from the wrong alphabet.
*
* @throws InvalidOptionException expected.
*/
@Test (expected = InvalidOptionException.class)
public void testValidateSampleTypeFailsForInvalidSampleTypes() throws InvalidOptionException {
InputValidator.validateSampleType("$");
}
/**
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} returns the expected {@link Integer}.
* In this method, posit

View File

@ -7,7 +7,6 @@ import java.io.IOException;
import java.util.Stack;
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
import de.vanitasvitae.imi.codes.persistence.FileRepository;
import de.vanitasvitae.imi.codes.types.StudyNumber;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@ -36,22 +35,22 @@ public class FileRepositoryTest {
public void testReadWriteSampleNumbers() throws InvalidOptionException, IOException {
StudyNumber studyNumber = new StudyNumber("abc");
assertEquals("An unknown study number must return 0 as next sample number",
0, repository.nextSampleCode(studyNumber));
0, repository.getNextSampleCode(studyNumber));
// The written next sample code must be returned next time
int next = 34;
repository.writeNextSampleCode(studyNumber, next);
repository.setNextSampleCode(studyNumber, next);
assertEquals("The written next sample number must be read next time.",
next, repository.nextSampleCode(studyNumber));
next, repository.getNextSampleCode(studyNumber));
// The FileRepository contains 35 as hard coded sample number for study "AAA".
StudyNumber hardCoded = new StudyNumber("AAA");
assertEquals("The hard coded value must be returned the first time its being read.",
35, repository.nextSampleCode(hardCoded));
repository.writeNextSampleCode(hardCoded, 89);
35, repository.getNextSampleCode(hardCoded));
repository.setNextSampleCode(hardCoded, 89);
assertEquals("If the hard coded value is overwritten, the new value must be read.",
89, repository.nextSampleCode(hardCoded));
89, repository.getNextSampleCode(hardCoded));
}
@AfterClass

View File

@ -0,0 +1,27 @@
package de.vanitasvitae.imi.codes.persistence;
import java.util.HashMap;
import java.util.Map;
import de.vanitasvitae.imi.codes.types.StudyNumber;
/**
* Simple implementation of the {@link Repository} interface.
* Use this only for testing purposes, as no values are persisted.
*/
public class SimpleTestRepository implements Repository {
private final Map<StudyNumber, Integer> sampleCodes = new HashMap<>();
@Override
public int getNextSampleCode(StudyNumber studyNumber) {
Integer sampleCode = sampleCodes.get(studyNumber);
return sampleCode != null ? sampleCode : 0;
}
@Override
public void setNextSampleCode(StudyNumber studyNumber, int sampleCode) {
sampleCodes.put(studyNumber, sampleCode);
}
}