Add some tests
This commit is contained in:
parent
8c2acbed79
commit
a379e2ddbe
8 changed files with 168 additions and 15 deletions
|
@ -26,4 +26,4 @@ Any valid study code can be entered. Some studies are preemptively hard-coded (`
|
||||||
Unknown studies will be newly created.
|
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.
|
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.
|
||||||
|
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
||||||
|
|
||||||
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
|
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
|
||||||
import de.vanitasvitae.imi.codes.persistence.FileRepository;
|
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.SampleTubeCode;
|
||||||
import de.vanitasvitae.imi.codes.types.SampleType;
|
import de.vanitasvitae.imi.codes.types.SampleType;
|
||||||
import de.vanitasvitae.imi.codes.types.StudyNumber;
|
import de.vanitasvitae.imi.codes.types.StudyNumber;
|
||||||
|
@ -34,23 +35,27 @@ public class CodeGenerator {
|
||||||
* @throws InvalidOptionException if too many codes would be generated.
|
* @throws InvalidOptionException if too many codes would be generated.
|
||||||
* @throws IOException if IO goes wrong reading or writing sample code records.
|
* @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 {
|
throws InvalidOptionException, IOException {
|
||||||
|
|
||||||
|
if (numberOfCodes < 1) {
|
||||||
|
throw new InvalidOptionException("Number of codes cannot be less than 1.");
|
||||||
|
}
|
||||||
List<SampleTubeCode> codes = new ArrayList<>();
|
List<SampleTubeCode> codes = new ArrayList<>();
|
||||||
|
|
||||||
// Read the next sample code from file
|
// Read the next sample code from file
|
||||||
int nextSampleCode = repository.nextSampleCode(studyNumber);
|
int nextSampleCode = repository.getNextSampleCode(studyNumber);
|
||||||
|
|
||||||
int nextTotal = nextSampleCode + numberOfCodes;
|
int nextTotal = nextSampleCode + numberOfCodes;
|
||||||
// Check, if we'd have an overflow of sample numbers
|
// Check, if we'd have an overflow of sample numbers
|
||||||
// We check like this to prevent integer overflows
|
// We check like this to prevent integer overflows
|
||||||
if (nextSampleCode > 9999 || numberOfCodes > 9999 || nextTotal - 1 > 9999) {
|
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.");
|
" (" + (nextTotal - 1) + "). Aborting.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write back the number of the next sample number that should be generated next time
|
// 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
|
// Generate codes
|
||||||
for (int i = 0; i < numberOfCodes; i++) {
|
for (int i = 0; i < numberOfCodes; i++) {
|
||||||
|
|
|
@ -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
|
* If the user tries to generate codes for those studies, the generated codes start at the hard coded values instead
|
||||||
* of 0.
|
* of 0.
|
||||||
*/
|
*/
|
||||||
public class FileRepository {
|
public class FileRepository implements Repository {
|
||||||
|
|
||||||
private final File base;
|
private final File base;
|
||||||
private final HashMap<StudyNumber, Integer> hardCoded = new HashMap<>();
|
private final HashMap<StudyNumber, Integer> hardCoded = new HashMap<>();
|
||||||
|
@ -65,7 +65,8 @@ public class FileRepository {
|
||||||
* @param study code for the study
|
* @param study code for the study
|
||||||
* @return sample number
|
* @return sample number
|
||||||
*/
|
*/
|
||||||
public int nextSampleCode(StudyNumber study) {
|
@Override
|
||||||
|
public int getNextSampleCode(StudyNumber study) {
|
||||||
File studyNumberFile = new File(base, study.toString());
|
File studyNumberFile = new File(base, study.toString());
|
||||||
int next;
|
int next;
|
||||||
// Check, if we have hard-coded values available
|
// Check, if we have hard-coded values available
|
||||||
|
@ -88,7 +89,8 @@ public class FileRepository {
|
||||||
*
|
*
|
||||||
* @throws IOException in case the file cannot be written
|
* @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);
|
writeInt(new File(base, study.toString()), nextSampleCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import de.vanitasvitae.imi.codes.input.InputValidator;
|
||||||
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
|
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
|
||||||
import de.vanitasvitae.imi.codes.types.SampleType;
|
import de.vanitasvitae.imi.codes.types.SampleType;
|
||||||
import de.vanitasvitae.imi.codes.types.StudyNumber;
|
import de.vanitasvitae.imi.codes.types.StudyNumber;
|
||||||
|
import jdk.internal.util.xml.impl.Input;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class InputValidatorTest {
|
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}.
|
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} returns the expected {@link Integer}.
|
||||||
* In this method, posit
|
* In this method, posit
|
||||||
|
|
|
@ -7,7 +7,6 @@ import java.io.IOException;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
|
import de.vanitasvitae.imi.codes.input.InvalidOptionException;
|
||||||
import de.vanitasvitae.imi.codes.persistence.FileRepository;
|
|
||||||
import de.vanitasvitae.imi.codes.types.StudyNumber;
|
import de.vanitasvitae.imi.codes.types.StudyNumber;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
@ -36,22 +35,22 @@ public class FileRepositoryTest {
|
||||||
public void testReadWriteSampleNumbers() throws InvalidOptionException, IOException {
|
public void testReadWriteSampleNumbers() throws InvalidOptionException, IOException {
|
||||||
StudyNumber studyNumber = new StudyNumber("abc");
|
StudyNumber studyNumber = new StudyNumber("abc");
|
||||||
assertEquals("An unknown study number must return 0 as next sample number",
|
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
|
// The written next sample code must be returned next time
|
||||||
int next = 34;
|
int next = 34;
|
||||||
repository.writeNextSampleCode(studyNumber, next);
|
repository.setNextSampleCode(studyNumber, next);
|
||||||
assertEquals("The written next sample number must be read next time.",
|
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".
|
// The FileRepository contains 35 as hard coded sample number for study "AAA".
|
||||||
|
|
||||||
StudyNumber hardCoded = new StudyNumber("AAA");
|
StudyNumber hardCoded = new StudyNumber("AAA");
|
||||||
assertEquals("The hard coded value must be returned the first time its being read.",
|
assertEquals("The hard coded value must be returned the first time its being read.",
|
||||||
35, repository.nextSampleCode(hardCoded));
|
35, repository.getNextSampleCode(hardCoded));
|
||||||
repository.writeNextSampleCode(hardCoded, 89);
|
repository.setNextSampleCode(hardCoded, 89);
|
||||||
assertEquals("If the hard coded value is overwritten, the new value must be read.",
|
assertEquals("If the hard coded value is overwritten, the new value must be read.",
|
||||||
89, repository.nextSampleCode(hardCoded));
|
89, repository.getNextSampleCode(hardCoded));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue