Merge pull request #3 from tdt4100/add-oving2

Add oving 2
This commit is contained in:
Andreas Omholt Olsen
2026-01-19 11:57:08 +01:00
committed by GitHub Enterprise
18 changed files with 1456 additions and 0 deletions

View File

@@ -6,3 +6,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
| -------------------------------------------- | ----------------------------------------- |
| [Øving 0](./oppgavetekster/oving0/README.md) | Introduksjon og oppsett av Java |
| [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang |
| [Øving 2](./oppgavetekster/oving2/README.md) | Innkapsling og validering |

View File

@@ -0,0 +1,38 @@
# Innkapsling - Account-oppgave
Oppgaven er en innkapslet og litt utvidet variant av [Account-oppgaven](../oving1/Account.md) under temaet [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373), og stiller blant annet større krav til validering.
Dersom du ikke har gjort [Account-oppgaven](../oving1/Account.md), bør du gjøre den først. Løsningsforslag til denne oppgaven kommer til å bli tilgjengelig [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving1/Account.java) etter siste demonstrasjonsfrist for øving 1.
Et `Account`-objekt inneholder data om beløpet som står på kontoen og rentefoten (prosentpoeng).
Begge verdiene skal oppgis og settes når objektet opprettes og ingen av verdiene kan være negative.
`Account`-klassen har metoder for å sette inn og ta ut beløp, og legge til påløpte renter, i tillegg til en konstruktør for å initialisere en ny konto. Alle disse skal utløse unntak av typen `IllegalArgumentException`, dersom et argument ikke tilfredstiller kravene som angis.
- `Account(double, double)` - konstruktøren skal ta inn startbeløpet og rentefoten (prosentpoeng). Ingen av disse kan være negative.
- `double getBalance()` - returnerer beløpet som står på kontoen.
- `double getInterestRate()` - returnerer renten på kontoen.
- `void setInterestRate(double)` - denne metoden tar inn en ikke-negativ verdi og setter renten til denne verdien.
- `void deposit(double)` - denne metoden tar inn et ikke-negativt beløp og øker konto-beløpet tilsvarende.
- `void withdraw(double)` - denne metoden tar inn et ikke-negativt beløp og minsker konto-beløpet tilsvarende. Dersom det nye konto-beløpet er negativt, så skal tilstanden ikke endre, og det skal utløses et unntak av typen `IllegalArgumentException`.
- `void addInterest()` - beregner renta og legger det til konto-beløpet.
## Leseliste
- [Gyldig tilstand](https://www.ntnu.no/wiki/display/tdt4100/Gyldig+tilstand) - Tilstanden til et objekt er verdien av alle attributtene. En viktig del av [oppførselen til et objekt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) er å sikre at tilstanden til objektet alltid er _gyldig_, dvs. at alle attributtene har gyldige/konsistente verdier.
- [Innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) - Innkapsling er en programmeringsteknikk som har som formål å hindre direkte tilgang til tilstanden til et objekt fra objekter av andre klasser.
- [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder) - En valideringsmetode har som formål å sjekke om en eller flere verdier er gyldige, slik at dette kan sjekkes av f.eks. setter-metoder før tilsvarende attributter evt. settes.
## Del 1: Innkapsling og validering - Teori
Ta utgangspunkt i koden fra [Account](../oving1/Account.md)-klassen og besvar følgende spørsmål:
- Forklar hvorfor metodene over kan sies å være en komplett innkapsling av tilstanden?
- Er denne klassen data-orientert eller tjeneste-orientert? Begrunn svaret!
## Del 2 - Java-kode
Implementer endringene fra [Account](../oving1/Account.md)-klassen i den nye `Account`-klassen med oppførsel som beskrevet over.
Testkode for denne oppgaven finner du i [oving2/AccountTest.java](../../src/test/java/oving2/AccountTest.java).

View File

@@ -0,0 +1,38 @@
# Innkapsling og gyldig tilstand - Oppgave om innkapsling og validering av klasser
I denne oppgaven skal du velge tre oppgaver som du har gjort i øving 1 fra listen nedenfor, og innkapsle og validere disse klassene. Dersom du ikke har gjort tre oppgaver, bør du gjøre dette først. Løsningsforslag til øving 1 kommer til å befinne seg [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving1) etter siste demonstrasjonsfrist for øving 1.
Skriv svar (stikkord/få, korte setninger) på spørsmål 1-4 (fra del 1 nedenfor) som kommentarer i koden din.
__Oppgaver__:
- [Digit-oppgave](../oving1/Digit.md) (Lett)
- [UpOrDownCounter-oppgave](../oving1/UpOrDownCounter.md) (Lett)
- [Location-oppgave](../oving1/Location.md) (Lett)
- [StopWatch-oppgave](../oving1/StopWatch.md) (Medium)
- [LineEditor-oppgave med fri peker](./LineEditor.md) (Vanskelig)
Merk at spesifikasjonen for [LineEditor](../oving1/LineEditor.md) er litt utvidet for denne oppgaven. Se [LineEditor-oppgave med fri peker](./LineEditor.md).
## Del 1: Innkapsling og validering - Teori
Ta utgangspunkt i (koden for) den originale klassen og besvar følgende spørsmål:
- Hvordan skal private og public brukes for at denne klassen skal være korrekt innkapslet?
- Hva slags validering bør legges til for å sikre gyldig tilstand?
- Hvilke metoder må evt. legges til?
- Vil du karakterisere denne klassen som data-orientert eller tjeneste-orientert. Begrunn svaret!
## Del 2: Java-kode
Implementer endringene foreslått i punktene 1-3 og prøv ut klassene. Husk å kopiere koden din fra mappen i øving 1 til [`src/main/java/oving2`](../../src/main/java/oving2)!
Testkoder for denne oppgaven finner du her:
- [oving2/DigitTest.java](../../src/test/java/oving2/DigitTest.java).
- [oving2/UpOrDownCounterTest.java](../../src/test/java/oving2/UpOrDownCounterTest.java).
- [oving2/LocationTest.java](../../src/test/java/oving2/LocationTest.java).
- [oving2/StopWatchTest.java](../../src/test/java/oving2/StopWatchTest.java).
- [oving2/LineEditorTest.java](../../src/test/java/oving2/LineEditorTest.java).
Testkodene viser om du har innkapslet på samme måte som fagstaben har gjort. Din kode kan fungere selv om testene feiler, dersom du har valgt en løsere/strammere innkapsling iht. argumentasjonen i 1-3. Er du enig med hvordan fagstaben har gjort det?

View File

@@ -0,0 +1,16 @@
# Gyldig tilstand - LineEditor-oppgave med fri peker
Oppgaven utvider [Tilstand og oppførsel - LineEditor-oppgave](../oving1/LineEditor.md) med validering.
Denne oppgaven tar utgangspunkt i [Tilstand og oppførsel - LineEditor-oppgave](../oving1/LineEditor.md) og utvider `LineEditor`-klassen med metoder for å endre teksten og tekstinnsettingsposisjonen direkte, så det blir enklere å gjøre om tilstanden til objektet.
Dersom du ikke har gjort [LineEditor-oppgaven](../oving1/LineEditor.md), bør du gjøre den først. Løsningsforslag til denne oppgaven kommer til å bli tilgjengelig [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving1/LineEditor.java) etter siste demonstrasjonsfrist for øving 1.
Endringer:
- Når teksten endres skal tekstinnsettingsposisjonen settes til å være bak teksten.
- Det skal ikke være mulig å passere `null` som tekst.
Hvordan vil du implementere dette med én eller flere metoder, inkludert valideringsmetode(r), slik at en er sikret at `LineEditor`-objekter aldri blir ugyldige?
Testkode for denne oppgaven finner du her: [oving2/LineEditorTest.java](../../src/test/java/oving2/LineEditorTest.java).

View File

@@ -0,0 +1,59 @@
# Innkapsling - Person-oppgave
Oppgaven handler om en `Person`-klasse, som håndterer informasjon om en person (navn, e-post, fødselsdato og kjønn) og implementerer innkapslingsmetoder med validering.
Et `Person`-objekt inneholder _navn_ (både fornavn og etternavn), _e-post_, _fødselsdag_ og _kjønn_:
- Navnet inneholder både fornavn og etternavn (og ingen mellomnavn), som begge må være på minst to bokstaver langt, navnene må være skilt med ett mellomrom og kun inneholde bokstaver.
- E-post-adressen (hvis den ikke er `null`) må være på formen `fornavn.etternavn@domene.landskode`, f.eks. `hallvard.trætteberg@ntnu.no` (en liste over landskoder finner du [her](http://pastebin.com/chG6WLWF)).
- Fødselsdagen skal være et dato-objekt (java.util.Date) og kan ikke være frem i tid.
- En persons kjønn skal kunne returneres som `'M'`, `'F'` eller `'\0'` (null-tegnet).
`Person`-klassen har tilgangsmetoder for å hente og sette tilstandene. Dersom et argument er ugyldig i seg selv, så skal unntaket `IllegalArgumentException` utløses.
- `void setName(String)` - oppdaterer navnet (fornavn og etternavn med mellomrom mellom), dersom det er gyldig i henhold til kravene over. Det er greit om navnet som settes, ikke stemmer med e-post-adressen. Kast et passende unntak dersom navnet er `null` eller dersom e-post-adressen allerede har blitt satt.
- `void setEmail(String)` - oppdaterer e-post-adressen, etter å ha sjekket at den stemmer med navnet. Kast et passende unntak dersom e-post-adressen er `null` eller dersom navnet enda ikke har blitt satt.
- `void setBirthday(Date)` - oppdaterer fødselsdatoen. Kast et passende unntak dersom fødselsdatoen er `null` eller ligger i fremtiden.
- `void setGender(char)` - oppdaterer kjønnet. Kast et passende unntak dersom kjønnet er ugyldig.
I tillegg til disse såkalte _setter_-metodene, så må `Person`-klassen ha tilsvarende _getter_-metoder.
## Leseliste
- [Gyldig tilstand](https://www.ntnu.no/wiki/display/tdt4100/Gyldig+tilstand) - Tilstanden til et objekt er verdien av alle attributtene. En viktig del av [oppførselen til et objekt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) er å sikre at tilstanden til objektet alltid er _gyldig_, dvs. at alle attributtene har gyldige/konsistente verdier.
- [Innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) - Innkapsling er en programmeringsteknikk som har som formål å hindre direkte tilgang til tilstanden til et objekt fra objekter av andre klasser.
- [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder) - En valideringsmetode har som formål å sjekke om en eller flere verdier er gyldige, slik at dette kan sjekkes av f.eks. setter-metoder før tilsvarende attributter evt. settes.
- [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) - Siden gir en innføring i String-klassen og en oversikt over nyttig String-metoder.
## Del 1 Java-kode
Implementer `Person`-klassen med stram innkapsling. Eventuelle hjelpemetoder for validering bør også ha stram innkapsling. Det kan være lurt å lese om [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) og dens metoder før du setter i gang.
Testkode for denne oppgaven finner du i [oving2/PersonTest.java](../../src/test/java/oving2/PersonTest.java).
Merk at din implementasjon må ligge i en pakke med samme navn som testkodens pakke. Pass derfor på at Person-klassen ligger i pakken `oving2`.
## Del 2 - Spørsmål om innkapsling
- Foreslå en alternativ innkapsling av navnet. Hint: del opp.
- Foreslå _to_ alternative strategier for å kapsle inn tilstand som er koblet slik navn og e-post er. Hint: 1) samtidig og 2) dekoble.
## Ekstraoppgave - Personnummer
Utvid klassen med en persons personnummer. Personnummeret kan ikke settes før kjønn og fødselsdag er satt.
Et personnummer består grovt sett av fødselsdatoen, et (vilkårlig) løpenummer og to kontrollsifre. Kontrollsifrene gjør det enklere å sjekke om et personnummer er ekte. Mer spesifikt er reglene for personnummer som følger:
- Et personnummer består av 11 siffer, med følgende struktur: **D1D2**M1M2**Y1Y2**N1N2N3**K1K2** (fet skrift for lesbarhet).
- De seks første sifrene, **D1D2**M1M2**Y1Y2**, tilsvarer fødselsdatoens dag (1-31), måned (1-12) og år (0-99).
- De tre neste sifrene, N1N2N3, kan antas å være vilkårlige, men N3 må være partall for kvinner og oddetall for menn.
- De to siste sifrene, K1K2, er kontrollsifre, som hver for seg beregnes ut fra de foregående sifrene. Formelen for dem begge er `11 (VS % 11)`, hvor VS (veid sum) for `K1` er `D1*F1 + D2*F2 + … + N2*F8 + N3*F9` og `VS` for `K2` er `D1*G1 + D2*G2 + … + N3*G9 + K1*G10`. Fene og Gene er oppgitt i tabellen under. Dersom formelen gir tallet 11 så skal verdien 0 brukes isteden. Om både K1 og K2 stemmer med kontrollsifferne generert basert på formlene over, så er kontrollsifferne i personnummeret gyldig.
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| F | 3 | 7 | 6 | 1 | 8 | 9 | 4 | 5 | 2 | |
| G | 5 | 4 | 3 | 2 | 7 | 6 | 5 | 4 | 3 | 2 |
Implementer kode for å sette (med metoden `setSSN(String)`) og validere et gyldig personnummer. Du må også kaste passende unntak dersom brukeren prøver å endre tilstanden slik at personnummeret ville blitt ugyldig.
Testkode for denne oppgaven finner du i [oving2/PersonTest2.java](../../src/test/java/oving2/PersonTest2.java).

View File

@@ -0,0 +1,46 @@
# Øving 2: Innkapsling og validering
**Øvingsmål**:
- Lære å innkapsle klasser og metoder etter god programmeringsskikk
- Lære å validere argumenter for å sikre gyldig tilstand
**Øvingskrav**:
- Kunne forstå og implementere hvordan en klasse best bør innkapsles
- Kunne skrive kode for å validere argumenter for å sikre gyldig tilstand
- Kunne utløse exceptions ved ugyldige argumenter i en metode
## Dette må du gjøre
### Del 1: Teori
Les [wikisiden om innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) og svar på følgende:
- Hva er en **synlighetsmodifikator**?
- Hva er forskjellen på **private** og **public** og når brukes de?
Teori-oppgavene besvares i en tekstfil eller på papir, og gjennomgås med studass ved godkjenning.
### Del 2: Programmering
Velg minst 2 av følgende oppgaver:
- [Innkapsling og validering av 3 eksisterende klasser](./Encapsulation.md) (Varierende)
- [Account](./Account.md) (Lett)
- [Person](./Person.md) (Medium)
- [Vehicle](./Vehicle.md) (Medium)
Oppgavene for denne øvingen skal lagres i [`src/main/java/oving2`](../../src/main/java/oving2). Test-filene som kjøres for å teste koden ligger i [`src/test/java/oving2`](../../src/test/java/oving2).
Oppgavene er markert med en vanskeliggrad relativt til hverandre. Det er en god idé å begynne med de lettere oppgavene dersom du ikke er komfortabel med pensum så langt, men det er anbefalt å prøve seg på de vanskeligere oppgavene om du synes de første oppgavene er uproblematiske. Dersom du allerede føler deg trygg på punktene i øvingskravene kan du forsøke å gå rett på de vanskeligere oppgavene. Du er selvfølgelig velkommen til å løse flere oppgaver enn minstekravet, hvilket lurt gjøres med tanke på eksamen og et langt liv som programmerende.
Før du setter i gang kan det vært lurt å lese nevnte [wikiside om innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) nøye. Forelesningene og tilhørende øvingsforelesning er selvsagt også lure å få med seg.
### Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på [Piazza](https://piazza.com/ntnu.no/spring2025/tdt4100).
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for stud.ass innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@@ -0,0 +1,30 @@
# Innkapsling - Vehicle-oppgave
Oppgaven handler om en `Vehicle`-klasse, som håndterer informasjon om et kjøretøy og implementerer innkapslingsmetoder med validering.
Et `Vehicle`-objekt inneholder type _kjøretøy_, _drivstoffet_ det bruker og _registreringsnummer_:
- Typen kan være enten motorsykkel eller bil.
- Drivstoffet kan være enten hydrogen, elektrisitet, diesel eller bensin. Kun biler kan gå på hydrogen.
- Gyldige registreringsnummeret avhenger av typen kjøretøy og drivstoff etter følgende regler:
- Kjøretøy som går på elektrisitet skal ha registreringsnummer som starter med bokstavene `"EL"` eller `"EK"`.
- Hydrogenbilene har registreringsnummer som begynner med bokstavene `"HY"`.
- Dieselkjøretøy og bensinkjøretøy har registreringsnummer som begynner på to bokstaver. De kan ikke være `"EK"`, `"EL"` eller `"HY"`. Bokstavene Æ, Ø og Å skal ikke brukes.
- For alle drivstoff gjelder det at det skal være brukt store bokstaver.
- Ellers så gjelder det at motorsykler har 4 sifre etter bokstavene, mens biler har 5.
Følgende metoder må implementeres:
- `Vehicle(char, char, String)` - Konstruktør der argument-rekkefølgen må være kjøretøystype, drivstofftype og registreringsnummer. Ved ugyldige argumenter utløses unntak av typen `IllegalArgumentException`.
- `char getFuelType()` - returnerer type drivstoff som følgende: `'H'` for hydrogen, `'E'` for elektrisitet, `'D'` for diesel eller `'G'` for bensin.
- `String getRegistrationNumber()` - returnerer registreringsnummeret.
- `void setRegistrationNumber(String)` - endrer registreringsnummeret dersom det er gyldig i henhold til kravene over, og utløser unntak av typen `IllegalArgumentException` dersom det ikke er gyldig.
- `char getVehicleType()` - returnerer kjøretøystype: `'M'` for motorsykkel, `'C'` for bil.
## Java-kode
Implementer `Vehicle`-klassen som beskrevet over med stram innkapsling. Eventuelle hjelpemetoder for validering bør også ha stram innkapsling. Det kan være lurt å lese om [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) og dens metoder før du setter i gang.
Testkode for denne oppgaven finner du i [oving2/VehicleTest.java](../../src/test/java/oving2/VehicleTest.java).
Merk at din implementasjon må ligge i en pakke med samme navn som testkodens pakke. Pass derfor på at Vehicle-klassen ligger i pakken `oving2`.

View File

View File

@@ -0,0 +1,68 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class AccountTest {
private static final double epsilon = 0.000001;
private Account account;
@BeforeEach
public void setUp() {
account = new Account(100, 5);
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(Account.class);
}
@Test
public void testConstructor() {
assertEquals(100.0d, account.getBalance(), epsilon);
assertEquals(5.0d, account.getInterestRate(), epsilon);
assertThrows(IllegalArgumentException.class, () -> {
new Account(-1, 5);
});
assertThrows(IllegalArgumentException.class, () -> {
new Account(100, -1);
});
}
@Test
public void testSetInterestRate() {
account.setInterestRate(7);
assertEquals(7, account.getInterestRate(), epsilon);
assertThrows(IllegalArgumentException.class, () -> {
account.setInterestRate(-2);
});
}
@Test
public void testDeposit() {
account.deposit(100);
assertEquals(200.0d, account.getBalance(), epsilon);
assertThrows(IllegalArgumentException.class, () -> {
account.deposit(-50);
});
}
@Test
public void testWithdraw() {
account.withdraw(50);
assertEquals(50.0d, account.getBalance(), epsilon);
assertThrows(IllegalArgumentException.class, () -> {
account.withdraw(150);
});
}
}

View File

@@ -0,0 +1,75 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class DigitTest {
private Digit digit;
private void testIncrement(int base, boolean checkValue, boolean checkToString) {
digit = new Digit(base);
int i = 0;
String digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
while (i < base) {
if (checkValue) {
assertEquals(i % base, digit.getValue());
}
if (checkToString) {
assertEquals(String.valueOf(digits.charAt(i % base)),
digit.toString().toUpperCase());
}
boolean overflow = digit.increment();
i++;
if (checkValue) {
assertEquals(i % base == 0, overflow);
assertEquals(i % base, digit.getValue());
}
if (checkToString) {
assertEquals(String.valueOf(digits.charAt(i % base)),
digit.toString().toUpperCase());
}
}
}
private void testIncrement(boolean checkValue, boolean checkToString) {
for (int base = 2; base <= 16; base++) {
this.testIncrement(base, checkValue, checkToString);
}
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(Digit.class);
}
@Test
public void testDigit() {
digit = new Digit(10);
assertEquals(0, digit.getValue());
assertEquals(10, digit.getBase());
assertThrows(IllegalArgumentException.class, () -> {
digit = new Digit(-1);
}, "Should not be able to create digit with negative base");
}
@Test
@DisplayName("Digit should increment correctly")
public void testIncrement() {
this.testIncrement(true, false);
}
@Test
@DisplayName("Digit should be displayed by correct characters. For example 10 in base 16 "
+ "should be displayed as A")
public void testToString() {
this.testIncrement(false, true);
}
}

View File

@@ -0,0 +1,196 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LineEditorTest {
private LineEditor lineEditor;
@BeforeEach
public void setUp() {
lineEditor = new LineEditor();
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(LineEditor.class);
}
@Test
public void testConstructor() {
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testGetSetText() {
lineEditor.setText("ABC");
assertEquals("ABC", lineEditor.getText());
assertEquals(3, lineEditor.getInsertionIndex());
lineEditor.setText("");
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
assertThrows(IllegalArgumentException.class, () -> {
lineEditor.setText(null);
}, "Cannot set text to null");
}
@Test
public void testGetSetInsertionIndex() {
lineEditor.setText("ABC");
assertEquals("ABC", lineEditor.getText());
assertEquals(3, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(0);
assertEquals("ABC", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(3);
assertEquals("ABC", lineEditor.getText());
assertEquals(3, lineEditor.getInsertionIndex());
assertThrows(IllegalArgumentException.class, () -> {
lineEditor.setInsertionIndex(-1);
});
assertThrows(IllegalArgumentException.class, () -> {
lineEditor.setInsertionIndex(4);
});
}
@Test
public void testLeft() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.left();
assertEquals("Ja", lineEditor.getText());
assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.left();
assertEquals("Ja", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.left();
assertEquals("Ja", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testRight() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.right();
assertEquals("Ja", lineEditor.getText());
assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.right();
lineEditor.setText("Ja");
assertEquals(2, lineEditor.getInsertionIndex());
lineEditor.right();
lineEditor.setText("Ja");
assertEquals(2, lineEditor.getInsertionIndex());
}
@Test
public void testDeleteLeft() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.deleteLeft();
assertEquals("Ja", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(1);
lineEditor.deleteLeft();
assertEquals("a", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
assertEquals("a", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.deleteLeft();
assertEquals("J", lineEditor.getText());
assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testDeleteRight() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.deleteRight();
assertEquals("Ja", lineEditor.getText());
assertEquals(2, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(1);
lineEditor.deleteRight();
assertEquals("J", lineEditor.getText());
assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
assertEquals("J", lineEditor.getText());
assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.deleteRight();
assertEquals("a", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testInsertString() {
lineEditor.insertString("");
assertEquals("", lineEditor.getText());
assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.insertString("Java");
assertEquals("Java", lineEditor.getText());
assertEquals(4, lineEditor.getInsertionIndex());
lineEditor.insertString(" er gøy!");
assertEquals("Java er gøy!", lineEditor.getText());
assertEquals(12, lineEditor.getInsertionIndex());
lineEditor.setText("Javagøy!");
lineEditor.setInsertionIndex(4);
lineEditor.insertString(" er ");
assertEquals("Java er gøy!", lineEditor.getText());
assertEquals(8, lineEditor.getInsertionIndex());
lineEditor.setText("er gøy!");
lineEditor.setInsertionIndex(0);
lineEditor.insertString("Java ");
assertEquals("Java er gøy!", lineEditor.getText());
assertEquals(5, lineEditor.getInsertionIndex());
assertThrows(IllegalArgumentException.class, () -> {
lineEditor.insertString(null);
}, "Cannot insert null");
}
}

View File

@@ -0,0 +1,107 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class LocationTest {
private Location loc;
/**
* Check that the position of {@link #loc} is equal to the parameters.
*
* @param x Expected x position
* @param y Expected y position
*/
private void checkPos(int x, int y) {
assertEquals(x, loc.getX(), "Wrong x coordinate");
assertEquals(y, loc.getY(), "Wrong y coordinate");
}
@BeforeEach
public void beforeEach() {
loc = new Location();
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(Location.class);
}
@Test
@DisplayName("Constructor")
public void testConstructor() {
this.checkPos(0, 0);
}
@Test
@DisplayName("Move up")
public void testUp() {
loc.up();
this.checkPos(0, -1);
loc.up();
this.checkPos(0, -2);
}
@Test
@DisplayName("Move down")
public void testDown() {
loc.down();
this.checkPos(0, 1);
loc.down();
this.checkPos(0, 2);
}
@Test
@DisplayName("Move left")
public void testLeft() {
loc.left();
this.checkPos(-1, 0);
loc.left();
this.checkPos(-2, 0);
}
@Test
@DisplayName("Move right")
public void testRight() {
loc.right();
this.checkPos(1, 0);
loc.right();
this.checkPos(2, 0);
}
@Test
@DisplayName("Move multiple directions")
public void testComplexMovement() {
loc.right();
this.checkPos(1, 0);
loc.down();
this.checkPos(1, 1);
loc.right();
this.checkPos(2, 1);
loc.down();
this.checkPos(2, 2);
loc.left();
this.checkPos(1, 2);
loc.up();
this.checkPos(1, 1);
loc.up();
this.checkPos(1, 0);
loc.left();
this.checkPos(0, 0);
}
}

View File

@@ -0,0 +1,221 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Date;
import java.util.Random;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class PersonTest {
private Person person;
private void testInvalidName(String invalidName, String existingName) {
assertThrows(IllegalArgumentException.class, () -> {
person.setName(invalidName);
});
assertEquals(existingName, person.getName());
}
private void testInvalidEmail(String invalidEmail, String existingEmail,
Class<? extends Exception> ex) {
assertThrows(ex, () -> {
person.setEmail(invalidEmail);
});
assertEquals(existingEmail, person.getEmail());
}
private static String generateValidDomain() {
Random random = new Random();
int length = random.nextInt(63) + 1;
String validCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
String domain = "";
for (int currentChar = 0; currentChar < length; currentChar++) {
int character = random.nextInt(36);
domain += validCharacters.substring(character, character + 1);
}
return domain;
}
@BeforeEach
public void setUp() {
person = new Person();
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(Person.class);
}
@Test
public void testSetName() {
String name = person.getName();
this.testInvalidName("Ola", name);
this.testInvalidName("O N", name);
this.testInvalidName("Ola Mellom Nordmann", name);
this.testInvalidName("O. Nordmann", name);
assertDoesNotThrow(() -> {
person.setName("Espen Askeladd");
});
assertThrows(IllegalArgumentException.class, () -> {
person.setName(null);
}, "Name cannot be null");
assertEquals("Espen Askeladd", person.getName());
}
@Test
public void testSetBirthday() {
long today = new Date().getTime();
long offset = 1000L * 60L * 60L * 24L * 100L; // About 100 days
// Test with null birthday
assertThrows(IllegalArgumentException.class, () -> {
person.setBirthday(null);
}, "Birthday cannot be null");
// Test with incorrect birthday
assertThrows(IllegalArgumentException.class, () -> {
Date theFuture = new Date(today + offset);
person.setBirthday(theFuture);
});
// Test with correct birthday
Date thePast = new Date(today - offset);
assertDoesNotThrow(() -> {
person.setBirthday(thePast);
});
assertEquals(thePast, person.getBirthday());
}
@Test
public void testSetEmail() {
assertThrows(IllegalStateException.class, () -> {
person.setEmail("ola.nordmann@ntnu.no");
}, "Email cannot be set before name");
person.setName("Ola Nordmann");
String email = person.getEmail();
this.testInvalidEmail("ola.nordmann@ntnu", email, IllegalArgumentException.class);
this.testInvalidEmail("ola.nordmann(at)ntnu.no", email, IllegalArgumentException.class);
this.testInvalidEmail("espen.askeladd@eventyr.no", email, IllegalArgumentException.class);
this.testInvalidEmail(null, email, IllegalArgumentException.class);
assertDoesNotThrow(() -> {
person.setEmail("ola.nordmann@ntnu.no");
});
assertEquals("ola.nordmann@ntnu.no", person.getEmail());
assertThrows(IllegalStateException.class, () -> {
person.setName("Untz Untz");
}, "Name cannot be set after email");
}
@Test
public void testExtraCountryTopLevelDomains() {
String[] cTLDs = {"ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at",
"au", "aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl",
"bm", "bn", "bo", "bq", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "cc", "cd",
"cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cw", "cx",
"cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es",
"et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh",
"gi", "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm",
"hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it",
"je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky",
"kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc",
"md", "me", "mf", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms",
"mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl",
"no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm",
"pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb",
"sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss",
"st", "sv", "sx", "sy", "sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm",
"tn", "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", "uy", "uz", "va",
"vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "ye", "yt", "za", "zm", "zw"};
String[] invalidCTLDs = {"aa", "ab", "ac", "ah", "aj", "ak", "an", "ap", "av", "ay", "bc",
"bk", "bp", "bu", "bx", "cb", "ce", "cj", "cp", "cq", "cs", "ct", "da", "db", "dc",
"dd", "df", "dg", "dh", "di", "dl", "dn", "dp", "dq", "dr", "ds", "dt", "du", "dv",
"dw", "dx", "dy", "ea", "eb", "ed", "ef", "ei", "ej", "ek", "el", "em", "en", "eo",
"ep", "eq", "eu", "ev", "ew", "ex", "ey", "ez", "fa", "fb", "fc", "fd", "fe", "ff",
"fg", "fh", "fl", "fn", "fp", "fq", "fs", "ft", "fu", "fv", "fw", "fx", "fy", "fz",
"gc", "gj", "gk", "go", "gv", "gx", "gz", "ha", "hb", "hc", "hd", "he", "hf", "hg",
"hh", "hi", "hj", "hl", "ho", "hp", "hq", "hs", "hv", "hw", "hx", "hy", "hz", "ia",
"ib", "ic", "if", "ig", "ih", "ii", "ij", "ik", "ip", "iu", "iv", "iw", "ix", "iy",
"iz", "ja", "jb", "jc", "jd", "jf", "jg", "jh", "ji", "jj", "jk", "jl", "jn", "jq",
"jr", "js", "jt", "ju", "jv", "jw", "jx", "jy", "jz", "ka", "kb", "kc", "kd", "kf",
"kj", "kk", "kl", "ko", "kq", "ks", "kt", "ku", "kv", "kx", "ld", "le", "lf", "lg",
"lh", "lj", "ll", "lm", "ln", "lo", "lp", "lq", "lw", "lx", "lz", "mb", "mi", "mj",
"nb", "nd", "nh", "nj", "nk", "nm", "nn", "nq", "ns", "nt", "nv", "nw", "nx", "ny",
"oa", "ob", "oc", "od", "oe", "of", "og", "oh", "oi", "oj", "ok", "ol", "on", "oo",
"op", "oq", "or", "os", "ot", "ou", "ov", "ow", "ox", "oy", "oz", "pb", "pc", "pd",
"pi", "pj", "po", "pp", "pq", "pu", "pv", "px", "pz", "qb", "qc", "qd", "qe", "qf",
"qg", "qh", "qi", "qj", "qk", "ql", "qm", "qn", "qo", "qp", "qq", "qr", "qs", "qt",
"qu", "qv", "qw", "qx", "qy", "qz", "ra", "rb", "rc", "rd", "rf", "rg", "rh", "ri",
"rj", "rk", "rl", "rm", "rn", "rp", "rq", "rr", "rt", "rv", "rx", "ry", "rz", "sf",
"sp", "sq", "su", "sw", "ta", "tb", "te", "ti", "tp", "tq", "ts", "tu", "tx", "ty",
"ub", "uc", "ud", "ue", "uf", "uh", "ui", "uj", "uk", "ul", "un", "uo", "up", "uq",
"ur", "ut", "uu", "uv", "uw", "ux", "vb", "vd", "vf", "vh", "vj", "vk", "vl", "vm",
"vo", "vp", "vq", "vr", "vs", "vt", "vv", "vw", "vx", "vy", "vz", "wa", "wb", "wc",
"wd", "we", "wg", "wh", "wi", "wj", "wk", "wl", "wm", "wn", "wo", "wp", "wq", "wr",
"wt", "wu", "wv", "ww", "wx", "wy", "wz", "xa", "xb", "xc", "xd", "xe", "xf", "xg",
"xh", "xi", "xj", "xk", "xl", "xm", "xn", "xo", "xp", "xq", "xr", "xs", "xt", "xu",
"xv", "xw", "xx", "xy", "xz", "ya", "yb", "yc", "yd", "yf", "yg", "yh", "yi", "yj",
"yk", "yl", "ym", "yn", "yo", "yp", "yq", "yr", "ys", "yu", "yv", "yw", "yx", "yy",
"yz", "zb", "zc", "zd", "ze", "zf", "zg", "zh", "zi", "zj", "zk", "zl", "zn", "zo",
"zp", "zq", "zr", "zs", "zt", "zu", "zv", "zx", "zy", "zz"};
person.setName("John Doe");
String email = person.getEmail();
for (String cTLD : invalidCTLDs) {
this.testInvalidEmail("john.doe@ntnu." + cTLD, email, IllegalArgumentException.class);
}
for (String cTLD : cTLDs) {
String localemail = "john.doe@" + PersonTest.generateValidDomain() + "." + cTLD;
assertDoesNotThrow(() -> {
person.setEmail(localemail);
});
assertEquals(localemail, person.getEmail());
}
}
@Test
public void testSetGender() {
String validGenders = "FM\0";
char gender = person.getGender();
for (char c = '\0'; c < '\uFFFF'; c++) {
char localc = c;
if (validGenders.indexOf(c) < 0) {
gender = person.getGender();
assertThrows(IllegalArgumentException.class, () -> {
person.setGender(localc);
});
assertEquals(gender, person.getGender());
} else {
assertDoesNotThrow(() -> {
person.setGender(localc);
});
assertEquals(localc, person.getGender());
}
}
}
}

View File

@@ -0,0 +1,97 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PersonTest2 {
private static final int[] factors1 = {3, 7, 6, 1, 8, 9, 4, 5, 2};
private static final int[] factors2 = {5, 4, 3, 2, 7, 6, 5, 4, 3, 2};
private Person person;
@BeforeEach
public void setUp() {
person = new Person();
}
private static String generateValid(int n1, int n2, int n3, String birthday) {
birthday = birthday + n1 + n2 + n3;
int k1 = 0, k2 = 0;
for (int i = 0; i < birthday.length(); i++) {
int num = Character.getNumericValue(birthday.charAt(i));
k1 += factors1[i] * num;
k2 += factors2[i] * num;
}
k1 = 11 - (k1 % 11);
if (k1 == 11) {
k1 = 0;
}
k2 += k1 * factors2[9];
k2 = 11 - (k2 % 11);
if (k2 == 11) {
k2 = 0;
}
return k1 + "" + k2;
}
@Test
public void testSetSSN() {
LocalDate localDate = LocalDate.of(1994, 1, 1);
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
person.setBirthday(date);
person.setGender('M');
assertThrows(IllegalArgumentException.class, () -> {
person.setSSN(null);
}, "SSN cannot be null");
assertDoesNotThrow(() -> {
person.setSSN("010194111" + PersonTest2.generateValid(1, 1, 1, "010194"));
});
assertEquals("01019411156", person.getSSN());
assertThrows(IllegalArgumentException.class, () -> {
person.setSSN("010194112" + PersonTest2.generateValid(1, 1, 2, "010194"));
});
assertEquals("01019411156", person.getSSN());
assertThrows(IllegalArgumentException.class, () -> {
person.setSSN("01019411122");
});
assertEquals("01019411156", person.getSSN());
}
@Test
public void testModifyingState() {
LocalDate localDate = LocalDate.of(1994, 1, 1);
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
person.setBirthday(date);
person.setGender('M');
person.setSSN("010194111" + PersonTest2.generateValid(1, 1, 1, "010194"));
assertThrows(IllegalStateException.class, () -> {
person.setGender('F');
});
assertThrows(IllegalStateException.class, () -> {
person.setBirthday(new Date());
});
}
}

View File

@@ -0,0 +1,179 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class StopWatchTest {
private StopWatch stopWatch;
@BeforeEach
public void beforeEach() {
stopWatch = new StopWatch();
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(StopWatch.class);
}
@Test
@DisplayName("Constructor")
public void testConstructor() {
assertFalse(stopWatch.isStarted(), "Stopwatch should not be started");
assertFalse(stopWatch.isStopped(), "Stopwatch should not be stopped");
assertEquals(0, stopWatch.getTicks(), "Wrong ticks returned");
assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started");
assertEquals(-1, stopWatch.getLapTime(), "Lap time should be -1 when not started");
assertEquals(-1, stopWatch.getLastLapTime(), "Last lap time should be -1 when not started");
}
@Test
@DisplayName("Tick without starting")
public void testTicksWithoutStart() {
stopWatch.tick(1);
assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started");
assertEquals(1, stopWatch.getTicks(), "Ticks should be 1 after calling #tick(1)");
stopWatch.tick(4);
assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started");
assertEquals(5, stopWatch.getTicks(), "Ticks should be 5 after calling #tick(4)");
}
@Test
@DisplayName("Tick, start and stop 1")
public void testStartTickStop() {
stopWatch.start();
assertEquals(0, stopWatch.getTime(), "Time should be 0 when just started");
assertEquals(0, stopWatch.getTicks(), "Ticks should be 0 when #tick() has not been called");
assertTrue(stopWatch.isStarted(), "Should be started after calling #start()");
assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()");
assertThrows(IllegalStateException.class, () -> {
stopWatch.start();
}, "Cannot start already running stopwatch");
stopWatch.tick(3);
assertEquals(3, stopWatch.getTime(),
"Time should be 3 when started and #tick(3) has been called");
assertEquals(3, stopWatch.getTicks(), "Ticks should be 3 when #tick(3) has been called");
assertTrue(stopWatch.isStarted(), "Should be started after calling #start()");
assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()");
stopWatch.tick(5);
assertEquals(8, stopWatch.getTime(),
"Time should be 8 when started and #tick(5) has been called");
assertEquals(8, stopWatch.getTicks(), "Ticks should be 8 when #tick(5) has been called");
assertTrue(stopWatch.isStarted(), "Should be started after calling #start()");
assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()");
stopWatch.stop();
assertEquals(8, stopWatch.getTime(), "Time should be 8 after #stop() has been called");
assertEquals(8, stopWatch.getTicks(), "Ticks should be 8 after #stop() has been called");
assertTrue(stopWatch.isStarted(), "Should be started even after #stop() has been called");
assertTrue(stopWatch.isStopped(), "Should be stopped after calling #stop()");
assertThrows(IllegalStateException.class, () -> {
stopWatch.stop();
}, "Cannot stop stopped stopwatch");
}
@Test
@DisplayName("Tick, start and stop 2")
public void testTickStartTickStopTick() {
stopWatch.tick(2);
assertEquals(-1, stopWatch.getTime(), "Time should be -1 when not started");
assertEquals(2, stopWatch.getTicks(), "Ticks should be 2 when #tick(2) has been called");
assertFalse(stopWatch.isStarted(), "Stopwatch should not be started");
assertFalse(stopWatch.isStopped(), "Stopwatch should not be stopped");
stopWatch.start();
assertEquals(0, stopWatch.getTime(), "Time should be 0 when just started");
assertEquals(2, stopWatch.getTicks(), "Ticks should be 2 after #start() has been called");
assertTrue(stopWatch.isStarted(), "Should be started after calling #start()");
assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #stop()");
stopWatch.tick(3);
assertEquals(3, stopWatch.getTime(),
"Time should be 3 when started and #tick(3) has been called");
assertEquals(5, stopWatch.getTicks(), "Ticks should be 5 when #tick(3) has been called");
assertTrue(stopWatch.isStarted(), "Should be started after calling #tick(3)");
assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #tick(3)");
stopWatch.tick(5);
assertEquals(8, stopWatch.getTime(),
"Time should be 8 when started and #tick(5) has been called");
assertEquals(10, stopWatch.getTicks(), "Ticks should be 10 when #tick(5) has been called");
assertTrue(stopWatch.isStarted(), "Should be started after calling #tick(5)");
assertFalse(stopWatch.isStopped(), "Should not be stopped before calling #tick(5)");
stopWatch.stop();
assertEquals(8, stopWatch.getTime(), "Time should be 8 after #stop() has been called");
assertEquals(10, stopWatch.getTicks(), "Ticks should be 10 after #stop() has been called");
assertTrue(stopWatch.isStarted(), "Should be started even after #stop() has been called");
assertTrue(stopWatch.isStopped(), "Should be stopped after calling #stop()");
stopWatch.tick(3);
assertEquals(8, stopWatch.getTime(),
"Time should be 8 after #tick(3) has been called while stopped");
assertEquals(13, stopWatch.getTicks(),
"Ticks should be 13 when #tick(3) has been called while stopped");
assertTrue(stopWatch.isStarted(),
"Should be started even after #tick() has been called while stopped");
assertTrue(stopWatch.isStopped(), "Should be stopped after calling #tick() while stopped");
assertThrows(IllegalArgumentException.class, () -> {
stopWatch.tick(-1);
}, "Time should not go backwards");
}
@Test
@DisplayName("Lap times")
public void testLaps() {
assertThrows(IllegalStateException.class, () -> {
stopWatch.lap();
}, "Should not be able to lap non-started timer");
stopWatch.start();
assertEquals(0, stopWatch.getTime(), "Time should be 0 when just started");
assertEquals(0, stopWatch.getLapTime(), "Lap time should be 0 when just started");
assertEquals(-1, stopWatch.getLastLapTime(),
"Last lap time should be -1 when there is no previous lap");
stopWatch.tick(3);
assertEquals(3, stopWatch.getTime(), "Time should be 3 after #tick(3) has been called");
assertEquals(3, stopWatch.getLapTime(),
"Lap time should be 3 after calling #tick(3) while started");
assertEquals(-1, stopWatch.getLastLapTime(),
"Last lap time should be -1 when there is no previous lap");
stopWatch.lap();
assertEquals(3, stopWatch.getTime(), "Time should still be 3 after starting a new lap");
assertEquals(0, stopWatch.getLapTime(), "Current lap time should be 0 when just started");
assertEquals(3, stopWatch.getLastLapTime(),
"Last lap time should be 3 when we just started a new lap");
stopWatch.tick(5);
assertEquals(8, stopWatch.getTime(), "Time should be 8 after #tick(5) has been called");
assertEquals(5, stopWatch.getLapTime(),
"Current lap time should be 5 after calling #tick(5)");
assertEquals(3, stopWatch.getLastLapTime(),
"Last lap time should be 3 even after time passes");
stopWatch.stop();
assertEquals(8, stopWatch.getTime(), "Time should be 8 after stopping");
assertEquals(0, stopWatch.getLapTime(), "Current lap time should be 0 when stopped");
assertEquals(5, stopWatch.getLastLapTime(),
"Last lap should be the lap time of the current lap when stopping");
assertThrows(IllegalStateException.class, () -> {
stopWatch.lap();
}, "Should not be able to lap stopped timer");
}
}

View File

@@ -0,0 +1,15 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class TestHelper {
public static void checkIfFieldsPrivate(Class<?> clazz) {
for (Field field : clazz.getDeclaredFields()) {
assertTrue(Modifier.isPrivate(field.getModifiers()),
"Expected field " + field.getName() + " to be private!");
}
}
}

View File

@@ -0,0 +1,70 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class UpOrDownCounterTest {
private static void testCount(UpOrDownCounter counter, int end, int delta) {
boolean result = true;
while (delta > 0 ? counter.getCounter() < end : counter.getCounter() > end) {
assertTrue(result,
"Before reaching the end value, the count() method should return true");
int i = counter.getCounter();
result = counter.count();
int expected = i + delta;
assertEquals(expected, counter.getCounter(),
"When counting from " + i + " the result should be " + expected + ". ");
}
assertFalse(result, "When reaching the end value, the count method should return false");
assertEquals(end, counter.getCounter(),
"After reaching the end value, the counter should not change");
assertFalse(counter.count(),
"After reaching the end value, the count() method should return false");
assertEquals(end, counter.getCounter(),
"After reaching the end value, the counter should not change");
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(UpOrDownCounter.class);
}
@Test
public void testUpOrDownCounter() {
UpOrDownCounter counter13 = new UpOrDownCounter(1, 3);
assertEquals(1, counter13.getCounter(),
"After instantiating an UpOrDownCounter the counter should be the start value");
UpOrDownCounter counter31 = new UpOrDownCounter(3, 1);
assertEquals(3, counter31.getCounter(),
"After instantiating an UpOrDownCounter the counter should be the start value");
}
@Test
public void testUpOrDownCounterWithException() {
assertThrows(IllegalArgumentException.class, () -> {
new UpOrDownCounter(0, 0);
}, "When the start and end values are equal an IllegalArgumentException should be thrown");
}
@Test
public void testCountUp() {
UpOrDownCounter counter13 = new UpOrDownCounter(1, 3);
UpOrDownCounterTest.testCount(counter13, 3, 1);
}
@Test
public void testCountDown() {
UpOrDownCounter counter31 = new UpOrDownCounter(3, 1);
UpOrDownCounterTest.testCount(counter31, 1, -1);
}
}

View File

@@ -0,0 +1,200 @@
package oving2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class VehicleTest {
private Vehicle vehicle;
private static void checkVehicleState(char vehicleType, char fuelType,
String registrationNumber, Vehicle vehicle) {
assertEquals(vehicleType, vehicle.getVehicleType());
assertEquals(fuelType, vehicle.getFuelType());
assertEquals(registrationNumber, vehicle.getRegistrationNumber());
}
private static void checkInvalidConstructor(char vehicleType, char fuelType,
String registrationNumber) {
assertThrows(IllegalArgumentException.class, () -> {
new Vehicle(vehicleType, fuelType, registrationNumber);
});
}
private static void checkInvalidsetRegistration(Vehicle vehicle, String originalRegNum,
String newRegNum) {
assertThrows(IllegalArgumentException.class, () -> {
vehicle.setRegistrationNumber(newRegNum);
});
assertEquals(originalRegNum, vehicle.getRegistrationNumber());
}
@Test
@DisplayName("Private fields")
public void testPrivateFields() {
TestHelper.checkIfFieldsPrivate(Vehicle.class);
}
@Test
public void testConstructor() {
vehicle = new Vehicle('C', 'D', "BN12345");
VehicleTest.checkVehicleState('C', 'D', "BN12345", vehicle);
vehicle = new Vehicle('M', 'E', "EL1234");
VehicleTest.checkVehicleState('M', 'E', "EL1234", vehicle);
VehicleTest.checkInvalidConstructor('C', 'D', null);
VehicleTest.checkInvalidConstructor('C', 'Y', "BN12345");
VehicleTest.checkInvalidConstructor('M', 'H', "HY1234");
VehicleTest.checkInvalidConstructor('P', 'D', "BN12345");
VehicleTest.checkInvalidConstructor('C', 'G', "A1234");
VehicleTest.checkInvalidConstructor('C', 'G', "A12345");
VehicleTest.checkInvalidConstructor('C', 'G', "A123456");
VehicleTest.checkInvalidConstructor('C', 'G', "A12344");
VehicleTest.checkInvalidConstructor('C', 'G', "AÆ12345");
VehicleTest.checkInvalidConstructor('C', 'G', "ab12345");
VehicleTest.checkInvalidConstructor('C', 'G', "A12345");
VehicleTest.checkInvalidConstructor('C', 'G', "A1B12345");
VehicleTest.checkInvalidConstructor('M', 'G', "A1234");
VehicleTest.checkInvalidConstructor('M', 'G', "A12345");
VehicleTest.checkInvalidConstructor('M', 'G', "A123");
VehicleTest.checkInvalidConstructor('M', 'G', "AB12345");
VehicleTest.checkInvalidConstructor('M', 'G', "ABC1234");
VehicleTest.checkInvalidConstructor('M', 'G', "ABC12345");
VehicleTest.checkInvalidConstructor('C', 'G', "AÅ1234");
VehicleTest.checkInvalidConstructor('C', 'G', "ab1234");
VehicleTest.checkInvalidConstructor('C', 'G', "EL12345");
VehicleTest.checkInvalidConstructor('C', 'G', "EK12345");
VehicleTest.checkInvalidConstructor('C', 'G', "HY12345");
VehicleTest.checkInvalidConstructor('C', 'D', "EL12345");
VehicleTest.checkInvalidConstructor('C', 'D', "EK12345");
VehicleTest.checkInvalidConstructor('C', 'D', "HY12345");
VehicleTest.checkInvalidConstructor('C', 'H', "EL12345");
VehicleTest.checkInvalidConstructor('C', 'H', "EK12345");
VehicleTest.checkInvalidConstructor('C', 'H', "BN12345");
VehicleTest.checkInvalidConstructor('C', 'E', "HY12345");
VehicleTest.checkInvalidConstructor('C', 'E', "BN12345");
VehicleTest.checkInvalidConstructor('M', 'G', "EL1234");
VehicleTest.checkInvalidConstructor('M', 'G', "EK1234");
VehicleTest.checkInvalidConstructor('M', 'G', "HY1234");
VehicleTest.checkInvalidConstructor('M', 'D', "EL1234");
VehicleTest.checkInvalidConstructor('M', 'D', "EK1234");
VehicleTest.checkInvalidConstructor('M', 'D', "HY1234");
VehicleTest.checkInvalidConstructor('M', 'E', "HY1234");
VehicleTest.checkInvalidConstructor('M', 'E', "BN1234");
}
@Test
public void testSetRegistrationNumber() {
vehicle = new Vehicle('C', 'D', "BN12345");
vehicle.setRegistrationNumber("AB54321");
VehicleTest.checkVehicleState('C', 'D', "AB54321", vehicle);
vehicle = new Vehicle('M', 'E', "EK1234");
vehicle.setRegistrationNumber("EL4321");
VehicleTest.checkVehicleState('M', 'E', "EL4321", vehicle);
vehicle = new Vehicle('C', 'D', "BN12345");
assertThrows(IllegalArgumentException.class, () -> {
vehicle.setRegistrationNumber("AB654321");
});
VehicleTest.checkVehicleState('C', 'D', "BN12345", vehicle);
vehicle = new Vehicle('M', 'E', "EL1234");
assertThrows(IllegalArgumentException.class, () -> {
vehicle.setRegistrationNumber("HY1234");
});
VehicleTest.checkVehicleState('M', 'E', "EL1234", vehicle);
vehicle = new Vehicle('C', 'G', "AB12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"AB123456");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"ABC1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"AÆ12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"ab12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"A1B2345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"AB123456");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"ABC1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"AÆ12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"ab12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"A1B2345");
vehicle = new Vehicle('M', 'G', "AB1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB123");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"AB12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"ABC1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"ABC12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AÅ1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ab1234");
vehicle = new Vehicle('C', 'G', "AB12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"EL12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"EK12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"HY12345");
vehicle = new Vehicle('C', 'D', "AB12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"EL12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"EK12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"HY12345");
vehicle = new Vehicle('C', 'H', "HY12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"EL12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"EK12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"BN12345");
vehicle = new Vehicle('C', 'E', "EL12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"HY12345");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(),
"BN12345");
vehicle = new Vehicle('M', 'G', "AB1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY1234");
vehicle = new Vehicle('M', 'D', "AB1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY1234");
vehicle = new Vehicle('M', 'E', "EK1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY1234");
VehicleTest.checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "BN1234");
}
}