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.
|
||||
|
||||
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.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++) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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