diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..ea90b630 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +calc \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..82e319e2 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000..63e90019 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..712ab9d9 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml new file mode 100644 index 00000000..f854ab00 --- /dev/null +++ b/.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_6_2.xml new file mode 100644 index 00000000..71711536 --- /dev/null +++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_2.xml new file mode 100644 index 00000000..ef160d24 --- /dev/null +++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_2.xml new file mode 100644 index 00000000..fc4c644e --- /dev/null +++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_6_2.xml b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_6_2.xml new file mode 100644 index 00000000..45b4af37 --- /dev/null +++ b/.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_2.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_2.xml new file mode 100644 index 00000000..f2699b95 --- /dev/null +++ b/.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_2.xml b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_2.xml new file mode 100644 index 00000000..c13a302c --- /dev/null +++ b/.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_6_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml new file mode 100644 index 00000000..fbc1b163 --- /dev/null +++ b/.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..972ec8d7 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..0fa07049 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/calc.iml b/calc.iml new file mode 100644 index 00000000..8d9aaaf7 --- /dev/null +++ b/calc.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/Finance.java b/src/main/java/Finance.java new file mode 100644 index 00000000..b43d775b --- /dev/null +++ b/src/main/java/Finance.java @@ -0,0 +1,64 @@ + +import com.h2.BestLoanRates; +import com.h2.MortgageCalculator; +import com.h2.SavingsCalculator; + +import java.util.Arrays; +import java.util.Map; + +public class Finance { + + public static final String BEST_LOAN_RATES = "bestLoanRates"; + public static final String SAVINGS_CALCULATOR = "savingsCalculator"; + public static final String MORTGAGE_CALCULATOR = "mortgageCalculator"; + + public static final Map commandsToUsage = Map.of( + BEST_LOAN_RATES, "usage: bestLoanRates", + SAVINGS_CALCULATOR, "usage: savingsCalculator ", + MORTGAGE_CALCULATOR, "usage: mortgageCalculator " + ); + + public static void main(String[] args) { + final String command = args[0]; + if (!commandsToUsage.containsKey(command)) { + System.out.println(command + ": command not found"); + System.exit(-1); + } + + final boolean isValidCommand = validateCommandArguments(args); + if (!isValidCommand) { + System.out.println(commandsToUsage.get(args[0])); + System.exit(-1); + } + + executeCommand(command, Arrays.copyOfRange(args, 1, args.length)); + } + + private static void executeCommand(String command, String[] arguments) { + switch (command) { + case BEST_LOAN_RATES: + System.out.println("Finding best loan rates ..."); // for testing just log + BestLoanRates.main(arguments); // for another test call these methods and have executeCommand add to main method + return; + case SAVINGS_CALCULATOR: + System.out.println("Finding your net savings ..."); + SavingsCalculator.main(arguments); + return; + case MORTGAGE_CALCULATOR: + System.out.println("Finding your monthly payment ..."); + MortgageCalculator.main(arguments); + } + } + + private static boolean validateCommandArguments(String[] args) { + switch (args[0]) { + case BEST_LOAN_RATES: + return args.length == 1; + case SAVINGS_CALCULATOR: + return args.length == 3; + case MORTGAGE_CALCULATOR: + return args.length == 4; + } + return false; + } +} diff --git a/src/main/java/com/h2/App.java b/src/main/java/com/h2/App.java index 6966d92c..15955ee2 100644 --- a/src/main/java/com/h2/App.java +++ b/src/main/java/com/h2/App.java @@ -10,4 +10,19 @@ public static void main( String[] args ) { System.out.println( "Hello World!" ); } + + public static int doubleTheNumber(int number) { + // do it in module02 + // todo: fix the implementation + // return -1; + return 2 * number; + } + + private static int add(int[] numbers) { + var sum = 0; + for (int number : numbers) { + sum += number; + } + return sum; + } } diff --git a/src/main/java/com/h2/BestLoanRates.java b/src/main/java/com/h2/BestLoanRates.java new file mode 100644 index 00000000..7678c120 --- /dev/null +++ b/src/main/java/com/h2/BestLoanRates.java @@ -0,0 +1,39 @@ +package com.h2; + +import java.util.Map; +import java.util.Scanner; + +public class BestLoanRates { + public final static Map bestRates = Map.of( + 1, 5.50f, + 2, 3.45f, + 3, 2.67f + ); + + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + + System.out.println("Enter your name"); + String name = scanner.nextLine(); + System.out.println("Hello " + name); + + System.out.println("Enter the loan term (in years)"); + int loanTermInYears = scanner.nextInt(); + float bestRate = getRates(loanTermInYears); + if (bestRate == 0.0f) { + System.out.println("No available rates for term: " + loanTermInYears + " years"); + } else { + System.out.println("Best Available Rate: " + getRates(loanTermInYears) + "%"); + } + + scanner.close(); + } + + public static float getRates(int loanTermInYears) { + if (bestRates.containsKey(loanTermInYears)) { + return bestRates.get(loanTermInYears); + } + return 0.0f; + } +} diff --git a/src/main/java/com/h2/MortgageCalculator.java b/src/main/java/com/h2/MortgageCalculator.java new file mode 100644 index 00000000..15c42884 --- /dev/null +++ b/src/main/java/com/h2/MortgageCalculator.java @@ -0,0 +1,53 @@ +package com.h2; + +import java.text.DecimalFormat; + +public class MortgageCalculator { + private final long loanAmount; + private final int termInYears; + private final float annualRate; + + public MortgageCalculator(long loanAmount, int termInYears, float annualRate) { + this.loanAmount = loanAmount; + this.termInYears = termInYears; + this.annualRate = annualRate; + } + + private int getNumberOfPayments() { + return this.termInYears * 12; + } + + private float getMonthlyInterestRate() { + float interestRate = annualRate / 100; + return interestRate / 12; + } + + public double getMonthlyPayment() { + long P = loanAmount; + float r = getMonthlyInterestRate(); + int n = getNumberOfPayments(); + + System.out.println("P=" + P); + System.out.println("r=" + r); + System.out.println("n=" + n); + double M = P * (((r * Math.pow(1 + r, n))) / ((Math.pow((1 + r), n)) - 1)); + + return M; + } + + public static void main(String[] args) { + if (args.length < 3) { + System.out.println("usage: mortgageCalculator "); + System.exit(-1); + } + + final long loanAmount = Utilities.getLongValue(args[0]); + final int termInYears = Utilities.getIntValue(args[1]); + final float annualRate = Utilities.getFloatValue(args[2]); + final MortgageCalculator c = new MortgageCalculator(loanAmount, termInYears, annualRate); + final double monthlyPayment = c.getMonthlyPayment(); + DecimalFormat df = new DecimalFormat("####0.00"); + + System.out.println("Monthly Payment: " + df.format(monthlyPayment)); + } +} diff --git a/src/main/java/com/h2/SavingsCalculator.java b/src/main/java/com/h2/SavingsCalculator.java new file mode 100644 index 00000000..ac9cc704 --- /dev/null +++ b/src/main/java/com/h2/SavingsCalculator.java @@ -0,0 +1,56 @@ +package com.h2; + +public class SavingsCalculator { + private final float[] credits; + private final float[] debits; + + public SavingsCalculator(float[] credits, float[] debits) { + this.credits = credits; + this.debits = debits; + } + + public float calculate() { + return sumOfCredits() - sumOfDebits(); + } + + private float sumOfCredits() { + float sum = 0.0f; + for (float credit : credits) { + sum += credit; + } + return sum; + } + + private float sumOfDebits() { + float sum = 0.0f; + for (float debit : debits) { + sum += debit; + } + return sum; + } + + public static void main(String[] args) { + if (args.length < 2) { + System.out.println("usage: savingsCalculator "); + System.exit(-1); + } + + final String[] creditsAsString = args[0].split(","); + final String[] debitsAsString = args[1].split(","); + + final float[] credits = new float[creditsAsString.length]; + final float[] debits = new float[debitsAsString.length]; + + for (int i = 0; i < creditsAsString.length; i++) { + credits[i] = Utilities.getFloatValue(creditsAsString[i]); + } + + for (int i = 0; i < debitsAsString.length; i++) { + debits[i] = Utilities.getFloatValue(debitsAsString[i]); + } + + final SavingsCalculator calculator = new SavingsCalculator(credits, debits); + + System.out.println("Net Savings = " + calculator.calculate()); + } +} diff --git a/src/main/java/com/h2/Utilities.java b/src/main/java/com/h2/Utilities.java new file mode 100644 index 00000000..ebfe5676 --- /dev/null +++ b/src/main/java/com/h2/Utilities.java @@ -0,0 +1,36 @@ +package com.h2; + +public class Utilities { + // float, long, int + + public static float getFloatValue(String in) { + float out = 0.0f; + try { + out = Float.parseFloat(in); + } catch (NumberFormatException e) { + System.out.println(in + " cannot be converted into a 'float' value. Exiting program."); + } + return out; + } + + public static long getLongValue(String in) { + long out = 0; + try { + out = Long.parseLong(in); + } catch (NumberFormatException e) { + System.out.println(in + " cannot be converted into a 'long' value. Exiting program."); + } + return out; + + } + + public static int getIntValue(String in) { + int out = 0; + try { + out = Integer.parseInt(in); + } catch (NumberFormatException e) { + System.out.println(in + " cannot be converted into a 'float' value. Exiting program."); + } + return out; + } +} diff --git a/src/test/java/FinanceTest.java b/src/test/java/FinanceTest.java new file mode 100644 index 00000000..b3799401 --- /dev/null +++ b/src/test/java/FinanceTest.java @@ -0,0 +1,112 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.function.Try; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass; + +public class FinanceTest { + + private final String classToFind = "Finance"; + + public Optional> getAppClass() { + Try> aClass = tryToLoadClass(classToFind); + return aClass.toOptional(); + } + + @Test + public void assertClassExistence() { + final Optional> maybeClass = getAppClass(); + assertTrue(maybeClass.isPresent(), classToFind + " should be present"); + assertEquals(classToFind, maybeClass.get().getCanonicalName()); + } + + @Disabled + @Test + public void testCommandsToUsage() { + /* + * 1. Existence of field + * 2. isPublic, isFinal, isStatic, isMap (type) + * 3. Has 3 entries + * 4. Test all entries + */ + } + + @Disabled + @Test + public void testMainCommandInput() { + /* + * 1. Pass command from the commandsToUsage + * 2. Test invalid command + */ + } + + @Disabled + @Test + public void testValidateCommandArgumentsExistence() { + /* + * 1. Method exists + * 2. isStatic, isPrivate, returns boolean + * 3. accepts String[] parameter + */ + } + + @Disabled + @Test + public void testValidateCommandArgumentsCorrectness() { + /* + * 1. Send all 3 valid argument[0] with different rest of argument length + * 2. Send invalid args[0] and assert that it should return false; + */ + } + + @Disabled + @Test + public void testMainWithValidCommandInvalidUsage() { + /* + * 1. test all valid command names with invalid arguments lengths + */ + } + + @Disabled + @Test + public void testCommandConstantFields() { + /* + * 1. Existence of BEST_LOAN_RATES, SAVINGS_CALCULATOR, MORTGAGE_CALCULATOR + * 2. isPublic, isStatic, isFinal + * 3. Right values for each field + */ + } + + @Disabled + @Test + public void testExecuteCommandExistence() { + /* + * 1. Method exists + * 2. isPrivate isStatic, returns nothing + * 3. Has parameters + */ + } + + @Disabled + @Test + public void testExecuteCommandExistenceForCorrectness() { + /* + * 1. Test the validity for correct statement printed on console based on command + */ + } + + @Disabled + @Test + public void testMainWithValidCommandUsage() { + /* + * 1. Test with bestLoanRates 1 + * 2. Test with savingsCalculator 20.0,30.0 10.0,5.0 + * 3. Test with mortgageCalculator 264000 30 3.74f + */ + } + +} diff --git a/src/test/java/com/h2/AppTest.java b/src/test/java/com/h2/AppTest.java index 38e8e89d..57b43f2f 100644 --- a/src/test/java/com/h2/AppTest.java +++ b/src/test/java/com/h2/AppTest.java @@ -1,20 +1,56 @@ package com.h2; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.function.Try; + +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.commons.util.ReflectionUtils.*; -import org.junit.jupiter.api.Test; +public class AppTest { + private final String classToFind = "com.h2.App"; + + public Optional> getAppClass() { + Try> aClass = tryToLoadClass(classToFind); + return aClass.toOptional(); + } + + @Test + public void assertClassExistence() { + final Optional> maybeClass = getAppClass(); + assertTrue(maybeClass.isPresent(), classToFind + " should be present"); + assertEquals(classToFind, maybeClass.get().getCanonicalName()); + } + + @Test + public void assertPrivateMethodExistence() { + final String methodName = "add"; + final Optional> maybeClass = getAppClass(); + Class aClass = maybeClass.get(); + Optional maybeMethod = findMethod(aClass, methodName, int[].class); + assertTrue(maybeMethod.isPresent(), methodName + " should be present in " + aClass.getCanonicalName()); + + final Method method = maybeMethod.get(); + assertTrue(isPrivate(method), methodName + " should be private"); + + assertEquals(int.class, method.getReturnType(), methodName + " should return type should be 'int'"); + + Parameter[] parameters = method.getParameters(); + assertEquals(1, parameters.length, methodName + " should have 1 parameter"); + assertEquals(int[].class, parameters[0].getType(), methodName + " parameter should be of type 'int[]'"); + + assertTrue(isStatic(method), methodName + "should be static method"); + assertTrue(isPrivate(method), methodName + "should be private method"); + } -/** - * Unit test for simple App. - */ -public class AppTest -{ - /** - * Rigorous Test :-) - */ @Test - public void shouldAnswerWithTrue() - { - assertTrue( true ); + public void testDoubleTheNumber() { + for (int i = 1; i < 10; i++) { + assertEquals(2 * i, App.doubleTheNumber(i), i + " should be " + 2 * i); + } } } diff --git a/src/test/java/com/h2/BestLoanRatesTest.java b/src/test/java/com/h2/BestLoanRatesTest.java new file mode 100644 index 00000000..9f4c5dea --- /dev/null +++ b/src/test/java/com/h2/BestLoanRatesTest.java @@ -0,0 +1,158 @@ +package com.h2; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.function.Try; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.commons.util.ReflectionUtils.*; + +public class BestLoanRatesTest { + private final String classToFind = "com.h2.BestLoanRates"; + + private final InputStream systemIn = System.in; + private final PrintStream systemOut = System.out; + + private ByteArrayInputStream testIn; + private ByteArrayOutputStream testOut; + + @BeforeEach + public void setUpOutput() { + testOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(testOut)); + } + + private void provideInput(String data) { + testIn = new ByteArrayInputStream(data.getBytes()); + System.setIn(testIn); + } + + private String getOutput() { + return testOut.toString(); + } + + @AfterEach + public void restoreSystemInputOutput() { + System.setIn(systemIn); + System.setOut(systemOut); + } + + public Optional> getAppClass() { + Try> aClass = tryToLoadClass(classToFind); + return aClass.toOptional(); + } + + @Test + public void assertClassExistence() { + final Optional> maybeClass = getAppClass(); + assertTrue(maybeClass.isPresent(), classToFind + " should be present"); + assertEquals(classToFind, maybeClass.get().getCanonicalName()); + } + + @Test + public void testName() { + final String name = "H2"; + final int age = 32; + final String testString = name + "\n" + age; + provideInput(testString); + + BestLoanRates.main(new String[0]); + + List outputList = Arrays.stream(getOutput().split("\n")).collect(Collectors.toList()); + assertEquals("Enter your name", outputList.get(0)); + assertEquals("Hello " + name, outputList.get(1)); + } + + @Test + public void testGetRatesMethod() { + String getRates = "getRates"; + final Optional> maybeClass = getAppClass(); + assertTrue(maybeClass.isPresent(), classToFind + " should be present"); + Class c = maybeClass.get(); + List methods = Arrays.stream(c.getDeclaredMethods()) + .filter(m -> m.getName().equals(getRates)) + .collect(Collectors.toList()); + + assertEquals(1, methods.size(), getRates + " must be defined as a method in CommandLineApp"); + + final Method method = methods.get(0); + assertEquals(float.class, method.getReturnType(), getRates + " must return 'float' as the return type"); + assertTrue(isStatic(method), getRates + " must be a static method"); + assertTrue(isPublic(method), getRates + " must be a public method"); + } + + @Test + public void testBestRatesExistence() throws IllegalAccessException { + String bestRates = "bestRates"; + final Optional> maybeClass = getAppClass(); + assertTrue(maybeClass.isPresent(), classToFind + " should be present"); + Class c = maybeClass.get(); + List fields = Arrays.stream(c.getFields()) + .filter(f -> f.getName().equals(bestRates)) + .collect(Collectors.toList()); + + assertEquals(1, fields.size(), bestRates + " must be defined as a field in CommandLineApp."); + + Field field = fields.get(0); + assertEquals(Map.class, field.getType(), bestRates + " must be of type 'Map'"); + + field.setAccessible(true); + Map fieldValues = (Map) field.get(BestLoanRates.class); + + assertEquals(3, fieldValues.size(), bestRates + " should have 3 entries"); + assertEquals(5.50f, fieldValues.get(1), bestRates + " should return '5.50f' for key '1'"); + assertEquals(3.45f, fieldValues.get(2), bestRates + " should return '3.45f' for key '2'"); + assertEquals(2.67f, fieldValues.get(3), bestRates + " should return '2.67f' for key '3'"); + } + + @Test + public void testNameAndValidLoanTerm() { + final String name = "H2"; + final int loanTermInYears = 2; + final String testString = name + "\n" + loanTermInYears; + provideInput(testString); + + BestLoanRates.main(new String[0]); + + List outputList = Arrays.stream(getOutput().split("\n")).collect(Collectors.toList()); + assertEquals("Enter your name", outputList.get(0)); + assertEquals("Hello " + name, outputList.get(1)); + + assertEquals("Enter the loan term (in years)", outputList.get(2)); + assertEquals("Best Available Rate: " + BestLoanRates.bestRates.get(loanTermInYears) + "%", outputList.get(3)); + assertEquals(4, outputList.size()); + } + + @Test + public void testNameAndInValidLoanTerm() { + final String name = "H2"; + final int loanTermInYears = 20; + final String testString = name + "\n" + loanTermInYears; + provideInput(testString); + + BestLoanRates.main(new String[0]); + + List outputList = Arrays.stream(getOutput().split("\n")).collect(Collectors.toList()); + assertEquals("Enter your name", outputList.get(0)); + assertEquals("Hello " + name, outputList.get(1)); + + assertEquals("Enter the loan term (in years)", outputList.get(2)); + assertEquals("No available rates for term: " + loanTermInYears + " years", outputList.get(3)); + assertEquals(4, outputList.size()); + } + +} diff --git a/src/test/java/com/h2/MortgageCalculatorTest.java b/src/test/java/com/h2/MortgageCalculatorTest.java new file mode 100644 index 00000000..c867ee23 --- /dev/null +++ b/src/test/java/com/h2/MortgageCalculatorTest.java @@ -0,0 +1,95 @@ +package com.h2; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class MortgageCalculatorTest { + + @Disabled + @Test + public void testMortgageCalculatorExists() { + } + + @Disabled + @Test + public void testExistenceOfPrivateFields() { + } + + @Disabled + @Test + public void testConstructor() { + } + + @Disabled + @Test + public void testFieldsValueSetWhenConstructorCalled() { + } + + @Disabled + @Test + public void testExistenceOfNumberOfPayments() { + /* + 1. Existence + 2. Private + 3. Return Type + */ + } + + @Disabled + @Test + public void testNumberOfPaymentsCorrectness() { + /* + 1. Correct output based on termInYears + */ + } + + @Disabled + @Test + public void testExistenceOfMonthlyInterestRate() { + /* + 1. Existence + 2. Private + 3. Return Type + */ + } + + @Disabled + @Test + public void testMonthlyInterestRateCorrectness() { + /* + 1. Correct output based on annualRate + */ + } + + @Disabled + @Test + public void testExistenceOfMonthlyPayment() { + /* + 1. Existence + 2. Private + 3. Return Type + */ + } + + @Disabled + @Test + public void testMonthlyPaymentCorrectness() { + /* + 1. Correct output based on loanAmount, termInTYears, and annualRate + */ + } + + @Disabled + @Test + public void testMainMethodPrintsCorrectOutput() { + /* + 1. MortgageCalculator.main. + */ + } + + @Disabled + @Test + public void testMainMethodForCorrectArgumentLengths() { + + } +} \ No newline at end of file diff --git a/src/test/java/com/h2/SavingsCalculatorTest.java b/src/test/java/com/h2/SavingsCalculatorTest.java new file mode 100644 index 00000000..d0bce46c --- /dev/null +++ b/src/test/java/com/h2/SavingsCalculatorTest.java @@ -0,0 +1,228 @@ +package com.h2; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.function.Try; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.platform.commons.util.ReflectionUtils.*; + +@SuppressWarnings("unused") +public class SavingsCalculatorTest { + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + + @BeforeEach + public void setStreams() { + System.setOut(new PrintStream(out)); + } + + @AfterEach + public void restoreInitialStreams() { + System.setOut(originalOut); + } + + private final String classToFind = "com.h2.SavingsCalculator"; + + public Optional> getAppClass() { + final Try> aClass = tryToLoadClass(classToFind); + return aClass.toOptional(); + } + + @Test + public void testSavingsCalculatorExists() { + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent(), classToFind + " must be created"); + } + + @Test + public void testExistenceOfPrivateFields() { + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + final Set fieldNames = new HashSet<>(Arrays.asList("credits", "debits")); + + final Field[] declaredFields = savingsCalculator.getDeclaredFields(); + assertEquals(2, declaredFields.length, "2 fields should be available in " + classToFind); + + for (final Field field : declaredFields) { + assertTrue(isPrivate(field), field.getName() + " should be declared private"); + assertEquals(float[].class, field.getType(), field.getName() + " should be of type 'float[]'"); + assertTrue(fieldNames.contains(field.getName()), field.getName() + " is not a valid name. It should be either 'credits' or 'debits'"); + } + } + + @Test + public void testConstructor() { + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + final Constructor[] constructors = savingsCalculator.getDeclaredConstructors(); + + assertEquals(1, constructors.length, classToFind + " should have 1 constructor"); + + final Constructor constructor = constructors[0]; + assertTrue(isPublic(constructor), "constructor must be declared 'public'"); + assertEquals(2, constructor.getParameterCount(), "Constructor should have 2 parameters"); + + for (final Parameter parameter : constructor.getParameters()) { + assertEquals(float[].class, parameter.getType(), "Constructor parameter should be of type 'float[]'"); + } + } + + @Test + public void testFieldsValueSetWhenConstructorCalled() throws IllegalAccessException { + float[] credits = new float[]{10.0f, 20.0f}; + float[] debits = new float[]{5.0f}; + final SavingsCalculator calculator = new SavingsCalculator(credits, debits); + + final Class clazz = calculator.getClass(); + final Field[] fields = clazz.getDeclaredFields(); + + for (final Field field : fields) { + field.setAccessible(true); + float[] fieldValues = (float[]) field.get(calculator); + if (field.getName().equals("credits")) { + assertArrayEquals(credits, fieldValues, "credits parameter should set the value in class field name 'credits'"); + } else if (field.getName().equals("debits")) { + assertArrayEquals(debits, fieldValues, "debits parameter should set the value in class field name 'debits'"); + } + } + } + + @Test + public void testCalculateExists() { + final String methodName = "calculate"; + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + + final Method[] methods = savingsCalculator.getDeclaredMethods(); + final List filteredMethod = Arrays.stream(methods).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); + + assertEquals(1, filteredMethod.size(), classToFind + " should contain a method called '" + methodName + "'"); + + final Method calculate = filteredMethod.get(0); + assertTrue(isPublic(calculate), methodName + " must be declared as 'public'"); + assertEquals(float.class, calculate.getReturnType(), methodName + " method must return a value of type 'float'"); + } + + @Test + public void testSumOfCreditsExists() { + final String methodName = "sumOfCredits"; + + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + + final Method[] methods = savingsCalculator.getDeclaredMethods(); + final List filteredMethod = Arrays.stream(methods).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); + + assertEquals(1, filteredMethod.size(), classToFind + " should contain a method called '" + methodName + "'"); + + final Method sumOfCredits = filteredMethod.get(0); + assertTrue(isPrivate(sumOfCredits), methodName + " must be declared as 'private'"); + assertEquals(float.class, sumOfCredits.getReturnType(), methodName + " method must return a value of type 'float'"); + } + + @Test + public void testSumOfCreditsWorksCorrectly() { + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + + float[] credits = new float[]{10.0f, 20.0f}; + float[] debits = new float[]{5.0f}; + + final SavingsCalculator calculator = newInstance(SavingsCalculator.class, credits, debits); + + final String methodName = "sumOfCredits"; + final Optional maybeMethod = findMethod(SavingsCalculator.class, methodName); + assertTrue(maybeMethod.isPresent()); + final Method sumOfCredits = maybeMethod.get(); + final float result = (float) invokeMethod(sumOfCredits, calculator); + + assertEquals(30.0f, result, methodName + " is not returning sum of credits."); + } + + @Test + public void testSumOfDebitsExists() { + final String methodName = "sumOfDebits"; + + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + + final Method[] methods = savingsCalculator.getDeclaredMethods(); + final List filteredMethod = Arrays.stream(methods).filter(method -> method.getName().equals(methodName)).collect(Collectors.toList()); + + assertEquals(1, filteredMethod.size(), classToFind + " should contain a method called '" + methodName + "'"); + + final Method sumOfCredits = filteredMethod.get(0); + assertTrue(isPrivate(sumOfCredits), methodName + " must be declared as 'private'"); + assertEquals(float.class, sumOfCredits.getReturnType(), methodName + " method must return a value of type 'float'"); + + } + + @Test + public void testSumOfDebitsWorksCorrectly() { + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + + float[] credits = new float[]{10.0f, 20.0f}; + float[] debits = new float[]{5.0f, 10.f}; + + final SavingsCalculator calculator = newInstance(SavingsCalculator.class, credits, debits); + + final String methodName = "sumOfDebits"; + final Optional maybeMethod = findMethod(SavingsCalculator.class, methodName); + assertTrue(maybeMethod.isPresent()); + final Method sumOfDebits = maybeMethod.get(); + final float result = (float) invokeMethod(sumOfDebits, calculator); + + assertEquals(15.0f, result, methodName + " is not returning sum of credits."); + } + + @Test + public void testCalculateWorksCorrectly() { + final Optional> maybeSavingsCalculator = getAppClass(); + assertTrue(maybeSavingsCalculator.isPresent()); + final Class savingsCalculator = maybeSavingsCalculator.get(); + + float[] credits = new float[]{10.0f, 20.0f}; + float[] debits = new float[]{5.0f, 10.f}; + + final SavingsCalculator calculator = newInstance(SavingsCalculator.class, credits, debits); + final float result = calculator.calculate(); + + assertEquals((10.0f + 20.0f) - (5.0f + 10.0f), result, "calculate method is not returning sum of credits minus sum of debits"); + } + + @Test + public void testMainMethodPrintsCorrectOutput() { + final String credits = "10.0,20.0"; + final String debits = "5.0,20.0"; + + SavingsCalculator.main(new String[]{credits, debits}); + assertEquals("Net Savings = 5.0\n", out.toString()); + } + + @Disabled + @Test + public void testMainMethodForCorrectArgumentLengths() { + + } + +} \ No newline at end of file diff --git a/src/test/java/com/h2/UtilitiesTest.java b/src/test/java/com/h2/UtilitiesTest.java new file mode 100644 index 00000000..4acd3137 --- /dev/null +++ b/src/test/java/com/h2/UtilitiesTest.java @@ -0,0 +1,72 @@ +package com.h2; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.function.Try; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass; + +public class UtilitiesTest { + private final String classToFind = "com.h2.Utilities"; + + public Optional> getAppClass() { + Try> aClass = tryToLoadClass(classToFind); + return aClass.toOptional(); + } + + @Test + public void assertClassExistence() { + final Optional> maybeClass = getAppClass(); + assertTrue(maybeClass.isPresent(), classToFind + " should be present"); + assertEquals(classToFind, maybeClass.get().getCanonicalName()); + } + + @Disabled + @Test + public void testGetFloatValueExistence() { + /* + * 1. Method exists + * 2. isPublic, isStatic, returns float, takes String param + */ + } + + @Disabled + @Test + public void testGetIntValueExistence() { + /* + * 1. Method exists + * 2. isPublic, isStatic, returns int, takes String param + */ + } + + @Disabled + @Test + public void testGetLongValueExistence() { + /* + * 1. Method exists + * 2. isPublic, isStatic, returns long, takes String param + */ + } + + @Disabled + @Test + public void testGetFloatValueCorrectness() { + + } + + @Disabled + @Test + public void testGetLongValueCorrectness() { + + } + + @Disabled + @Test + public void testGetIntValueCorrectness() { + + } +}