Merge pull request #8 from tdt4100/add-oving7

Add oving 7
This commit is contained in:
Andreas Omholt Olsen
2026-03-06 11:01:52 +01:00
committed by GitHub Enterprise
44 changed files with 1947 additions and 0 deletions

View File

@@ -11,3 +11,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
| [Øving 4](./oppgavetekster/oving4/README.md) | Objektstrukturer |
| [Øving 5](./oppgavetekster/oving5/README.md) | Grensesnitt |
| [Øving 6](./oppgavetekster/oving6/README.md) | Observatør-observert og delegering |
| [Øving 7](./oppgavetekster/oving7/README.md) | Arv og abstrakte klasser |

View File

@@ -0,0 +1,41 @@
# Arv - AbstractAccount-oppgave
Denne oppgaven handler om å lage en felles abstrakt superklasse `AbstractAccount` for `CreditAccount`, `DebitAccount`- og `SavingsAccount2`-klassene.
Denne oppgaven er en annen variant av [SavingsAccount](./SavingsAccount.md)-oppgaven, med fokus på bruk av abstrakte klasser og arv.
Denne oppgaven er på likt format som dere møtte i [Øving 3 - Card-oppgaven](../oving3/Card.md), der det ikke blir oppgitt en detaljert beskrivelse av klassene her i `README`-filen, men heller gjennom Javadoc-dokumentasjonen. Dette er igjen for å gi dere øving i å lese og forstå dokumentasjon, som er et vanlig format å bli gitt oppgaver på eksamen.
> Kjapt tips: Hold musen over metoden/klassen for å lese Javadoc-dokumentasjonen på et fint format.
Filene i denne oppgaven skal legges i [oving7/abstractaccount](../../src/main/java/oving7/abstractaccount).
## Del 1 - Abstrakt klasse AbstractAccount
En bank består av mange ulike type kontoer: sparekontoer, brukskontoer, depositumskontoer, støttekontoer, osv. Siden disse har mye felles, f.eks. har alle en balanse, så er det praktisk å samle så mye som mulig av den felles logikken i en superklasse, som alle kan arve fra. Denne superklassen er imidlertid ikke noen egen type konto, og derfor gjør vi den abstrakt, slik at den ikke kan instansieres. De konkrete konto-klassene som arver fra den, må selvsagt være instansierbare.
Vær oppmerksom på at du fra og med [del 2](#del-2---debitaccount-extends-abstractaccount) skal lage subklasser av `AbstractAccount` og at du ved å bruke rett innkapsling (hint: `protected`-modifikatoren) skal la *subklassene* nyttiggjøre seg *superklassen* i størst mulig grad.
Skjelletet til `AbstractAccount`-klassen finner du i [oving7/abstractaccount/AbstractAccount.java](../../src/main/java/oving7/abstractaccount/AbstractAccount.java).
Det er ingen spesifikke tester for denne klassen, siden abstrakte klasser ikke kan instansieres.
## Del 2 - DebitAccount extends AbstractAccount
Skjelettet til `DebitAccount`-klassen finner du i [oving7/abstractaccount/DebitAccount.java](../../src/main/java/oving7/abstractaccount/DebitAccount.java).
Testkode for oppgavene finner du her: [oving7/abstractaccount/DebitAccountTest.java](../../src/test/java/oving7/abstractaccount/DebitAccountTest.java).
## Del 3 - CreditAccount extends AbstractAccount
Skjelettet til `CreditAccount`-klassen finner du i [oving7/abstractaccount/CreditAccount.java](../../src/main/java/oving7/abstractaccount/CreditAccount.java).
Testkode for oppgavene finner du her: [oving7/abstractaccount/CreditAccountTest.java](../../src/test/java/oving7/abstractaccount/CreditAccountTest.java).
## Del 4 - SavingsAccount extends AbstractAccount
> Merk at denne klassen har samme navn som den fra [SavingsAccount-oppgaven](./SavingsAccount.md), men at dette er uproblematisk siden de befinner seg i forskjellige mapper.
Skjelettet til `SavingsAccount`-klassen finner du i [oving7/abstractaccount/SavingsAccount.java](../../src/main/java/oving7/abstractaccount/SavingsAccount.java).
Testkode for oppgavene finner du her: [oving7/abstractaccount/SavingsAccountTest.java](../../src/test/java/oving7/abstractaccount/SavingsAccountTest.java).

View File

@@ -0,0 +1,21 @@
# Arv - CardContainerImpl-oppgave
Denne oppgaven handler om å lage en felles superklasse `CardContainerImpl` for `CardDeck`- og `CardHand`-klassene, laget i [Card-oppgaven](../oving4/Card.md) og [CardContainer-oppgaven](../oving5/CardContainer.md). Dersom du ikke har gjort `Card`-oppgavene allerede, bør du gjøre disse først. Hvis du ikke har gjort det, kan du kopiere koden fra [løsningsforslaget](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving5/card), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 5.
Filene i denne oppgaven skal legges i [oving7/card](../../src/main/java/oving7/card).
## Del 1 - Superklassen CardContainerImpl
Lag en `CardContainerImpl`-superklasse, som implementerer grensesnittet `CardContainer` (se [CardContainer-oppgaven](../oving5/CardContainer.md)) og inneholder koden som er felles for `CardDeck` og `CardHand`.
La `CardDeck` og `CardHand` arve `CardContainerImpl` og gjør nødvendige endringer i disse klassene, slik at totaloppførselen er som før. F.eks. skal `CardDeck`-objektet ha samme konstruktør som før, som skal sikre samme initielle tilstand (men ikke nødvendigvis med samme konstruktør-kode).
Merk at målet er at mest mulig kode skal flyttes til _superklassen_ og gjenbrukes i _subklassene_. Det er viktig å bruke innkapsling rett (hint: `protected`-modifikatoren) for å nyttiggjøre seg superklassen i størst mulig grad.
## Del 2 - Regler for maksimalt antall kort
Anta at en ønsker å unngå at instanser av `CardContainerImpl` (eller av en av subklassene) inneholder for mange kort. Legg til et _privat_ `maxCardCount`-felt i `CardContainerImpl`, en konstruktør som _initialiserer_ feltet og en _getter_ for å lese verdien. Legg så til evt. endre kode i `CardContainerImpl` som sikrer at antall kort ikke overstiger dette tallet og at subklassene ikke kan omgå denne valideringen.
`CardContainerImpl`-subklassene `CardDeck` og `CardHand` skal sette maks-antallet som følger: `CardDeck` skal sette makstallet til _52_ og `CardHand` skal ta inn maks-antallet i _sin_ konstruktør. Hvis man forsøker å legge til flere kort enn hva som er tillatt i `CardHand`, skal det utløses en `IllegalStateException`.
Testkode for oppgavene finner du her: [oving7/card/CardDeckTest.java](../../src/test/java/oving7/card/CardDeckTest.java) og [oving7/card/CardHandTest.java](../../src/test/java/oving7/card/CardHandTest.java).

View File

@@ -0,0 +1,65 @@
# Observatør-observert-teknikken og arv - HighscoreList-oppgave med ObservableList
Denne oppgaven handler om å bruke observatør-observert-teknikken for å bli informert om endringer i en highscore-liste. Vi bruker også arv for å skille ut gjenbrukbar kode for en generell, observerbar liste.
Observatør-observert-teknikken går ut på at det observerte objektet sier ifra til en eller flere observatører om at tilstanden er endret. I denne oppgaven skal vi lage en `HighscoreList` som kan si fra til lyttere av typen `ListListener` når nye resultater blir registrert. En hovedprogramklasse kalt `HighscoreProgram` vil bli brukt til å sjekke at det virker. Denne klassen oppretter en `HighscoreList`-instans, legger inn resultater (tall) fra konsollet som legges til listen og skriver ut listen hver gang et nytt resultat faktisk blir lagt til.
Klassene skal legges i [`src/main/java/oving7/observablelist`](../../src/main/java/oving7/observablelist) og tilhørende tester ligger i [`src/test/java/oving7/observablelist`](../../src/test/java/oving7/observablelist).
## Del 1: Implementasjon av ObservableList og ObservableHighscoreList
En `ObservableHighscoreList` skal holde styr på heltallsresultater (av typen `int`/`Integer`). Listen skal være _observerbar_ ved at den kan registrere lyttere (`ObservableListListener`-instanser) og si fra til dem når listen blir endret. Listen skal ha en maksimal lengde, som settes i _konstruktøren_, f.eks. skal en topp $10$-liste kunne opprettes med `new ObservableHighscoreList(10)`. Nye resultater registreres med metoden `addResult(int)`, som skal finne riktig posisjon og legge resultatet inn (dersom det er godt nok). Dersom listen er for lang, så skal det dårligste resultatet fjernes. NB: _Lavest verdi er best_, f.eks. antall sekunder på en oppgave eller antall flytt i Sokoban.
`ObservableListListener`-grensesnittet er vist i klassediagrammet nedenfor og må implementers av alle klasser som ønsker å fungere som lyttere for `ObservableHighscoreList`-instanser. Lyttere registrerer seg med `ObservableHighscoreList` sin `addObservableListListener`-metode og vil siden få beskjed om nye resultater ved at `listChanged`-metoden kalles. Argumentene som tas inn er `ObservableHighscoreList`-objektet som ble endret og _posisjonen_ i listen der endringen skjedde.
Merk at første argument til `listChanged`-metoden er av typen `ObservableList`. Dette er en abstrakt superklasse for `ObservableHighscoreList`, som først brukes i [del 3](#del-3-observablelist) og som da skal holde orden på listen. `ObservableList` vil ha en del generelle metoder som `ObservableHighscoreList` arver og kan bruke. For å kunne kjøre testene for `ObservableHighscoreList` allerede her i [del 1](#del-1-implementasjon-av-observablelist-og-observablehighscorelist), så må `ObservableList` være definert fra starten. Lag derfor en tom `ObservableList`-klasse og bruk denne som superklasse for `ObservableHighscoreList`.
Her er en oversikt over metoden som må implementeres:
- `ObservableHighscoreList(int maxSize)` - konstruktøren tar inn _maks antall_ resultater som listen skal kunne holde. Denne verdien må brukes av `addResult`, slik at resultater som er for dårlige kastes.
- `int size()` - returnerer antall elementer i listen, som altså aldri skal overstige maks-antallet.
- `int getElement(int)` - returnerer resultatet i posisjonen angitt av argumentet.
- `void addResult(int)` - registrere et nytt resultat, og dersom resultatet er godt nok til å komme med på listen, så legges det inn på riktig plass. Dersom listen blir for lang, så må dårligste resultat kastes. Alle registrerte lyttere må få beskjed om en evt. endring av listen, inkludert hvilken posisjon som ble endret.
- `void addObservableListListener(ObservableListListener)` - registrerer en ny lytter.
- `void removeObservableListListener(ObservableListListener)` - fjerner en tidligere registrert lytter.
Klassediagram for `HighscoreList`, `ListListener` og `ObservableList`:
![ObservableList_del1](./img/observablelist_del1.png)
Testkode for denne oppgaven finner du her: [oving7/observablelist/ObservableHighscoreListTest.java](../../src/test/java/oving7/observablelist/ObservableHighscoreListTest.java).
## Del 2: Hovedprogram ObservableHighscoreListProgram
Lag en hovedprogramklasse kalt `ObservableHighscoreListProgram`, som tester at `ObservableHighscoreList`-klassen din virker som den skal. La den opprette en `ObservableHighscoreList`-instans, lese inn tall fra konsollet (f.eks. med en `Scanner` og `nextInt`-metoden) og legge disse inn i listen. Sørg for at `ObservableHighscoreListProgram` implementerer `ObservableListListener`-grensesnittet og registrerer seg som lytter på `HighscoreList`-instansen. La lyttermetoden `listChanged` skrive ut informasjon og resultatene i `HighscoreList`-instansen og posisjonsargumentet, slik at du ser at alt virker som det skal.
Vi foreslår følgende metoder og oppførsel:
- `void init()` - oppretter en ny `ObservableHighscoreList` og registrerer seg selv (altså `ObservableHighscoreListProgram`-instansen) som lytter.
- `void run()` - leser inn tall (resultater) fra konsollet og legger dem til i listen.
- `void listChanged(ObservableList, int)` - observerer endringer i `ObservableHighscoreList`-instansen og skriver ut posisjonsargumentet, samt selve listen, til konsollet.
Klassediagrammet viser hvordan klassene henger sammen, og vårt forslag til metoder:
![ObservableList_del2](./img/observablelist_del2.png)
## Del 3: ObservableList
Den abstrakte superklassen `ObservableList` skal legges til som en generell superklasse for observerbare lister, som `ObservableHighscoreList` skal arve fra. Denne klassen skal både holde en liste med objekter (`Object`) og håndtere registrering av lyttere, altså en liste med `ObservableListListener`-instanse, som får beskjed om endringer i listen (slik at listen dermed er _observerbar_). Dette betyr at `ObservableList` overtar håndtering av både resultater og lyttere fra `ObservableHighscoreList`-klassen. For å gjøre `ObservableList` mer generell og gjenbrukbar, så lar vi den håndtere `Object`-instanser (heller enn `Integer`). Samtidig deklarerer den en _abstrakt_ metode `acceptsElement`, som subklasser må _redefinere_ for å bestemme hva slags objekter det skal være lov å legge inn. `ObservableHighscoreList` vil f.eks. måtte redefinere den slik at bare `Integer`-objekter aksepteres.
`ObservableList` skal ha følgende metoder (noen er altså overtatt fra `ObservableHighscoreList`):
- `int size()` - returnerer antall elementer i listen.
- `Object getElement(int)` - returnerer elementet i posisjonen angitt av argumentet.
- `abstract boolean acceptsElement(Object)` - returnerer hvorvidt _subklassen_ aksepterer at objektet legges inn i listen (f.eks. aksepterer `HighscoreList` kun `Integer`-objekter).
- `void addElement(int, Object)` - legger til et element på posisjonen angitt av argumentet, men bare dersom det _aksepteres_ som element. Dersom elementet ikke aksepteres, så skal `IllegalArgumentException` utløses. Dersom posisjonen er ulovlig så skal `IndexOutOfBoundsException` utløses.
- `void addElement(Object)` - legger til et element bakerst i listen, men bare dersom det _aksepteres_ som element. Dersom elementet ikke aksepteres, så skal `IllegalArgumentException` utløses.
- `void removeElement(int)` - fjerner elementet på posisjonen angitt av argumentet. Dersom posisjonen er ulovlig så skal `IndexOutOfBoundsException` utløses.
`ObservableHighscoreList` skal endres slik at den i størst mulig grad bruker metodene som arves fra `ObservableList`, men forøvrig ikke endrer oppførsel. Kjør hovedprogramklassen `ObservableHighscoreListProgram` for å sjekke at dette faktisk stemmer.
Klassediagrammet viser hvordan klassene henger sammen, og hvor metodene nå er deklarert/implementert. Merk at `addElement`- og `removeElement`-metodene er angitt som `protected` (ruter-symbolet), slik at kun subklasser skal kunne bruke dem.
![ObservableList_del3](./img/observablelist_del3.png)
Testkode for denne oppgaven finner du her: [oving7/observablelist/ObservableListTest.java](../../src/test/java/oving7/observablelist/ObservableListTest.java).

View File

@@ -0,0 +1,46 @@
# Øving 07: Arv og abstrakte klasser
## Øvingsmål
- Lære hvordan arv-mekansimen brukes i OO
- Lære om instanser, typer, deklarasjoner og tilordninger
- Lære om sub- og superklasser samt om synlighetsmodifikatorer som brukes ved arv
- Lære om abstrakte klasser, deres bruksområder og fordeler
## Øvingskrav
- Kunne bruke arv til å modellerere enkle(re) objektstrukturer- og relasjoner i Java
- Kunne la flere subklasser bruke funksjonalitet definert i samme superklasse
- Kunne la en subklasse redefinere metoder definert i en superklasse
- Kunne samle felles oppførsel til to eller flere subklasser i en felles abstrakt klasse
## Dette må du gjøre
Oppgavene skal lagres i [`src/main/java/oving7`](../../src/main/java/oving7).
I begge delene er antageligvis vanskelighetsgraden stigende. Alle er høyst eksamensrelevante og det anbefales følgelig å ta en titt på samtlige.
For å få 2 poeng på øvingen må det gjennomføres til sammen *4* valgfrie oppgaver fra [del 1](#del-1-arv) og [del 2](#del-2-abstrakte-klasser-og-arv).
### Del 1: Arv
Velg og gjennomfør *minst én* av oppgavene om arv:
- [CardContainerImpl](./CardContainerImpl.md) (Lett) (Anbefalt)
- [Train](./Train.md) (Lett)
- [SavingsAccount](./SavingsAccount.md) (Middels)
### Del 2: Abstrakte klasser og arv
Velg og gjennomfør *minst én* av oppgavene om abstrakte klasser og arv:
- [AbstractAccount](./AbstractAccount.md) (Middels) (Anbefalt)
- [ObservableList](./ObservableList.md) (Vanskelig)
## 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 en læringsassistent 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,63 @@
# Arv - SavingsAccount-oppgave
Denne oppgaven handler om å lage en felles superklasse `SavingsAccount` for `BSU`- og `ForeldreSpar`-klassene. `SavingsAccount` skal dessuten implementere `Account`-grensesnittet.
Denne oppgaven bygger videre på `Account`-oppgaven fra [øving 2](../oving2/Account.md).
Filene i denne oppgaven skal legges i [oving7/savingsaccount](../../src/main/java/oving7/savingsaccount).
## Del 1 - SavingsAccount implements Account
En bank består av mange ulike type kontoer: sparekontoer, brukskontoer, depositumskontoer, støttekontoer, osv. Felles for alle kontoer er `Account`-grensesnittet, som er definert under:
```java
package oving7.savingsaccount;
public interface Account {
void deposit(double amount);
void withdraw(double amount);
double getBalance();
}
```
Vi skal i denne oppgaven fokusere på sparekontoer og du skal nå lage en `SavingsAccount`-superklasse, som implementerer `Account`-grensesnittet. Funksjonaliteten som hver av metodene definert i grensesnittet over skal støtte er:
- `void deposit(double)` - øker kontobalansen med innskutt beløp. Merk at det innskutte beløpet må være positivt. Ved ulovlig innskudd skal en `IllegalArgumentException` utløses.
- `void withdraw(double)` - minsker kontobalansen med beløpet som blir tatt ut. Merk at uttaksbeløpet må være positivt, ellers skal et unntak av typen `IllegalArgumentException` utløses. Dersom det ikke er dekning på kontoen (en `SavingsAccount` kan ikke ha negativ balanse) skal et unntak av typen `IllegalStateException` utløses.
- `double getbalance()` - returnerer kontobalansen.
I tillegg til å støtte `Account`-grensesnittet over, som er felles for alle kontoer, skal sparekontoer ha en rentefot og en metode som forrenter kontoen. Denne kalles av bankene for hver sparekonto på slutten av året slik at alle dets kunder opptjener renter (ikke tenk på at banker egentlig holder styr på hvor stor balansen har vært gjennom hele året eller forrenter kontoen kontinuerlig - her skal vi bare anta at innestående kontobalanse ved årsslutt forrentes i sin helhet) - derfor heter metoden `endYearUpdate()`. I tillegg skal `SavingsAccount`-klassen ha en konstruktør som tvinger alle objekter av denne typen til å bli instansiert med en rentefot. Dette er oppsummert her:
- `SavingsAccount(double)` - konstruktør som tar inn rentefoten på kontoen (et desimaltall, f.eks. `0.05` tilsvarer en rente på $5%$). Åpningsbalansen skal være `0.0`.
- `void endYearUpdate()` - forrenter kontobalansen basert på rentefoten. Vi tenker oss at denne kalles av kode utenfor denne klassen, f.eks. resten av et tenkt banksystem ved årsoppgjør, som et signal på at nå er et nytt år over.
Vær oppmerksom på at du i [del 2](#del-2---bsu-extends-savingsaccount) og [del 3](#del-3---foreldrespar-extends-savingsaccount) skal lage _subklasser_ av `SavingsAccount` og at du ved å bruke rett innkapsling (hint: `protected`-modifikatoren) kan la _subklassene_ nyttiggjøre seg _superklassen_ i størst mulig grad.
> Merk at denne klassen har samme navn som den fra [AbstractAccount-oppgaven](./AbstractAccount.md), men at dette er uproblematisk siden de befinner seg i forskjellige mapper.
Testkode for oppgavene finner du her: [oving7/savingsaccount/SavingsAccountTest.java](../../src/test/java/oving7/savingsaccount/SavingsAccountTest.java).
## Del 2 - BSU extends SavingsAccount
I tillegg til generelle sparekontoer finnes det en spesiell type sparekonto som heter BSU. Du skal nå lage en `BSU`-klasse som arver fra `SavingsAccount`-superklassen. Her er målet at du skal gjenbruke mest mulig av _superklassen_ og samtidig støtte BSU-spesifikk oppførsel. En BSU-konto er, i tillegg til å være en sparekonto, spesiell i den forstand at det kun er lovlig å sette inn inntil et forhåndsbestemt beløp per år (den gamle regjeringen fastslo at BSU-kontoer i 2014 skulle ha en innskuddsgrense på kr $25 000$, men din kode skal ha støtte for å ha en vilkårlig grense) og at det kun er lovlig å ta ut av det beløpet som er satt inn siste år. Med andre ord vil en ved årsskifte få mulighet til å sette inn nye innskudd innenfor innskuddsgrensen, men en har ikke lenger mulighet til å ta ut hele balansen (innskudd fra tidligere år låses). Dessuten gir en vanlig BSU-konto $20%$ skattefradrag for årets innskudd.
Du må selv avgjøre hvilke felt som må legges til for å støtte den beskrevne oppførsel. I tillegg stilles følgende krav til klassen:
- `BSU(double, double)` - konstruktør som tar inn rentefoten på kontoen og et desimaltall som angir hvor mye det er tillatt å sette inn på kontoen per år.
- `double getTaxDeduction()` - returnerer skattefradrag for inneværende år. Dette vil være $20%$ av innskutt(e) beløp siste år.
Testkode for oppgavene finner du her: [oving7/savingsaccount/BSUTest.java](../../src/test/java/oving7/savingsaccount/BSUTest.java).
## Del 3 - ForeldreSpar extends SavingsAccount
En annen spesiell type sparekonto, her kalt ForeldreSpar, har et begrenset antall lovlige uttak per år (ofte i bytte mot en høyere rente). Du skal nå lage en slik `ForeldreSpar`-klasse som arver fra `SavingsAccount`-superklassen. Her er igjen målet at du skal gjenbruke mest mulig av _superklassen_ og samtidig støtte ForeldreSpar-spesifikk oppførsel. Denne klassen skal sikre at kun det lovlige antallet uttak gjøres i løpet av et år.
Du må selv avgjøre hvilke felt som må legges til før å støtte den beskrevne oppførsel. I tillegg stilles følgende krav til klassen:
- `ForeldreSpar(double, int)` - konstruktør som tar inn rentefoten på kontoen og et heltall som angir antall lovlige uttak per år.
- `int getRemainingWithdrawals()` - returnerer antall gjenstående uttak fra sparekontoen.
Testkode for oppgavene finner du her: [oving7/savingsaccount/ForeldreSparTest.java](../../src/test/java/oving7/savingsaccount/ForeldreSparTest.java).

View File

@@ -0,0 +1,51 @@
# Arv - Train-oppgave
I denne oppgaven skal vi modellere to typer togvogner og bruke dem i et tog. Vi vil bruke arv og samle det som er felles for togvognene i en _superklasse_.
Denne oppgaven er på likt format som dere møtte i [Øving 3 - Card-oppgaven](../oving3/Card.md), der det ikke blir oppgitt en detaljert beskrivelse av klassene her i `README`-filen, men heller gjennom Javadoc-dokumentasjonen. Dette er igjen for å gi dere øving i å lese og forstå dokumentasjon, som er et vanlig format å bli gitt oppgaver på eksamen.
> Kjapt tips: Hold musen over metoden/klassen for å lese Javadoc-dokumentasjonen på et fint format.
Filene i denne oppgaven skal legges i [oving7/train](../../src/main/java/oving7/train).
## Del 1 - TrainCar
![Train_del1](./img/train_del1.png)
I denne delen skal du lage en klasse kalt `TrainCar` for en enkel og generell togvogn, med følgende funksjonalitet som beskrevet i Javadoc-dokumentasjonen i klassen. Se også diagrammet over.
Skjellet for klassen finner du i [oving7/train/TrainCar.java](../../src/main/java/oving7/train/TrainCar.java).
Testkode for oppgaven finner du her: [oving7/train/TrainCarTest.java](../../src/test/java/oving7/train/TrainCarTest.java).
## Del 2 - CargoCar og PassengerCar
![Train_del2](./img/train_del2.png)
I denne delen skal du lage to forskjellige typer togvogner som er spesialiserte for sitt bruk. Begge arver fra `TrainCar`.
### CargoCar extends TrainCar
Denne klassen skal gjenspeile en lastevogn som frakter diverse ting og tang. Funksjonalitet skal være som beskrevet i dokumentasjonen. Se også diagrammet over.
Skjellet for klassen finner du i [oving7/train/CargoCar.java](../../src/main/java/oving7/train/CargoCar.java).
Testkode for oppgaven finner du her: [oving7/train/CargoCarTest.java](../../src/test/java/oving7/train/CargoCarTest.java).
### PassengerCar extends TrainCar
Denne klassen gjenspeiler en passasjervogn. Metodene skal være som beskrevet i dokumentasjonen. Se også diagrammet over.
Legg merke til at for å beregne totalvekta, så kan du anta at en gjennomsnittspassasjer veier 80 kg.
Skjellet for klassen finner du i [oving7/train/PassengerCar.java](../../src/main/java/oving7/train/PassengerCar.java).
Testkode for oppgaven finner du her: [oving7/train/PassengerCarTest.java](../../src/test/java/oving7/train/PassengerCarTest.java).
## Del 3 - Train
![Train_del3](./img/train_del3.png)
Klassen `Train` skal forestille et tog bestående av et sett vogner. Klassen skal ha metoder som beskrevet i dokumentasjonen. Se også diagrammet over.
Testkode for oppgaven finner du her: [oving7/train/TrainTest.java](../../src/test/java/oving7/train/TrainTest.java).

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -0,0 +1,26 @@
@startuml observablelist_del1
skinparam dpi 400
class ObservableList
class ObservableHighscoreList {
- int maxSize
- List<Integer> results
+ ObservableHighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
}
interface ObservableListListener {
void listChanged(ObservableList)
}
ObservableHighscoreList --u|> ObservableList
ObservableHighscoreList --> "observableListListeners: *" ObservableListListener
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

View File

@@ -0,0 +1,34 @@
@startuml observablelist_del2
skinparam dpi 400
class ObservableList
class ObservableHighscoreList {
- int maxSize
- List<Integer> results
+ ObservableHighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
}
interface ObservableListListener {
void listChanged(ObservableList)
}
class ObservableHighscoreListProgram {
- ObservableHighscoreList observableHighscoreList
+ void init()
+ void run()
}
ObservableHighscoreList --l|> ObservableList : "\t\t"
ObservableHighscoreList --> "observableListListeners: *" ObservableListListener
ObservableHighscoreListProgram ..r|> ObservableListListener : "\t\t"
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

View File

@@ -0,0 +1,44 @@
@startuml observablelist_del3
skinparam dpi 400
abstract class ObservableList {
- List<Object> elements
+ int size()
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
+ Object getElement(int)
# {abstract} boolean acceptsElement(Object)
# void addElement(Object)
# void removeElement(Object)
}
class ObservableHighscoreList {
- int maxSize
- List<Integer> results
+ ObservableHighscoreList(int)
+ int size()
+ int getElement(int)
+ void addResult(int)
+ void addObservableListListener(ObservableListListener)
+ void removeObservableListListener(ObservableListListener)
}
interface ObservableListListener {
void listChanged(ObservableList)
}
class ObservableHighscoreListProgram {
- ObservableHighscoreList observableHighscoreList
+ void init()
+ void run()
}
ObservableHighscoreList --l|> ObservableList : "\t\t"
ObservableHighscoreList --> "observableListListeners: *" ObservableListListener
ObservableHighscoreListProgram ..r|> ObservableListListener : "\t\t"
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -0,0 +1,14 @@
@startuml train_del1
skinparam dpi 400
class TrainCar {
- int deadWeight
+ int getTotalWeight()
+ void setDeadWeight(int)
+ int getDeadWeight()
+ String toString()
}
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -0,0 +1,33 @@
@startuml train_del2
skinparam dpi 400
class TrainCar {
- int deadWeight
+ int getTotalWeight()
+ void setDeadWeight(int)
+ int getDeadWeight()
+ String toString()
}
class CargoCar {
- int cargoWeight
+ CargoCar(int, int)
+ void setCargoWeight(int)
+ int getCargoWeight()
}
class PassengerCar {
- int passengerCount
+ PassengerCar(int, int)
+ void setPassengerCount(int)
+ int getPassengerCount()
}
CargoCar --u|> TrainCar
PassengerCar --u|> TrainCar
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View File

@@ -0,0 +1,44 @@
@startuml train_del3
skinparam dpi 400
class TrainCar {
- int deadWeight
+ int getTotalWeight()
+ void setDeadWeight(int)
+ int getDeadWeight()
+ String toString()
}
class CargoCar {
- int cargoWeight
+ CargoCar(int, int)
+ void setCargoWeight(int)
+ int getCargoWeight()
+ String toString()
}
class PassengerCar {
- int passengerCount
+ PassengerCar(int, int)
+ void setPassengerCount(int)
+ int getPassengerCount()
}
class Train {
+ void addTrainCar(TrainCar)
+ boolean contains(TrainCar)
+ int getTotalWeight()
+ int getPassengerCount()
+ int getCargoWeight()
+ String toString()
}
CargoCar --u|> TrainCar
PassengerCar --u|> TrainCar
Train --> "trainCars: *" TrainCar
@enduml

View File

@@ -0,0 +1,57 @@
package oving7.abstractaccount;
/**
* A bank consists of many different types of accounts: credit accounts, debit accounts, savings
* accounts, etc. Since these have a lot in common, e.g. all have a balance, it is practical to
* collect as much of the common logic as possible in a superclass, which all can inherit from.
* However, this superclass is not a type of account in itself, and therefore we make it
* {@code abstract}, so that it cannot be instantiated. The concrete account classes that inherit
* from it, must of course be instantiable. The methods defined in the {@code AbstractAccount} class
* is similar to that of the Account interface in the SavingsAccount task.
*/
public abstract class AbstractAccountDocs {
// AbstractAccount has a state {@code balance} for the account balance. The balance should
// either be set to 0.0 by default or in the constructor
// TODO: Add fields and potentially a constructor here
/**
* Decreases the account balance by the specified amount. Note that the rules for withdrawals
* are different for the classes that implement {@code AbstractAccount}, and must therefore be
* implemented in each class.
*
* @param amount the amount to withdraw
* @throws IllegalArgumentException if the amount cannot be withdrawn
*/
// TODO: Define abstract method {@code void internalWithdraw} here
/**
* Increases the account balance by the specified amount.
*
* @param amount the amount to deposit
* @throws IllegalArgumentException if the amount is not positive
*/
public void deposit(double amount) {
// TODO: Implement this method
}
/**
* This method calls the {@link #internalWithdraw()} method, which is implemented in each
* subclass.
*
* @param amount the amount to withdraw
* @throws IllegalArgumentException if the amount is not positive
*/
public void withdraw(double amount) {
// TODO: Implement this method
}
/**
* @return the current balance of the account
*/
public double getBalance() {
// TODO: Implement this method
return 0.0;
}
}

View File

@@ -0,0 +1,50 @@
package oving7.abstractaccount;
/**
* A {@code CreditAccount} has in addition to {@code balance} a state for {@code creditLine}, i.e.
* available credit on the account. This credit line allows the account to be overdrawn (that the
* balance is negative) within the credit line. If {@link #internalWithdraw()} tries to withdraw
* more money than is available, taking the credit line into account, an
* {@code IllegalArgumentException} should be thrown.
*
* @see AbstractAccount
*/
public class CreditAccountDocs extends AbstractAccountDocs {
// TODO: Add fields here
/**
* Initializes a new {@code CreditAccount} with the specified credit line.
*
* @param creditLine the credit line
* @throws IllegalArgumentException if the credit line is negative
*/
public CreditAccountDocs(double creditLine) {
// TODO: Implement this constructor
}
// TODO: Override abstract method here
/**
* @return the credit line
*
* @see CreditAccountTest#testCreditLine()
*/
public double getCreditLine() {
// TODO: Implement this method
return 0.0;
}
/**
* Sets the credit line.
*
* @param creditLine the credit line
* @throws IllegalArgumentException if the credit line is negative
* @throws IllegalStateException if the new credit line does not cover the existing balance
*
* @see CreditAccountTest#testCreditLine()
*/
public void setCreditLine(double creditLine) {
// TODO: Implement this method
}
}

View File

@@ -0,0 +1,15 @@
package oving7.abstractaccount;
/**
* A debit account is the simplest form of account, where the only requirement is that the balance
* at any time must be greater than or equal to {@code 0.0}. {@code DebitAccount} extends (inherits
* from) {@link AbstractAccount} and ensure that the balance never falls below {@code 0.0}. If an
* attempt is made to withdraw more money than is available, an {@code IllegalArgumentException}
* should be thrown.
*
* @see AbstractAccount
*/
public class DebitAccountDocs extends AbstractAccountDocs {
// TODO: Override abstract method here
}

View File

@@ -0,0 +1,28 @@
package oving7.abstractaccount;
/**
* A {@code SavingsAccount} can only have a positive balance. In addition, the account has
* withdrawal restrictions. A {@code SavingsAccount} has {@code x} number of {@code withdrawals}. If
* you want to withdraw money after all withdrawals have been used up, the balance should be charged
* a {@code fee}. If the balance is too low to cover the fee, an {@code IllegalArgumentException}
* should be thrown.
*
* @see AbstractAccount
*/
public class SavingsAccountDocs extends AbstractAccountDocs {
// TODO: Add fields here
/**
* Initializes a new {@code SavingsAccount} with the specified number of withdrawals and fee.
*
* @param withdrawals the number of withdrawals
* @param fee the fee
* @throws IllegalArgumentException if the number of withdrawals or the fee is negative
*/
public SavingsAccountDocs(int withdrawals, double fee) {
// TODO: Implement this constructor
}
// TODO: Override abstract method here
}

View File

View File

@@ -0,0 +1,63 @@
package oving7.train;
/**
* One of two different types of train cars, both specialized versions for different purposes. A
* {@code CargoCar} represents a cargo car that transports various things and stuff.
*
* @see TrainCar
* @see PassengerCar
*/
public class CargoCarDocs extends TrainCarDocs {
// TODO: Add fields here
/**
* Constructor for the cargo car.
*
* @param deadWeight the weight of an empty cargo car
* @param cargoWeight the weight of the cargo in the cargo car
* @throws IllegalArgumentException if either deadWeight or cargoWeight is negative
*
* @see CargoCarTest#testWeight()
*/
public CargoCarDocs(int deadWeight, int cargoWeight) {
// TODO: Implement this constructor
super(deadWeight);
}
/**
* @return the weight of the cargo in the cargo car
*
* @see CargoCarTest#testWeight()
*/
public int getCargoWeight() {
// TODO: Implement this method
return 0;
}
/**
* @param cargoWeight the weight of the cargo in the cargo car
* @throws IllegalArgumentException if cargoWeight is negative
*
* @see CargoCarTest#testWeight()
*/
public void setCargoWeight(int cargoWeight) {
// TODO: Implement this method
}
@Override
public int getTotalWeight() {
// TODO: Implement this method
return 0;
}
@Override
public String toString() {
// TODO: Implement this method
return null;
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,66 @@
package oving7.train;
/**
* One of two different types of train cars, both specialized versions for different purposes. A
* {@code PassengerCar} represents a passenger car that transports passengers.
*
* @see TrainCar
* @see CargoCar
*/
public class PassengerCarDocs extends TrainCarDocs {
// TODO: Add fields here
/**
* Constructor for the passenger car.
*
* @param deadWeight the weight of an empty passenger car
* @param passengerCount the number of passengers in the passenger car
* @throws IllegalArgumentException if either deadWeight or passengerCount is negative
*
* @see PassengerCarTest#testWeight()
*/
public PassengerCarDocs(int deadWeight, int passengerCount) {
// TODO: Implement this constructor
super(deadWeight);
}
/**
* @return the number of passengers in the passenger car
*
* @see PassengerCarTest#testWeight()
*/
public int getPassengerCount() {
// TODO: Implement this method
return 0;
}
/**
* @param passengerCount the number of passengers in the passenger car
* @throws IllegalArgumentException if passengerCount is negative
*
* @see PassengerCarTest#testWeight()
*/
public void setPassengerCount(int passengerCount) {
// TODO: Implement this method
}
@Override
public int getTotalWeight() {
// To calculate the total weight of the passenger car, you can assume that an average
// passenger weighs 80 kg
// TODO: Implement this method
return 0;
}
@Override
public String toString() {
// TODO: Implement this method
return null;
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,81 @@
package oving7.train;
/**
* The class {@code Train} represents a train that consists of one or more train cars.
*
* @see TrainCar
* @see CargoCar
* @see PassengerCar
*/
public class TrainDocs {
// TODO: Add fields here
/**
* @param trainCar the train car to check for
* @return {@code true} if the train contains the train car, {@code false} otherwise
*
* @see TrainTest#testAddCarToTrain()
*/
public boolean contains(TrainCar trainCar) {
// TODO: Implement this method
return false;
}
/**
* Adds a train car to the train.
*
* @param trainCar the train car to add
* @throws IllegalArgumentException if the train car is {@code null}
*
* @see TrainTest#testAddCarToTrain()
*/
public void addTrainCar(TrainCar trainCar) {
// TODO: Implement this method
}
/**
* @return the sum of the total weight of all the train cars in the train. There is no need to
* take the weight of the locomotive into account
*
* @see TrainTest#testTotalTrainWeight()
*/
public int getTotalWeight() {
// TODO: Implement this method
return 0;
}
/**
* @return similar to {@link PassengerCar#getPassengerCount()}, but for the entire train
*
* @see TrainTest#testPassengerCount()
*/
public int getPassengerCount() {
// TODO: Implement this method
return 0;
}
/**
* @return similar to {@link CargoCar#getCargoWeight()}, but for the entire train
*
* @see TrainTest#testCargoWeight()
*/
public int getCargoWeight() {
// TODO: Implement this method
return 0;
}
/**
* @return a string representation of the train. The string should consist of the
* {@link #toString()}s of all train cars in the train
*/
@Override
public String toString() {
return null;
}
// TODO: Write a main method to test the class
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,71 @@
package oving7.train;
/**
* The class {@code TrainCar} represents a simple and general train car.
*/
public class TrainCarDocs {
// TODO: Add fields here
/**
* Constructor for a train car.
*
* @param deadWeight the weight of an empty train car
* @throws IllegalArgumentException if deadWeight is negative
*
* @see TrainCarTest#testDeadWeight()
*/
public TrainCarDocs(int deadWeight) {
// TODO: Implement this constructor
}
/**
* @param deadWeight the weight of an empty train car. In other words, the weight of only the
* carriage, without passengers and cargo
* @throws IllegalArgumentException if deadWeight is negative
*
* @see TrainCarTest#testDeadWeight()
*/
public void setDeadWeight(int deadWeight) {
// TODO: Implement this method
}
/**
* @return the weight of an empty train car. In other words, the weight of only the carriage,
* without passengers and cargo
*
* @see TrainCarTest#testDeadWeight()
*/
public int getDeadWeight() {
// TODO: Implement this method
return 0;
}
/**
* @return the total weight of the train car. Note that this method should also be callable on
* subclasses and still return the total weight of the train car (keyword:
* redefinition).
*
* @see TrainCarTest#testDeadWeight()
*/
public int getTotalWeight() {
// TODO: Implement this method
return 0;
}
/**
* @return a string representation of the train car. The string should contain the type of the
* train car and the total weight of the train car. For {@link PassengerCar}, the number
* of passengers should also be included. For {@link CargoCar}, the weight of the cargo
* should also be included.
*/
@Override
public String toString() {
// TODO: Implement this method
return null;
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,79 @@
package oving7.abstractaccount;
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 CreditAccountTest {
private static final double epsilon = 0.0005;
private CreditAccount sub;
@BeforeEach
public void setUp() {
sub = new CreditAccount(10_000.0);
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
assertEquals(0.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.deposit(10_000.0);
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.deposit(-10_000.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
sub.deposit(20_000.0);
sub.withdraw(5000.0);
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.withdraw(20_000.0);
assertEquals(-5000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(20_000.0);
}, "Withdrawal exceeding the credit limit should have triggered an IllegalArgumentException");
}
@Test
@DisplayName("Check that the credit limit works as expected")
public void testCreditLine() {
assertEquals(10_000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
sub.setCreditLine(5000.0);
assertEquals(5000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.setCreditLine(-5000.0);
}, "Cannot have a negative credit limit");
assertEquals(5000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
sub.withdraw(4000.0);
assertThrows(IllegalStateException.class, () -> {
sub.setCreditLine(3000.0);
}, "Cannot set a credit limit that would result in an invalid balance");
assertEquals(-4000.0, sub.getBalance(), epsilon, "The balance was incorrect");
assertEquals(5000.0, sub.getCreditLine(), epsilon, "The credit limit was incorrect");
}
}

View File

@@ -0,0 +1,52 @@
package oving7.abstractaccount;
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 DebitAccountTest {
private static final double epsilon = 0.0005;
private DebitAccount sub;
@BeforeEach
public void setUp() {
sub = new DebitAccount();
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
assertEquals(0.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.deposit(10_000.0);
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.deposit(-10_000.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
sub.deposit(20_000.0);
sub.withdraw(5000.0);
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(20_000.0);
}, "Withdrawal exceeding the balance should have triggered an IllegalArgumentException");
}
}

View File

@@ -0,0 +1,65 @@
package oving7.abstractaccount;
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 SavingsAccountTest {
private static final double epsilon = 0.0005;
private SavingsAccount sub;
@BeforeEach
public void setUp() {
sub = new SavingsAccount(1, 50.0);
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
assertEquals(0.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.deposit(10_000.0);
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.deposit(-10_000.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
sub.deposit(20_000.0);
sub.withdraw(5000.0);
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(20_000.0);
}, "Withdrawal exceeding the balance should have triggered an IllegalArgumentException");
assertEquals(15_000.0, sub.getBalance(), epsilon, "The account balance was incorrect");
sub.withdraw(10_000.0);
assertEquals(4950.0, sub.getBalance(), epsilon,
"The account balance was incorrect after the fee was deducted");
assertThrows(IllegalArgumentException.class, () -> {
sub.withdraw(4930.0);
}, "Withdrawal exceeding the balance + fee should have triggered an IllegalArgumentException");
assertEquals(4950.0, sub.getBalance(), epsilon,
"The account balance was incorrect after the fee was deducted");
}
}

View File

@@ -0,0 +1,87 @@
package oving7.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class CardDeckTest {
private Card s1;
private Card s2;
private Card h1;
private Card h2;
private Card d1;
private Card d2;
private Card c1;
private Card c2;
private CardDeck deck;
private List<Card> expected;
private static void testCards(CardContainer it, List<Card> expected) {
assertEquals(expected.size(), it.getCardCount());
for (int i = 0; i < expected.size(); i++) {
Card expectedCard = expected.get(i);
Card actualCard = it.getCard(i);
assertEquals(expectedCard.getSuit(), actualCard.getSuit(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
i++;
}
}
private static void testCards(Iterator<Card> actual, Iterator<Card> expected) {
while (expected.hasNext()) {
assertTrue(actual.hasNext());
Card expectedCard = expected.next();
Card actualCard = actual.next();
assertEquals(expectedCard.getSuit(), actualCard.getSuit(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
}
}
@BeforeEach
public void setUp() {
deck = new CardDeck(2);
s1 = new Card('S', 1);
s2 = new Card('S', 2);
h1 = new Card('H', 1);
h2 = new Card('H', 2);
d1 = new Card('D', 1);
d2 = new Card('D', 2);
c1 = new Card('C', 1);
c2 = new Card('C', 2);
expected = new LinkedList<>(List.of(s1, s2, h1, h2, d1, d2, c1, c2));
}
@Test
@DisplayName("Test maxCardCount")
public void testMaxCardCount() {
assertTrue(deck instanceof CardContainerImpl);
assertEquals(52, deck.getMaxCardCount());
}
@Test
@DisplayName("Test that CardDeckImpl implements CardContainer")
public void testCardContainer() {
CardDeckTest.testCards(deck, expected);
}
@Test
@DisplayName("Test that CardDeckImpl implements Iterable")
public void testDeckIterator() {
CardDeckTest.testCards(deck.iterator(), expected.iterator());
}
}

View File

@@ -0,0 +1,82 @@
package oving7.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Iterator;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class CardHandTest {
private Card c2;
private Card s1;
private CardHand hand;
private List<Card> expected;
private static void testCards(CardContainer it, List<Card> expected) {
assertEquals(expected.size(), it.getCardCount());
for (int i = 0; i < expected.size(); i++) {
Card expectedCard = expected.get(i);
Card actualCard = it.getCard(i);
assertEquals(expectedCard.getSuit(), actualCard.getSuit(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(),
String.format("Card number %d should have been %s, but was %s", i + 1,
expectedCard, actualCard));
i++;
}
}
private static void testCards(Iterator<Card> actual, Iterator<Card> expected) {
while (expected.hasNext()) {
assertTrue(actual.hasNext());
Card expectedCard = expected.next();
Card actualCard = actual.next();
assertEquals(expectedCard.getSuit(), actualCard.getSuit(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
assertEquals(expectedCard.getFace(), actualCard.getFace(), String
.format("The card should have been %s, but was %s", expectedCard, actualCard));
}
}
@BeforeEach
public void setUp() {
hand = new CardHand(2);
s1 = new Card('S', 1);
c2 = new Card('C', 2);
expected = List.of(s1, c2);
}
@Test
@DisplayName("Test cardCount")
public void testCardCount() {
assertTrue(hand instanceof CardContainerImpl);
assertEquals(0, hand.getCardCount(), "CardCount should have been 0 at the start");
hand.addCard(new Card('S', 1));
hand.addCard(new Card('S', 2));
assertEquals(2, hand.getCardCount(), "CardCount should have been 2 at the start");
}
@Test
@DisplayName("Test that cardDeckImpl implements cardContainer")
public void testCardContainer() {
hand.addCard(new Card('S', 1));
hand.addCard(new Card('C', 2));
CardHandTest.testCards(hand, expected);
}
@Test
@DisplayName("Test that cardDeckImpl implements iterable")
public void testDeckIterator() {
hand.addCard(new Card('S', 1));
hand.addCard(new Card('C', 2));
CardHandTest.testCards(hand.iterator(), expected.iterator());
}
}

View File

@@ -0,0 +1,162 @@
package oving7.observablelist;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class ObservableHighscoreListTest {
private int pos1;
private int pos2;
private ObservableHighscoreList highscoreList;
private static void checkHighscoreList(String contextMessage, ObservableHighscoreList list,
List<Integer> elements) {
assertEquals(elements.size(), list.size(),
contextMessage + " -> Testing the length of the highscore list");
int i = 0;
for (int element : elements) {
assertEquals(element, list.getElement(i),
contextMessage + " -> Testing that the element at position " + i + " matches");
i++;
}
}
private void addResultWithListener(int pos, int element) {
pos1 = pos;
highscoreList.addResult(element);
// Check that the position that was changed is the same as the one sent to the listener
assertEquals(pos1, pos2, "Added " + element + " at position " + pos
+ " -> Testing the position received by the listener");
}
@BeforeEach
public void setUp() {
highscoreList = new ObservableHighscoreList(3);
pos1 = -1;
pos2 = -1;
}
@Test
@DisplayName("Test constructor")
public void testConstructor() {
assertEquals(0, highscoreList.size(), "Testing initialization of the highscore list");
}
@Test
@DisplayName("Add results (simple)")
public void testAddElementSimple() {
highscoreList.addResult(5);
ObservableHighscoreListTest.checkHighscoreList("Added 5 to an empty list", highscoreList,
List.of(5));
highscoreList.addResult(6);
ObservableHighscoreListTest.checkHighscoreList("Added 6 to the list [5]", highscoreList,
List.of(5, 6));
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [5, 6]", highscoreList,
List.of(2, 5, 6));
}
@Test
@DisplayName("Add results - list becomes too long")
public void testAddElementMoreThanMax() {
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
highscoreList.addResult(3);
ObservableHighscoreListTest.checkHighscoreList("Added 3 to the list [2, 5, 6]",
highscoreList, List.of(2, 3, 5));
highscoreList.addResult(7);
ObservableHighscoreListTest.checkHighscoreList("Added 7 to the list [2, 3, 5]",
highscoreList, List.of(2, 3, 5));
}
@Test
@DisplayName("Add two identical elements")
public void testAddElementDuplicate() {
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [2, 5, 6]",
highscoreList, List.of(2, 2, 5));
}
@Test
@DisplayName("Test listeners (simple)")
public void testListListenersSimple() {
// Mock a listener
ObservableListListener listener = (list, pos) -> pos2 = pos;
highscoreList.addObservableListListener(listener);
this.addResultWithListener(0, 5);
ObservableHighscoreListTest.checkHighscoreList("Added 5 to the list []", highscoreList,
List.of(5));
this.addResultWithListener(1, 6);
ObservableHighscoreListTest.checkHighscoreList("Added 6 to the list [5]", highscoreList,
List.of(5, 6));
this.addResultWithListener(0, 2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [5, 6]", highscoreList,
List.of(2, 5, 6));
}
@Test
@DisplayName("With listener - list becomes too long")
public void testListListenerMoreThanMax() {
// Mock a listener
ObservableListListener listener = (list, pos) -> pos2 = pos;
highscoreList.addObservableListListener(listener);
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
this.addResultWithListener(1, 3);
ObservableHighscoreListTest.checkHighscoreList("Added 3 to the list [2, 5, 6]",
highscoreList, List.of(2, 3, 5));
// Reset pos2 since the next element falls outside the list and is therefore not updated by
// itself and sent to the listener
pos2 = -1;
this.addResultWithListener(-1, 7);
ObservableHighscoreListTest.checkHighscoreList("Added 7 to the list [2, 3, 5]",
highscoreList, List.of(2, 3, 5));
}
@Test
@DisplayName("With listener - two identical elements")
public void testListListenerDuplicate() {
// Mock a listener
ObservableListListener listener = (list, pos) -> pos2 = pos;
highscoreList.addObservableListListener(listener);
highscoreList.addResult(5);
highscoreList.addResult(6);
highscoreList.addResult(2);
ObservableHighscoreListTest.checkHighscoreList("Added 5, 6, and 2 to the list",
highscoreList, List.of(2, 5, 6));
this.addResultWithListener(1, 2);
ObservableHighscoreListTest.checkHighscoreList("Added 2 to the list [2, 5, 6]",
highscoreList, List.of(2, 2, 5));
}
}

View File

@@ -0,0 +1,106 @@
package oving7.observablelist;
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 java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class ObservableListTest {
private int pos1;
private int pos2;
private ObservableList observableList;
private static void checkObservableList(ObservableList list, List<Integer> elements,
String contextMessage) {
assertEquals(elements.size(), list.size(),
contextMessage + " -> Testing the length of observableList");
int i = 0;
for (int element : elements) {
assertEquals(element, list.getElement(i),
contextMessage + " -> Testing that the element at position " + i + " matches");
i++;
}
}
private void addElementWithListener(int pos, int element) {
pos1 = pos;
observableList.addElement(pos, element);
// Check that the position that was changed is the same as the one sent to the listener
assertEquals(pos1, pos2, "Added " + element + " at position " + pos
+ " -> Testing the position received by the listener");
}
@BeforeEach
public void setUp() {
observableList = new ObservableList() {
@Override
public boolean acceptsElement(Object element) {
return element instanceof Integer;
}
};
pos1 = -1;
pos2 = -1;
}
@Test
@DisplayName("Test constructor")
public void testConstructor() {
assertEquals(0, observableList.size());
}
@Test
@DisplayName("Test acceptance of elements")
public void testAcceptsElement() {
assertTrue(observableList.acceptsElement(5), "Testing that the list accepts integers");
assertFalse(observableList.acceptsElement("5"),
"Testing that the list does not accept strings");
assertThrows(IllegalArgumentException.class, () -> {
observableList.addElement("5");
}, "Testing that the list cannot accept elements of type string");
}
@Test
@DisplayName("Test adding elements")
public void testAddElement() {
observableList.addElement(5);
ObservableListTest.checkObservableList(observableList, List.of(5),
"Added 5 to an empty list");
observableList.addElement(6);
ObservableListTest.checkObservableList(observableList, List.of(5, 6),
"Added 6 to the list [5]");
observableList.addElement(0, 2);
ObservableListTest.checkObservableList(observableList, List.of(2, 5, 6),
"Added 2 at position 0 in the list [5, 6]");
}
@Test
@DisplayName("Test listener")
public void testListListener() {
ObservableListListener listener = (list, pos) -> pos2 = pos;
observableList.addObservableListListener(listener);
this.addElementWithListener(0, 5);
ObservableListTest.checkObservableList(observableList, List.of(5),
"Added 5 to the list []");
this.addElementWithListener(1, 6);
ObservableListTest.checkObservableList(observableList, List.of(5, 6),
"Added 6 to the list [5]");
this.addElementWithListener(0, 2);
ObservableListTest.checkObservableList(observableList, List.of(2, 5, 6),
"Added 2 to the list [5, 6]");
}
}

View File

@@ -0,0 +1,84 @@
package oving7.savingsaccount;
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 BSUTest {
private static final double epsilon = 0.001;
private BSU bsu;
@BeforeEach
public void setUp() {
bsu = new BSU(0.05, 25_000.0);
}
@Test
@DisplayName("Check that deposits work as expected")
public void testDeposit() {
bsu.deposit(10_000.0);
assertEquals(10_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
bsu.deposit(-100.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertThrows(IllegalStateException.class, () -> {
bsu.deposit(20_000.0);
}, "Should not be able to deposit more money than the deposit limit");
bsu.endYearUpdate();
bsu.deposit(20_000.0);
assertEquals(10_000.0 * (1 + 0.05) + 20_000.0, bsu.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Check that withdrawals work as expected")
public void testWithdraw() {
bsu.deposit(20_000.0);
bsu.withdraw(5000.0);
assertEquals(15_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalArgumentException.class, () -> {
bsu.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(15_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
assertThrows(IllegalStateException.class, () -> {
bsu.withdraw(20_000);
}, "Should not be able to withdraw more money than deposited this year");
assertEquals(15_000.0, bsu.getBalance(), epsilon, "The account balance was incorrect");
bsu.endYearUpdate();
assertThrows(IllegalStateException.class, () -> {
bsu.withdraw(10_000);
}, "Should not be able to withdraw more money than deposited this year");
assertEquals(15_000 * (1 + 0.05), bsu.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Check that the tax deduction is correct")
public void testTaxDeduction() {
bsu.deposit(20_000.0);
assertEquals(20_000.0 * 0.20, bsu.getTaxDeduction(), epsilon,
"The tax deduction was incorrect");
bsu.endYearUpdate();
bsu.deposit(10_000.0);
assertEquals(10_000.0 * 0.20, bsu.getTaxDeduction(), epsilon,
"The tax deduction was incorrect");
bsu.endYearUpdate();
assertEquals(0.0, bsu.getTaxDeduction(), epsilon, "The tax deduction was incorrect");
}
}

View File

@@ -0,0 +1,72 @@
package oving7.savingsaccount;
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 ForeldreSparTest {
private static final double epsilon = 0.001;
private ForeldreSpar foreldreSpar;
@BeforeEach
public void setUp() {
foreldreSpar = new ForeldreSpar(0.04, 3);
}
@Test
@DisplayName("Check that withdraw works as expected")
public void testWithdraw() {
foreldreSpar.deposit(10_000.0);
foreldreSpar.withdraw(1000.0);
assertEquals(9000.0, foreldreSpar.getBalance(), epsilon,
"The account balance is incorrect");
assertThrows(IllegalArgumentException.class, () -> {
foreldreSpar.withdraw(-10_000.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(9000, foreldreSpar.getBalance(), epsilon, "The account balance is incorrect");
assertThrows(IllegalArgumentException.class, () -> {
foreldreSpar.withdraw(10_000);
}, "Should not be able to withdraw more money than is in the account");
assertEquals(9000.0, foreldreSpar.getBalance(), epsilon,
"The account balance is incorrect");
foreldreSpar.withdraw(1000.0);
foreldreSpar.withdraw(1000.0);
assertThrows(IllegalStateException.class, () -> {
foreldreSpar.withdraw(1000.0);
}, "Should not be able to make more withdrawals than the set limit");
foreldreSpar.endYearUpdate();
foreldreSpar.withdraw(1000.0);
assertEquals(7000.0 * (1 + 0.04) - 1000.0, foreldreSpar.getBalance(), epsilon,
"The account balance is incorrect");
}
@Test
@DisplayName("Check that remaining withdrawals are always correct")
public void testRemainingWithdrawals() {
foreldreSpar.deposit(10_000.0);
foreldreSpar.withdraw(1000.0);
assertEquals(2, foreldreSpar.getRemainingWithdrawals());
foreldreSpar.withdraw(1000.0);
foreldreSpar.withdraw(1000.0);
assertEquals(0, foreldreSpar.getRemainingWithdrawals());
assertThrows(IllegalStateException.class, () -> {
foreldreSpar.withdraw(1000.0);
}, "Should not be able to make more withdrawals than the set limit");
foreldreSpar.endYearUpdate();
assertEquals(3, foreldreSpar.getRemainingWithdrawals());
}
}

View File

@@ -0,0 +1,76 @@
package oving7.savingsaccount;
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 SavingsAccountTest {
private static final double epsilon = 0.001;
private SavingsAccount savingsAccount;
@BeforeEach
public void setUp() {
savingsAccount = new SavingsAccount(0.1);
}
@Test
@DisplayName("Check that the balance is correct after deposit")
public void testBalance() {
savingsAccount.deposit(100.0);
assertEquals(100.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Test deposit and withdraw")
public void testDepositAndWithdraw() {
savingsAccount.deposit(100.0);
savingsAccount.withdraw(40.0);
assertEquals(60.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Test deposit and withdraw with illegal input")
public void testDepositAndWithdrawIllegalInput() {
savingsAccount.deposit(10.0);
assertThrows(IllegalArgumentException.class, () -> {
savingsAccount.deposit(-100.0);
}, "Negative deposit should have triggered an IllegalArgumentException!");
assertEquals(10.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
savingsAccount.deposit(10.0);
assertThrows(IllegalArgumentException.class, () -> {
savingsAccount.withdraw(-100.0);
}, "Negative withdrawal should have triggered an IllegalArgumentException!");
assertEquals(20.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
savingsAccount.deposit(10.0);
assertThrows(IllegalArgumentException.class, () -> {
savingsAccount.withdraw(40.0);
}, "Withdrawal of more than the balance should have triggered an IllegalArgumentException");
assertEquals(30.0, savingsAccount.getBalance(), epsilon,
"The account balance was incorrect");
}
@Test
@DisplayName("Test that the interest is calculated correctly")
public void endYearUpdate() {
savingsAccount.deposit(100.0);
savingsAccount.endYearUpdate();
assertEquals(100.0 * (1 + 0.10), savingsAccount.getBalance(), epsilon,
"The account balance was incorrect after interest was added");
}
}

View File

@@ -0,0 +1,28 @@
package oving7.train;
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 CargoCarTest {
private CargoCar cargoCar;
@BeforeEach
public void setUp() {
cargoCar = new CargoCar(3000, 2000);
}
@Test
@DisplayName("Check total weight")
public void testWeight() {
assertEquals(5000, cargoCar.getTotalWeight(), "Test total weight after initialization");
cargoCar.setCargoWeight(4000);
assertEquals(7000, cargoCar.getTotalWeight(),
"Test total weight after changing cargo weight");
assertEquals(4000, cargoCar.getCargoWeight(),
"Test cargo weight after changing the weight");
}
}

View File

@@ -0,0 +1,29 @@
package oving7.train;
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 PassengerCarTest {
private PassengerCar passengerCar;
@BeforeEach
public void setUp() {
passengerCar = new PassengerCar(3000, 200);
}
@Test
@DisplayName("Check total weight")
public void testWeight() {
assertEquals(3000 + (200 * 80), passengerCar.getTotalWeight(),
"Test total weight after initialization");
passengerCar.setPassengerCount(100);
assertEquals(3000 + (100 * 80), passengerCar.getTotalWeight(),
"Test total weight after changing the number of passengers");
assertEquals(100, passengerCar.getPassengerCount(),
"Test passenger count after changing the number");
}
}

View File

@@ -0,0 +1,26 @@
package oving7.train;
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 TrainCarTest {
private TrainCar trainCar;
@BeforeEach
public void setUp() {
trainCar = new TrainCar(3000);
}
@Test
@DisplayName("Dead weight equals total weight")
public void testDeadWeight() {
assertEquals(3000, trainCar.getTotalWeight(), "Test initialization of dead weight");
trainCar.setDeadWeight(5000);
assertEquals(5000, trainCar.getTotalWeight(),
"Test that total weight equals set dead weight");
}
}

View File

@@ -0,0 +1,85 @@
package oving7.train;
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 TrainTest {
private CargoCar cc1;
private CargoCar cc2;
private PassengerCar pc1;
private PassengerCar pc2;
private Train train;
@BeforeEach
public void setUp() {
train = new Train();
pc1 = new PassengerCar(2000, 200);
pc2 = new PassengerCar(1500, 100);
cc1 = new CargoCar(3000, 5000);
cc2 = new CargoCar(2500, 7000);
}
@Test
@DisplayName("Add cars to train")
public void testAddCarToTrain() {
train.addTrainCar(pc1);
train.addTrainCar(pc2);
train.addTrainCar(cc1);
assertTrue(train.contains(pc1),
"Test if the train contains passenger car 1 after it has been added");
assertTrue(train.contains(pc2),
"Test if the train contains passenger car 2 after it has been added");
assertTrue(train.contains(cc1),
"Test if the train contains cargo car 1 after it has been added");
assertFalse(train.contains(cc2),
"Test if the train contains cargo car 2 without it being added");
assertThrows(IllegalArgumentException.class, () -> {
train.addTrainCar(null);
}, "Test if an IllegalArgumentException is thrown when adding a null car");
}
@Test
@DisplayName("Check total weight of the train")
public void testTotalTrainWeight() {
train.addTrainCar(pc1);
train.addTrainCar(cc1);
assertEquals(8000 + (2000 + (200 * 80)), train.getTotalWeight(),
"Test the train's total weight after adding a passenger car and a cargo car");
train.addTrainCar(pc2);
assertEquals(8000 + (2000 + (200 * 80)) + (1500 + (100 * 80)), train.getTotalWeight(),
"Test the train's total weight after adding another passenger car");
}
@Test
@DisplayName("Check passenger count on the train")
public void testPassengerCount() {
train.addTrainCar(pc1);
train.addTrainCar(pc2);
assertEquals(300, train.getPassengerCount(),
"Test passenger count after adding passenger cars");
train.addTrainCar(cc1);
assertEquals(300, train.getPassengerCount(),
"Test passenger count after adding a cargo car");
}
@Test
@DisplayName("Check cargo weight on the train")
public void testCargoWeight() {
train.addTrainCar(cc1);
train.addTrainCar(cc2);
assertEquals(12_000, train.getCargoWeight(), "Test cargo weight after adding cargo cars");
train.addTrainCar(pc1);
assertEquals(12_000, train.getCargoWeight(),
"Test cargo weight after adding a passenger car");
}
}