Skip to content

Commit

Permalink
Add 'Retirement Calculator' solution
Browse files Browse the repository at this point in the history
  • Loading branch information
durimkryeziu committed Nov 18, 2024
1 parent 39b8836 commit 0304a97
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Exercises for Programmers: 57 Challenges to Develop Your Coding Skills by Brian
- Exercise 3. [Printing Quotes](https://github.com/durimkryeziu/exercises-for-programmers-java/tree/main/printing-quotes)
- Exercise 4. [Mad Lib](https://github.com/durimkryeziu/exercises-for-programmers-java/tree/main/mad-lib)
- Exercise 5. [Simple Math](https://github.com/durimkryeziu/exercises-for-programmers-java/tree/main/simple-math)
- Exercise 6. Retirement Calculator
- Exercise 6. [Retirement Calculator](https://github.com/durimkryeziu/exercises-for-programmers-java/tree/main/retirement-calculator)

**Calculations**
- Exercise 7. Area of a Rectangular Room
Expand Down
8 changes: 8 additions & 0 deletions retirement-calculator/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
id 'com.craftsmanshipinsoftware.common-conventions'
}

dependencies {
testImplementation(libs.assertj.core)
testImplementation(libs.junit.jupiter)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.craftsmanshipinsoftware.retirement;

import java.time.Year;

public class Main {

public static void main(String[] args) {
RetirementCalculator calculator = new RetirementCalculator(System.in, System.out, Year::now);
calculator.printYearsLeft();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.craftsmanshipinsoftware.retirement;

import java.io.InputStream;
import java.io.PrintStream;
import java.time.Year;
import java.util.Objects;
import java.util.Scanner;

class RetirementCalculator {

private final InputStream inputStream;
private final PrintStream printStream;
private final YearProvider yearProvider;

RetirementCalculator(InputStream inputStream, PrintStream printStream, YearProvider yearProvider) {
Objects.requireNonNull(inputStream, "inputStream must not be null");
Objects.requireNonNull(printStream, "printStream must not be null");
Objects.requireNonNull(yearProvider, "yearProvider must not be null");
this.inputStream = inputStream;
this.printStream = printStream;
this.yearProvider = yearProvider;
}

void printYearsLeft() {
try (Scanner scanner = new Scanner(inputStream)) {
int currentAge = toInt(promptForInput("What is your current age? ", scanner));
int retirementAge = toInt(promptForInput("At what age would you like to retire? ", scanner));
int yearsLeft = retirementAge - currentAge;
if (yearsLeft <= 0) {
this.printStream.println("You can already retire.");
} else {
this.printStream.printf("You have %d years left until you can retire.%n", yearsLeft);
}
Year currentYear = this.yearProvider.currentYear();
this.printStream.printf("It's %s, so you can retire in %s.%n", currentYear,
currentYear.plusYears(yearsLeft));
}
}

private String promptForInput(String prompt, Scanner scanner) {
this.printStream.print(prompt);
String input = scanner.hasNext() ? scanner.nextLine() : null;
if (input == null || input.isBlank()) {
throw new IllegalArgumentException("Input must not be empty!");
}
return input;
}

private int toInt(String value) {
try {
int number = Integer.parseInt(value);
if (number < 0) {
throw new IllegalArgumentException("Please enter a positive number! Input: %s".formatted(value));
}
return number;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Please enter a valid number! Input: %s".formatted(value), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.craftsmanshipinsoftware.retirement;

import java.time.Year;

@FunctionalInterface
interface YearProvider {

Year currentYear();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.craftsmanshipinsoftware.retirement;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.time.Year;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class RetirementCalculatorTest {

@Test
void asksForCurrentAge() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream("25\n65".getBytes()),
new PrintStream(outputStream), Year::now);

calculator.printYearsLeft();

assertThat(outputStream.toString()).contains("What is your current age?");
}

@Test
void asksForRetirementAge() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream("25\n65".getBytes()),
new PrintStream(outputStream), Year::now);

calculator.printYearsLeft();

assertThat(outputStream.toString()).contains("At what age would you like to retire?");
}

@Test
void displaysYearsLeftUntilRetirement() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream("25\n65".getBytes()),
new PrintStream(outputStream), new FakeYearProvider());

calculator.printYearsLeft();

assertThat(outputStream.toString()).containsSequence("You have 40 years left until you can retire.\n",
"It's 2015, so you can retire in 2055.\n");
}

@ParameterizedTest
@ValueSource(strings = {"65\n65", "75\n65"})
void displaysYouCanAlreadyRetireWhenCurrentAgeIsRetirementAgeOrMore(String input) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()),
new PrintStream(outputStream), Year::now);

calculator.printYearsLeft();

assertThat(outputStream.toString()).contains("You can already retire.");
}

@ParameterizedTest
@ValueSource(strings = {"\n65", "25\n"})
void inputIsRequired(String input) {
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()),
new PrintStream(new ByteArrayOutputStream()), Year::now);

assertThatIllegalArgumentException()
.isThrownBy(calculator::printYearsLeft)
.withMessage("Input must not be empty!");
}

@ParameterizedTest
@ValueSource(strings = {"abc\n65", "25\nabc"})
void inputMustBeANumber(String input) {
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()),
new PrintStream(new ByteArrayOutputStream()), Year::now);

assertThatIllegalArgumentException()
.isThrownBy(calculator::printYearsLeft)
.withMessage("Please enter a valid number! Input: abc");
}

@ParameterizedTest
@ValueSource(strings = {"-1\n65", "25\n-1"})
void inputMustBeAPositiveNumber(String input) {
RetirementCalculator calculator = new RetirementCalculator(new ByteArrayInputStream(input.getBytes()),
new PrintStream(new ByteArrayOutputStream()), Year::now);

assertThatIllegalArgumentException()
.isThrownBy(calculator::printYearsLeft)
.withMessage("Please enter a positive number! Input: -1");
}

static final class FakeYearProvider implements YearProvider {

@Override
public Year currentYear() {
return Year.of(2015);
}
}
}
9 changes: 8 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
rootProject.name = 'exercises-for-programmers-java'
include('saying-hello', 'characters-count', 'printing-quotes', 'mad-lib', 'simple-math')
include(
'saying-hello',
'characters-count',
'printing-quotes',
'mad-lib',
'simple-math',
'retirement-calculator'
)

0 comments on commit 0304a97

Please sign in to comment.