Much testing
This commit is contained in:
parent
30aeef5da5
commit
fa3b665047
7 changed files with 335 additions and 27 deletions
|
@ -3,7 +3,7 @@ package de.vanitasvitae.imi.codes;
|
||||||
import org.apache.commons.cli.Option;
|
import org.apache.commons.cli.Option;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
|
|
||||||
public class Arguments {
|
class Arguments {
|
||||||
|
|
||||||
static final Option STUDY_NUMBER = Option.builder("s")
|
static final Option STUDY_NUMBER = Option.builder("s")
|
||||||
.longOpt("study")
|
.longOpt("study")
|
||||||
|
@ -16,20 +16,28 @@ public class Arguments {
|
||||||
static final Option SAMPLE_TYPE = Option.builder("t")
|
static final Option SAMPLE_TYPE = Option.builder("t")
|
||||||
.longOpt("type")
|
.longOpt("type")
|
||||||
.desc("Type of the sample (b = blood, u = urine)")
|
.desc("Type of the sample (b = blood, u = urine)")
|
||||||
.required(true)
|
.required()
|
||||||
.hasArg()
|
.hasArg()
|
||||||
.argName("sample type")
|
.argName("sample type")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
static final Option NUMBER_CODES = Option.builder("n")
|
||||||
|
.longOpt("number")
|
||||||
|
.desc("Number of codes to be generated")
|
||||||
|
.required()
|
||||||
|
.hasArg()
|
||||||
|
.argName("n")
|
||||||
|
.build();
|
||||||
|
|
||||||
static final Option OUTPUT_DESTINATION = Option.builder("o")
|
static final Option OUTPUT_DESTINATION = Option.builder("o")
|
||||||
.longOpt("OUTPUT_DESTINATION")
|
.longOpt("output")
|
||||||
.desc("Filename for the generated html output")
|
.desc("Filename for the generated html output")
|
||||||
.hasArg()
|
.hasArg()
|
||||||
.argName("file name")
|
.argName("file name")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
static final Option EXTERNAL_BROWSER = Option.builder("b")
|
static final Option EXTERNAL_BROWSER = Option.builder("b")
|
||||||
.longOpt("EXTERNAL_BROWSER")
|
.longOpt("browser")
|
||||||
.desc("Open the generated HTML output in a an external browser")
|
.desc("Open the generated HTML output in a an external browser")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -43,14 +51,12 @@ public class Arguments {
|
||||||
* @return command line options.
|
* @return command line options.
|
||||||
*/
|
*/
|
||||||
public static Options getCommandLineOptions() {
|
public static Options getCommandLineOptions() {
|
||||||
Options options = new Options();
|
return new Options()
|
||||||
|
.addOption(STUDY_NUMBER)
|
||||||
options.addOption(STUDY_NUMBER);
|
.addOption(SAMPLE_TYPE)
|
||||||
options.addOption(SAMPLE_TYPE);
|
.addOption(NUMBER_CODES)
|
||||||
options.addOption(OUTPUT_DESTINATION);
|
.addOption(OUTPUT_DESTINATION)
|
||||||
options.addOption(EXTERNAL_BROWSER);
|
.addOption(EXTERNAL_BROWSER)
|
||||||
options.addOption(HELP);
|
.addOption(HELP);
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import java.util.regex.Pattern;
|
||||||
public class InputValidator {
|
public class InputValidator {
|
||||||
|
|
||||||
// Allowed characters
|
// Allowed characters
|
||||||
private static final String ALPHABET = "[a-zA-Z0-9]";
|
public static final String ALPHABET = "[a-zA-Z0-9]";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
REGEX pattern for study numbers. Allowed are all 3-letter numbers from the alphabet [a-zA-Z0-9].
|
REGEX pattern for study numbers. Allowed are all 3-letter numbers from the alphabet [a-zA-Z0-9].
|
||||||
|
@ -22,11 +22,12 @@ public class InputValidator {
|
||||||
*
|
*
|
||||||
* @param in input String
|
* @param in input String
|
||||||
* @return unmodified input String if it matches.
|
* @return unmodified input String if it matches.
|
||||||
* @throws IllegalArgumentException if the input String doesn't match the regex.
|
*
|
||||||
|
* @throws InvalidOptionException if the input String doesn't match the regex.
|
||||||
*/
|
*/
|
||||||
public static String validateStudyNumber(String in) {
|
public static String validateStudyNumber(String in) throws InvalidOptionException {
|
||||||
if (!STUDY_NUMBER_MATCHER.matcher(in).matches()) {
|
if (!STUDY_NUMBER_MATCHER.matcher(in).matches()) {
|
||||||
throw new IllegalArgumentException("Study number does not match REGEX.");
|
throw new InvalidOptionException("Study number must be a three digit number from the alphabet [a-zA-Z0-9].");
|
||||||
}
|
}
|
||||||
|
|
||||||
return in;
|
return in;
|
||||||
|
@ -38,10 +39,38 @@ public class InputValidator {
|
||||||
* @param in input String
|
* @param in input String
|
||||||
* @return parsed {@link SampleType}.
|
* @return parsed {@link SampleType}.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException if the given String doesn't match a {@link SampleType}.
|
* @throws InvalidOptionException if the given String doesn't match a {@link SampleType}.
|
||||||
* @throws NullPointerException in case the given String is {@code null}.
|
* @throws NullPointerException in case the given String is {@code null}.
|
||||||
*/
|
*/
|
||||||
public static SampleType validateSampleType(String in) {
|
public static SampleType validateSampleType(String in) throws InvalidOptionException {
|
||||||
return SampleType.valueOf(in);
|
try {
|
||||||
|
return SampleType.getEnum(in);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
String message = "Invalid sample type \"" + in + "\".";
|
||||||
|
throw new InvalidOptionException(message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate, that the user provided number of codes to be generated is an integer greater than 0.
|
||||||
|
*
|
||||||
|
* @param in input String
|
||||||
|
* @return number of codes to be generated.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException in case the input string is
|
||||||
|
*/
|
||||||
|
public static int validateNumberOfCodes(String in) throws InvalidOptionException {
|
||||||
|
int i;
|
||||||
|
try {
|
||||||
|
i = Integer.parseInt(in);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new InvalidOptionException("Invalid input \"" + in + "\". Please enter a positive number.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i <= 0) {
|
||||||
|
throw new InvalidOptionException("Number of generated codes must be greater than 0.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception which gets thrown when the user-provided input arguments are not valid.
|
||||||
|
*/
|
||||||
|
public class InvalidOptionException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link InvalidOptionException}. The message MUST be human readable, as it will get printed to the
|
||||||
|
* CLI.
|
||||||
|
* @param message human readable error message.
|
||||||
|
*/
|
||||||
|
public InvalidOptionException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link InvalidOptionException}. The message MUST be human readable, as it will get printed to the
|
||||||
|
* CLI.
|
||||||
|
* @param message human readable error message.
|
||||||
|
* @param cause cause of the exception.
|
||||||
|
*/
|
||||||
|
public InvalidOptionException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ public class Main {
|
||||||
private static final String HELP_FOOTER = "\nAuthor: Paul Schaub <paul.schaub@wwu.de>";
|
private static final String HELP_FOOTER = "\nAuthor: Paul Schaub <paul.schaub@wwu.de>";
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
Options options = Arguments.getCommandLineOptions();
|
Options options = Arguments.getCommandLineOptions();
|
||||||
CommandLineParser parser = new DefaultParser();
|
CommandLineParser parser = new DefaultParser();
|
||||||
|
|
||||||
|
@ -27,6 +26,7 @@ public class Main {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User issues '-h', so just show help text and exit.
|
||||||
if (arguments.hasOption(Arguments.HELP)) {
|
if (arguments.hasOption(Arguments.HELP)) {
|
||||||
printHelp(options);
|
printHelp(options);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -2,19 +2,61 @@ package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
public enum SampleType {
|
public enum SampleType {
|
||||||
|
|
||||||
b("Blood"),
|
// Hard coded values. Prepended with an underscore to allow numeric values (eg. _2).
|
||||||
u("Urine"),
|
_b('b', "Blood"),
|
||||||
|
_u('u', "Urine"),
|
||||||
;
|
;
|
||||||
|
|
||||||
// Members of the enum
|
// Members of the enum
|
||||||
|
|
||||||
private final String name;
|
private final char name;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
SampleType(String name) {
|
/**
|
||||||
|
* Create a SampleType enum which is described by a name (one letter from the alphabet [a-zA-Z0-9]) and a
|
||||||
|
* human readable description.
|
||||||
|
*
|
||||||
|
* @param name one letter name
|
||||||
|
* @param description description
|
||||||
|
*/
|
||||||
|
SampleType(char name, String description) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
/**
|
||||||
return name;
|
* Override {@link Enum#toString()} in order to return the name of the enum instead of its value.
|
||||||
|
*
|
||||||
|
* @return name
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the human readable description of the enum.
|
||||||
|
*/
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement method for {@link #valueOf(String)}. You MUST use this method instead, as we have to escape
|
||||||
|
* the names of the enums with a underscore in order to allow for numerals to be used as enum name (for example "_0").
|
||||||
|
* This method takes a string value (eg. "u") and returns the corresponding enum ("_u").
|
||||||
|
*
|
||||||
|
* @param name name of the enum without an underscore.
|
||||||
|
* @return enum corresponding to the name.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if no matching enum was found.
|
||||||
|
*/
|
||||||
|
public static SampleType getEnum(String name) {
|
||||||
|
for (SampleType s : SampleType.values()) {
|
||||||
|
if (s.toString().equals(name)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("No SampleType found for name \"" + name + "\".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
164
src/test/java/de/vanitasvitae/imi/codes/InputValidatorTest.java
Normal file
164
src/test/java/de/vanitasvitae/imi/codes/InputValidatorTest.java
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class InputValidatorTest {
|
||||||
|
|
||||||
|
private static final Random RAND = new Random();
|
||||||
|
|
||||||
|
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateStudyNumber(String)} accepts 3 digit inputs from the allowed alphabet.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException NOT expected.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testValidateStudyNumber() throws InvalidOptionException {
|
||||||
|
String in = generateTestStringOfLength(3);
|
||||||
|
assertEquals("validateStudyNumber must accept valid three digit inputs from the alphabet [a-zA-Z0-9].",
|
||||||
|
in, InputValidator.validateStudyNumber(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateStudyNumber(String)} fails for inputs of correct length, but from
|
||||||
|
* illegal characters.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateStudyNumberFailsForInvalidAlphabet() throws InvalidOptionException {
|
||||||
|
String in = "7*A";
|
||||||
|
|
||||||
|
// -> InvalidOptionException
|
||||||
|
InputValidator.validateStudyNumber(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateStudyNumber(String)} fails for Strings of length 0, 1 or two from
|
||||||
|
* the allowed alphabet.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected.
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateStudyNumberFailsForInputOfInsufficientLength() throws InvalidOptionException {
|
||||||
|
String in = generateTestStringOfLength(RAND.nextInt(3));
|
||||||
|
|
||||||
|
// -> InvalidOptionException
|
||||||
|
InputValidator.validateStudyNumber(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateStudyNumber(String)} fails for input Strings which are too long.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected.
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateStudyNumberFailsForLongerInput() throws InvalidOptionException {
|
||||||
|
String in = generateTestStringOfLength(RAND.nextInt(50) + 4);
|
||||||
|
|
||||||
|
// -> InvalidOptionException
|
||||||
|
InputValidator.validateStudyNumber(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generates a String of length len which consists of letters from the alphabet [a-zA-Z0-9].
|
||||||
|
*/
|
||||||
|
private static String generateTestStringOfLength(int len) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
sb.append(ALPHABET.charAt(RAND.nextInt(ALPHABET.length())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateSampleType(String)} returns the expected {@link SampleType} for
|
||||||
|
* given input strings.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException NOT expected
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testValidateSampleType() throws InvalidOptionException {
|
||||||
|
for (SampleType t : SampleType.values()) {
|
||||||
|
assertEquals("validateSampleType didn't return expected value for input \"" + t.toString() + "\"",
|
||||||
|
t, InputValidator.validateSampleType(t.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateSampleType(String)} throws a {@link InvalidOptionException} for
|
||||||
|
* unknown sample types (in this case {@code x}).
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateSampleTypeFailsForUnknownInputs() throws InvalidOptionException {
|
||||||
|
// -> InvalidOptionException
|
||||||
|
InputValidator.validateSampleType("x");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} returns the expected {@link Integer}.
|
||||||
|
* In this method, posit
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException NOT expected
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testValidateNumberOfCodes() throws InvalidOptionException {
|
||||||
|
// nextInt(bounds) returns a number between 0 (inclusive) and bound (exclusive), so +1 should be safe.
|
||||||
|
int number = RAND.nextInt(Integer.MAX_VALUE) + 1;
|
||||||
|
String string = Integer.toString(number);
|
||||||
|
|
||||||
|
assertEquals("validateNumberOfCodes failed to validate input \"" + string + "\"",
|
||||||
|
number, InputValidator.validateNumberOfCodes(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} throws a {@link InvalidOptionException}
|
||||||
|
* for negative numbers and 0.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateNumberOfCodesFailsForNegativeNumbers() throws InvalidOptionException {
|
||||||
|
int number = -RAND.nextInt(Integer.MAX_VALUE);
|
||||||
|
String string = Integer.toString(number);
|
||||||
|
|
||||||
|
// -> InvalidOptionException
|
||||||
|
InputValidator.validateNumberOfCodes(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} throws a {@link InvalidOptionException}
|
||||||
|
* for non-numeric inputs, which cannot be parsed into an {@link Integer}.
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateNumberOfCodesFailsForNonNumericInput() throws InvalidOptionException {
|
||||||
|
String string = "notNumeric";
|
||||||
|
|
||||||
|
// -> InvalidOptionException
|
||||||
|
InputValidator.validateNumberOfCodes(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} throws a {@link InvalidOptionException}
|
||||||
|
* for inputs that exceed the range of an {@link Integer} ({@link Integer#MAX_VALUE}).
|
||||||
|
*
|
||||||
|
* @throws InvalidOptionException expected
|
||||||
|
*/
|
||||||
|
@Test(expected = InvalidOptionException.class)
|
||||||
|
public void testValidateNumberOfCodesFailsForNumbersTooLarge() throws InvalidOptionException {
|
||||||
|
// 10 * Integer.MAX_VALUE
|
||||||
|
String string = "21474836470";
|
||||||
|
|
||||||
|
InputValidator.validateNumberOfCodes(string);
|
||||||
|
}
|
||||||
|
}
|
39
src/test/java/de/vanitasvitae/imi/codes/SampleTypeTest.java
Normal file
39
src/test/java/de/vanitasvitae/imi/codes/SampleTypeTest.java
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test functionality of the {@link SampleType} enum.
|
||||||
|
*/
|
||||||
|
public class SampleTypeTest {
|
||||||
|
|
||||||
|
private static final Pattern SAMPLE_TYPE_VALUE_PATTERN = Pattern.compile(InputValidator.ALPHABET);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether all {@link SampleType} names are of length 1 and from the alphabet [a-zA-Z0-9].
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSampleTypeNames() {
|
||||||
|
for (SampleType t : SampleType.values()) {
|
||||||
|
assertTrue("SampleType value names must be one letter from the alphabet [a-zA-Z0-9].",
|
||||||
|
SAMPLE_TYPE_VALUE_PATTERN.matcher(t.toString()).matches());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test, whether {@link SampleType#getEnum(String)} correctly maps {@link SampleType#name} to the corresponding
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetEnum() {
|
||||||
|
for (SampleType t : SampleType.values()) {
|
||||||
|
assertEquals("getEnum must return the SampleType, which corresponds to the given name.",
|
||||||
|
t, SampleType.getEnum(t.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue