@@ -8,3 +8,4 @@ Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmeri
|
||||
| [Øving 1](./oppgavetekster/oving1/README.md) | Java-syntaks og objektorientert tankegang |
|
||||
| [Øving 2](./oppgavetekster/oving2/README.md) | Innkapsling og validering |
|
||||
| [Øving 3](./oppgavetekster/oving3/README.md) | Klasser og testing |
|
||||
| [Øving 4](./oppgavetekster/oving4/README.md) | Objektstrukturer |
|
||||
|
||||
26
oppgavetekster/oving4/Card.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Objektstrukturer - Card-oppgave del 2
|
||||
|
||||
Denne oppgaven handler om klasser for kortspill: `Card` (kort), `CardDeck` (kortstokk) og `CardHand` (korthånd), hvorav de to siste inneholder én eller flere `Card`-objekter. Oppgaven bygger på `Card` og `CardDeck` i [Innkapsling - Card-oppgave](../oving3/Card.md).
|
||||
|
||||
Filene i denne oppgaven skal lagres i [oving4/card](../../src/main/java/oving4/card).
|
||||
|
||||
**Merk**: Om du ikke har gjort `Card`-oppgaven allerede, bør du gjøre dette 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/oving3/card/Card.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 3.
|
||||
|
||||
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser, og de underordnede objektene vil kunne flyttes/overføres mellom de overordnede. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner). Et eksempel er kortspill, hvor kortene starter i kortstokken, fordeles på korthender og til slutt ender i en kortbunke. Et kort kan bare være ett sted om gangen, og må overføres fra ett sted til et annet, f.eks. fra kortstokk til korthender i utdelingsfasen. I [Innkapsling - Card-oppgave](../oving3/Card.md) ble det lagd logikk for kortstokk og enkeltkort. I denne oppgaven skal du implementere logikk for korthender, og utvide kortstokkens logikk litt.
|
||||
|
||||
`Card`-klassen har du allerede implementert, men du må sannsynligvis kopiere koden over fra `oving3` til `oving4`. Her er det enklest å lage en ny `Card`-klasse i `oving4` og så lime inn innholdet fra den gamle. Husk å ha riktig `package`.
|
||||
|
||||
`CardDeck`-klassen har du også implementert, og denne må også flyttes på samme måte som `Card`. Denne klassen skal utvides:
|
||||
|
||||
- `void deal(CardHand cardHand, int n)` - flytter `n` kort fra kortstokken (`CardDeck`-objektet) til korthånda (`CardHand`-objektet, som er første argument), ved å ta ett og ett kort med høyeste gyldige indeks, fjerne det fra `CardDeck`-objektet og legge det til `CardHand`-objektet. Kast unntak av typen `IllegalArgumentException` dersom `cardHand` er `null`.
|
||||
|
||||
`CardHand` er en ny klasse som skal implementeres. `CardHand`-objekter inneholder initielt ingen kort, og klassen inneholder de samme standardmetodene som `CardDeck`, altså `getCardCount()` og `getCard(int)`, for å lese hvor mange og hvilke kort den inneholder. I tillegg har den to metoder for å endre tilstand:
|
||||
|
||||
- `void addCard(Card)` - legger argumentet til dette `CardHand`-objektet. Kast unntak av typen `IllegalArgumentException` dersom argumentet er `null`.
|
||||
- `Card play(int n)` - returnerer og fjerner kort nr. `n` (første kort har nr. $0$) fra dette `CardHand`-objektet (som om det ble spilt ut).
|
||||
|
||||
## Java-kode
|
||||
|
||||
Utvid `CardDeck` og lag `CardHand` som beskrevet over. Test klassene med selvlagde main-metoder og ved å kjøre JUnit-testene.
|
||||
|
||||
Testkode for denne oppgaven finner du her: [oving4/card/CardTest.java](../../src/test/java/oving4/card/CardTest.java), [oving4/card/CardDeckTest.java](../../src/test/java/oving4/card/CardDeckTest.java), [oving4/card/CardHandTest.java](../../src/test/java/oving4/card/CardHandTest.java).
|
||||
62
oppgavetekster/oving4/Partner.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Objektstrukturer - Partner-oppgave
|
||||
|
||||
Denne oppgaven handler om en `Partner`-klasse med en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) kalt partner tilbake til samme klasse (altså kjønnsnøytralt partnerskap) og det å sikre konsistens, slik at Partner-objekter er parvis knyttet sammen.
|
||||
|
||||
En viktig del av det å implementere assosiasjoner er å sikre konsistens, dvs. at objekter i hver ende av en kobling refererer korrekt til hverandre. Et eksempel på dette for [1-1-assosiasjoner](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) er (kjønnsnøytralt) partnerskap, hvor to partnere er koblet til hverandre når partnerskap inngås og kobles fra hverandre ved en evt. skillsmisse. I denne oppgaven skal en `Partner`-klasse implementeres og ulike situasjoner håndteres korrekt, som illustrert nedenfor.
|
||||
|
||||
`Partner`-klassen skal inneholde informasjon om _navn_ (en `String` ulik `null`), som bare skal kunne settes i konstruktøren, og _partneren_, som er et annet `Partner`-objekt. Navnet er ikke viktig for oppførselen, men er grei å ha med i en `toString()`-metode, for å skille `Partner`-objektene fra hverandre. `Partner`-klassen skal ha følgende metoder for å lese tilstanden:
|
||||
|
||||
- `String getName()` - returnerer navnet knyttet til dette `Partner`-objektet.
|
||||
- `Partner getPartner()` - returnerer `Partner`-objektet som er knyttet til dette `Partner`-objektet, evt. `null`, hvis partnerskap ikke er inngått.
|
||||
|
||||
`Partner`-klassen har kun én endringsmetode, `void setPartner(Partner)`, som brukes både for å inngå partnerskap, når argumentet er et `Partner`-objekt, og oppløse det, når argumentet er `null`. Figurene under illustrerer de tre tilfellene som må kunne håndteres, og som `JUnit`-testene sjekker.
|
||||
|
||||
## Eksempler på kall
|
||||
|
||||
### 1. Inngåelse av partnerskap
|
||||
|
||||
**Kall**: `p1.setPartner(p2)`
|
||||
|
||||
**Beskrivelse**: `Partner`-objektene `p1` og `p2` kobles sammen med ett kall til `setPartner`. Før kallet er `p1` og `p2` ikke koblet sammen, og etter kallet er det koblet sammen.
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
### 2. Oppløsning av partnerskap
|
||||
|
||||
**Kall**: `p1.setPartner(null)`
|
||||
|
||||
**Beskrivelse**: `Partner`-objektene `p1` og `p2` kobles fra hverandre med ett kall til `setPartner` med `null` som argument. Før kallet er `p1` og `p2` koblet sammen, og etter kallet er det ikke lenger koblet sammen.
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
### 3. Oppløsning og inngåelse av partnerskap i ett
|
||||
|
||||
**Kall**: `p1.setPartner(p3)`
|
||||
|
||||
**Beskrivelse**: `Partner`-objektene `p1`, `p2`, `p3` og `p4` er parvis koblet sammen, før ett kall til `setPartner` kobler sammen `p1` og `p3`, mens `p2` og `p4` kobles fra deres tidligere partnere.
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
## Gjøremål
|
||||
|
||||
Oppgaven er (enkelt og greit) å implementere `Partner`-klassen og sjekke (f.eks. med en `main()`-metode) at `Partner`-objektene oppfører seg som de skal.
|
||||
|
||||
Testkode for denne oppgaven finner du her: [oving4/PartnerTest.java](../../src/test/java/oving4/PartnerTest.java).
|
||||
134
oppgavetekster/oving4/Person.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Objektstrukturer - Person-oppgave
|
||||
|
||||
Denne oppgaven handler om en `Person`-klasse med en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner) med rollene _children_ og _mother_/_father_ til samme klasse (altså barn-mor/far-forhold) og det å sikre konsistens, slik at foreldre og barn er korrekt knyttet sammen.
|
||||
|
||||
En viktig del av det å implementere assosiasjoner er å sikre konsistens, dvs. at objekter i hver ende av en kobling refererer korrekt til hverandre. Et eksempel på dette for [1-n-assosiasjoner](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner) er foreldreskap, hvor foreldre og barn er koblet til samme i et slektstre. I denne oppgaven skal en `Person`-klasse implementeres og det å legge til (og fjerne) barn håndteres korrekt, som illustrert nedenfor.
|
||||
|
||||
`Person`-klassen skal inneholde informasjon om _navn_ (en `String` ulik `null`) og _kjønn_ (en `char`, `'F'` eller `'M'`), som bare skal kunne settes i konstruktøren, og _mor_, _far_ og _barn_, som er andre `Person`-objekter. Navnet er ikke viktig for oppførselen, men er grei å ha med i en `toString()`-metode, for å skille `Person`-objektene fra hverandre. `Person`-klassen skal ha følgende metoder for å lese tilstanden:
|
||||
|
||||
- `String getName()` - returnerer navnet knyttet til dette `Person`-objektet.
|
||||
- `char getGender()` - returnerer tegnet som representerer kjønnet, enten `'F'` eller `'M'`.
|
||||
- `Person getMother()` - returnerer `Person`-objektet som er moren, evt. `null`.
|
||||
- `Person getFather()` - returnerer `Person`-objektet som er faren, evt. `null`.
|
||||
- `int getChildCount()` - returnerer antall barn dette `Person`-objektet har.
|
||||
- `Person getChild(int n)` - returnerer barn nr. `n` (altså et `Person`-objekt), evt. utløser (et passende) unntak om `n` er for stor (eller liten).
|
||||
|
||||
`Person`-klassen har to sett med endringsmetoder, knyttet til de to rollene i hver ende av _children_-_mother_/-_father_-assosiasjonene.
|
||||
|
||||
Fra _children_-perspektivet har vi følgende to metoder:
|
||||
|
||||
- `void addChild(Person)` - oppretter en kobling til et barn (et annet `Person`-objekt). Dersom `Person`-objektet som metoden kalles på, er en _kvinne_, så skal denne bli barnets _mor_, og motsatt, dersom `Person`-objektet som metoden kalles på, er en _mann_, så skal denne bli barnets _far_. Argumentet kan ikke være `null`.
|
||||
- `void removeChild(Person)` - fjerner en kobling til et barn (et annet `Person`-objekt). Dersom `Person`-objektet som metoden kalles på, er _moren_ til argumentet, så skal _mother_-koblingen fjernes, og motsatt, dersom `Person`-objektet som metoden kalles på, er argumentets _far_, så skal _father_-koblingen fjernes.
|
||||
|
||||
Fra _mother_/_father_-perspektivet har vi følgende to metoder:
|
||||
|
||||
- `void setMother(Person)` - setter argumentet (en kvinne) som _moren_ til `Person`-objektet som metoden kalles på. Argumentet får samtidig registrert `Person`-objektet som metoden kalles på, som sitt _barn_.
|
||||
- `void setFather(Person)` - setter argumentet (en mann) som _faren_ til `Person`-objektet som metoden kalles på. Argumentet får samtidig registrert `Person`-objektet som metoden kalles på, som sitt _barn_.
|
||||
|
||||
Det som er verd å merke seg er at begge sett med metoder, `addChild`/`removeChild` og `setMother`/`setFather`, må ha logikk som håndterer koblingen den andre veien, så `addChild`/`removeChild` må kalle `setMother`/`setFather` og omvendt, eller ha kode med tilsvarende effekt. Dette kan være nokså fiklete, fordi en både må sikre konsistens og unngå uendelig nøstede kall (inntil du får `StackOverflowException`).
|
||||
|
||||
Listen og figurene under illustrerer de fem tilfellene som må kunne håndteres, og som testes av testene det er lenket til.
|
||||
|
||||
## Eksempler på kall
|
||||
|
||||
### 1. Opprettelse av koblinger med `addChild`
|
||||
|
||||
**Kall**:
|
||||
`marit.addChild(jens)`
|
||||
|
||||
`hallvard.addChild(jens)`
|
||||
|
||||
(Dette har samme effekt som kallene under punkt [2](#2-opprettelse-av-koblinger-med-setmother-og-setfather).)
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
### 2. Opprettelse av koblinger med `setMother` og `setFather`
|
||||
|
||||
**Kall**:
|
||||
`jens.setMother(marit)`
|
||||
|
||||
`jens.setFather(hallvard)`
|
||||
|
||||
(Dette har samme effekt som kallene under punkt [1](#1-opprettelse-av-koblinger-med-addchild).)
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
### 3. Fjerning av koblinger med `removeChild`
|
||||
|
||||
**Kall**:
|
||||
`marit.removeChild(jens)`
|
||||
|
||||
`hallvard.removeChild(jens)`
|
||||
|
||||
(Dette har samme effekt som kallene under punkt [4](#4-fjerning-av-koblinger-med-setmother-og-setfather).)
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
### 4. Fjerning av koblinger med `setMother` og `setFather`
|
||||
|
||||
**Kall**:
|
||||
`jens.setMother(null)`
|
||||
|
||||
`jens.setFather(null)`
|
||||
|
||||
(Dette har samme effekt som kallene under punkt [3](#3-fjerning-av-koblinger-med-removechild).)
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
### 5. Fjerning og oppretting av kobling med `setMother` og `setFather`, en slags "adoption"
|
||||
|
||||
**Kall**:
|
||||
`jens.setFather(torkel)`
|
||||
|
||||
`jens.setMother(jorunn)`
|
||||
|
||||
**Før kall**:
|
||||
|
||||

|
||||
|
||||
**Etter kall**:
|
||||
|
||||

|
||||
|
||||
## Oppgaven
|
||||
|
||||
Oppgaven er delt i to trinn, den første håndterer _children_- og _mother_/_father_-rollen isolert og uten krav om konsistens,
|
||||
mens det andre skal sikre konsistens.
|
||||
|
||||
### Del 1
|
||||
|
||||
- Implementer `addChild`- og `removeChild`-metodene slik at `getChildCount`- og `getChild`-metodene virker som forventet. Disse metodene håndterer altså kun _children_-rollen.
|
||||
- Implementer `setMother`- og `setFather`-metodene slik at `getMother`- og `getFather`-metodene virker som forventet. Disse metodene håndteres altså kun _mother_/_father_-rollen.
|
||||
|
||||
Test metodene ved å koble opp `Person`-objekter tilsvarende din egen familie. Du blir nødt til å bruke de tre metodene `addChild`, `setMother` og `setFather`. Prøv å få med minst tre generasjoner.
|
||||
|
||||
### Del 2
|
||||
|
||||
Utvid metodene til å sikre konsistens. Test at det fortsatt virker å koble opp din egen familie, denne gangen ved å bare bruke
|
||||
`addChild` og ved å bare bruke `setMother` og `setFather`. Kjør `JUnit`-testene som hører til oppgaven.
|
||||
|
||||
Testkode for denne oppgaven finner du her: [oving4/PersonTest.java](../../src/test/java/oving4/PersonTest.java).
|
||||
61
oppgavetekster/oving4/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Øving 4: Objektstrukturer
|
||||
|
||||
## Øvingsmål
|
||||
|
||||
- Lære hva assosiasjoner er og hvordan dette brukes i OO
|
||||
- Lære hvordan man sikrer konsistent oppførsel mellom assosierte objekter
|
||||
|
||||
## Øvingskrav
|
||||
|
||||
- Kunne implementere klasser som har assosiasjoner til én eller flere andre klasser
|
||||
- Kunne sikre at disse assosiasjon er konsistente i enkle objektstrukturer
|
||||
- Kunne implementere metoder som oppretter, oppdaterer og fjerner slike assosiasjoner
|
||||
|
||||
## Dette må du gjøre
|
||||
|
||||
### Del 1: Programmering
|
||||
|
||||
I denne øvingen skal du velge og gjennomføre ENTEN både Partner- og Card del 2-oppgavene ELLER minst én av Twitter-, Stopwatch- og Person-oppgavene. Merk at **noen av oppgavene i neste øving (øving 5), bygger videre på noen av oppgavene under**, disse er uthevet med **fet skrift**. Det er ikke et krav at man gjør de uthevede oppgavene, men de gir flere oppgaver å velge mellom i øving 6.
|
||||
|
||||
**Gjør enten _begge_ disse:**
|
||||
|
||||
- [Partner](./Partner.md) (lett)
|
||||
- **[Card del 2](./Card.md)** (lett)
|
||||
|
||||
**Eller _minst én_ av følgende oppgaver:**
|
||||
|
||||
- **[Twitter](./Twitter.md)** (medium, men lang)
|
||||
- [Stopwatch](./StopWatch.md) (medium)
|
||||
- [Person](./Person.md) (medium/vanskelig)
|
||||
|
||||
Oppgavene for denne øvingen skal du lagre i [`src/main/java/oving4`](../../src/main/java/oving4). Test-filene ligger i [`src/test/java/oving4`](../../src/test/java/oving4).
|
||||
|
||||
Alle oppgavene ovenfor er høyst eksamensrelevante og det anbefales å ta en titt på alle sammen.
|
||||
|
||||
### Del 2: Klassediagram
|
||||
|
||||
- Lag et [klassediagram](https://www.ntnu.no/wiki/display/tdt4100/Klassediagrammer) for en av oppgavene du velger. Husk å få med relasjonene mellom klassene.
|
||||
|
||||
Diagrammet kan for eksempel skrives på papir eller tegnes/lages i et valgfritt program. Du trenger ikke levere inn diagrammene, men de skal vises til studass under godkjenning av øvingen.
|
||||
|
||||
### Del 3: Testing
|
||||
|
||||
Skriv kode som tester oppførselen til `CoffeeCup`-klassen, dvs. at du skal teste om metodene i listen under har rett oppførsel og returnerer det de skal, i tillegg til at du skal teste konstruktørene. Det er ikke nødvendig å teste absolutt alle mulige tilfeller, men det kreves at du tester den grunnleggende funksjonaliteten. Basert på resultatene dine, vurder om klassen er implementert på en logisk måte.
|
||||
|
||||
- `double getCapacity()`
|
||||
- `double getCurrentVolume()`
|
||||
- `void increaseCupSize(double)`
|
||||
- `void drinkCoffee(double)`
|
||||
- `void fillCoffee(double)`
|
||||
|
||||
Du finner `CoffeeCup`-klassen under [`src/main/java/oving4/testing`](../../src/main/java/oving4/testing).
|
||||
|
||||
Her er det anbefalt å bruke [JUnit](https://www.ntnu.no/wiki/display/tdt4100/Enhetstesting+med+JUnit), men det er lov å teste vha. `main()`-metoden også. Dersom du bruker JUnit må du opprette testen under navnet `CoffeeCupTest` i [`src/test/java/oving4/testing`](../../src/test/java/oving4/testing).
|
||||
|
||||
### 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.
|
||||
34
oppgavetekster/oving4/StopWatch.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Objektstrukturer - StopWatchManager-oppgave
|
||||
|
||||
Denne oppgaven handler om en `StopWatchManager`-klasse som inneholder flere `StopWatch`-objekter. Oppgaven bygger på klassen lagd i [StopWatch-oppgaven](../oving1/Stopwatch.md) fra "tilstand og oppførsel".
|
||||
|
||||
Dersom du ikke har gjort [`StopWatch`-oppgaven](../oving1/Stopwatch.md) allerede, bør du gjøre denne 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/oving1/StopWatch.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 1. Merk at validering i `StopWatch`-klassen ble gjort i [Innkapsling](../oving2/Encapsulation.md).
|
||||
|
||||
Filene i denne oppgaven skal lagres i [oving4/stopwatch](../../src/main/java/oving4/stopwatch).
|
||||
|
||||
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner).
|
||||
|
||||
I denne oppgaven skal du implementere en `StopWatchManager`-klasse som kan holde styr på flere stoppeklokker. Ved hjelp av `StopWatchManager` skal man enkelt kunne holde styr på flere stoppeklokker og sørge for at alle stoppeklokker får beskjed om tiden som går. Dette kan være nyttig hvis man f.eks. ønsker å holde styr på flere løpere i et skirenn der ikke alle starter og fullfører samtidig, men hvor allikevel klokken må gå for alle.
|
||||
|
||||
Det skal være mulig å opprette nye stoppeklokker med et tilhørende navn (streng). Navnet skal man senere kunne bruke til å hente stoppeklokken igjen eller fjerne stoppeklokken fra `StopWatchManager`. For å få til dette kan det være lurt å se litt på `Map` fra [Collection-rammeverket](https://www.ntnu.no/wiki/display/tdt4100/Collection-rammeverket).
|
||||
|
||||
`StopWatchManager` skal ha følgende endringsmetoder:
|
||||
|
||||
- `StopWatch newStopWatch(String name)` - Oppretter en ny stoppeklokke knyttet til navnet `name`. Returnerer den nye stoppeklokken. Argumentet kan ikke være `null` eller allerede være knyttet til en stoppeklokke.
|
||||
- `void removeStopWatch(String name)` - Fjerner stoppeklokken tilknyttet navnet `name`.
|
||||
- `void tick(int ticks)` - Informerer alle stoppeklokkene om at ticks tikk har gått.
|
||||
|
||||
`StopWatchManager` skal ha følgende lesemetoder:
|
||||
|
||||
- `StopWatch getStopWatch(String name)` - returnerer stoppeklokken tilknyttet navnet `name`.
|
||||
- `Collection<StopWatch> getAllWatches()` - returnerer alle stoppeklokkene.
|
||||
- `Collection<StopWatch> getStartedWatches()` - returnerer alle stoppeklokkene som er startet.
|
||||
- `Collection<StopWatch> getStoppedWatches()` - returnerer alle stoppeklokkene som er stoppet.
|
||||
|
||||
**Merk**: Det er viktig at de metodene som returnerer en samling av stoppeklokker returnerer nye samlinger. De som får en samling må kunne endre på den (f.eks. fjerne elementer) uten at dette forstyrrer `StopWatchManager` eller andre som har fått samlinger tidligere.
|
||||
|
||||
## Java-kode
|
||||
|
||||
Kopier `StopWatch` fra `oving2`-pakken og lag `StopWatchManager` som beskrevet over. Test klassen med selvlagde `main()`-metoder og ved å kjøre `JUnit`-testene.
|
||||
|
||||
Testkode for denne oppgaven finner du her: [oving4/stopwatch/StopWatchTest.java](../../src/test/java/oving4/stopwatch/StopWatchTest.java) og [oving4/stopwatch/StopWatchManagerTest.java](../../src/test/java/oving4/stopwatch/StopWatchManagerTest.java).
|
||||
67
oppgavetekster/oving4/Twitter.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Objektstrukturer - Twitter-oppgave
|
||||
|
||||
Denne oppgaven handler om en begrenset klone av `Twitter`, med to klasser, `TwitterAccount` og `Tweet`.
|
||||
|
||||
Filene i denne oppgaven skal lagres i [oving4/twitter](../../src/main/java/oving4/twitter).
|
||||
|
||||
En `Twitter`-konto kan følge andre `Twitter`-kontoer og motsatt: en `Twitter`-konto kan bli fulgt av andre `Twitter`-kontoer.
|
||||
Dette er altså en gjensidig kobling: Hvis konto A følger konto B, så er konto B fulgt av konto A. En kan selvsagt ikke følge seg selv.
|
||||
|
||||
I tillegg har hver Twitter-konto en mengde _tweets_, som er små, korte tekster. En tweet hører til den kontoen den ble sendt fra. Hvis noen finner en annen sin tweet interessant har man muligheten til å retweete denne. Da lager man en tweet som refererer til originalen, og (implisitt) få original-tweeten sin tekst. Merk at i en kjede av retweets, så vil alle referere til samme original-tweet. Med andre ord, hvis tweet B er en retweet av A og tweet C er en retweet av B, vil både tweet B og C ha A som original-tweet, slik det er vist under.
|
||||
|
||||
**Riktig objektstrutur**, når B er en retweet av A og C er en retweet av B:
|
||||
|
||||

|
||||
|
||||
**Feil objektstrutur**, når B er en retweet av A og C er en retweet av B:
|
||||
|
||||

|
||||
|
||||
## Tweet-klassen
|
||||
|
||||
`Tweet` skal ha to konstruktører, en for hver måte å tweete på:
|
||||
|
||||
- `Tweet(TwitterAccount, String)` - En ny original-tweet. Ingen av argumentene kan være `null`.
|
||||
- `Tweet(TwitterAccount, Tweet)` - En retweet av `Tweet`-argumentet. Utløser et passende unntak hvis original-tweeten er fra samme konto. Ingen av argumentene kan være `null`.
|
||||
|
||||
`Tweet` skal ha metodene:
|
||||
|
||||
- `String getText()` - returnerer teksten til en tweet.
|
||||
- `TwitterAccount getOwner()` - returnerer kontoen som tweeten kom fra.
|
||||
- `Tweet getOriginalTweet()` - returnerer original-tweeten, hvis den er en retweet, ellers `null`.
|
||||
- `int getRetweetCount()` - returnerer antall ganger denne tweeten har blitt retweetet.
|
||||
|
||||
## TwitterAccount-klassen
|
||||
|
||||
`TwitterAccount` skal ha konstruktøren:
|
||||
|
||||
- `TwitterAccount(String)` - som tar inn brukernavnet.
|
||||
|
||||
`TwitterAccount` skal ha metodene:
|
||||
|
||||
- `String getUserName()` - returnerer brukernavnet.
|
||||
- `void follow(TwitterAccount account)` - denne (`this`) kontoen starter å følge account. Hvis account allerede følges, skal metoden ikke gjøre noe. Dersom `account` er seg selv eller `null`, skal metoden kaste et passende unntak.
|
||||
- `void unfollow(TwitterAccount account)` - slutt å følge account.
|
||||
- `boolean isFollowing(TwitterAccount account)` - returnerer om denne kontoen følger account.
|
||||
- `boolean isFollowedBy(TwitterAccount account)` - returnerer om account følger denne kontoen.
|
||||
- `void tweet(String)` - lager en ny tweet for denne kontoen.
|
||||
- `void retweet(Tweet tweet)` - retweeter tweet fra denne kontoen.
|
||||
- `Tweet getTweet(int i)` - returner tweet nummer `i`, der $1$ er den nyeste, $2$ den nest nyeste, osv. (Merk rekkefølgen!)
|
||||
- `int getTweetCount()` - returner antall tweets til kontoen.
|
||||
- `int getRetweetCount()` - returner antall retweets av tweets fra denne kontoen.
|
||||
|
||||
## Del 1
|
||||
|
||||
- Implementer `Tweet`-klassen.
|
||||
- For å teste klassen må du sende inn TwitterAccount-objekter i konstruktøren. Lag en forenklet versjon av `TwitterAccount`-klassen for dette formålet, der du kun implementerer konstruktøren og evt. en passende `toString()`-metode. Dette gjør det mulig å teste `Tweet`-klassen din uten at du må implementere hele `TwitterAccount`-klassen først.
|
||||
|
||||
## Del 2
|
||||
|
||||
- Implementer `TwitterAccount`-klassen.
|
||||
- Test klassen og dens samspill med `Tweet`-klassen ved å lage Twitter-konto for deg selv og noen av vennene dine. La noen av kontoene følge hverandre, tweete og retweete.
|
||||
|
||||
Testkode for denne oppgaven finner du her: [oving4/twitter/TweetTest.java](../../src/test/java/oving4/twitter/TweetTest.java) og [oving4/twitter/TwitterAccountTest.java](../../src/test/java/oving4/twitter/TwitterAccountTest.java).
|
||||
|
||||
## Frivillig utvidelse
|
||||
|
||||
På Twitter kan man markere en annen sin tweet som en favoritt. Implementer passende metoder for å kunne gjøre dette. En konto må ha oversikt over hvilke tweets den har markert som favoritter, og en tweet må vite hvem og hvor mange som har markert den som favoritt. Hva synes du burde skje hvis man markerer en retweet som en favoritt?
|
||||
BIN
oppgavetekster/oving4/img/partner1.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
8
oppgavetekster/oving4/img/partner1.puml
Normal file
@@ -0,0 +1,8 @@
|
||||
@startuml partner1
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#p1:Partner" as p1
|
||||
object "~#p2:Partner" as p2
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/partner2.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
11
oppgavetekster/oving4/img/partner2.puml
Normal file
@@ -0,0 +1,11 @@
|
||||
@startuml partner2
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#p1:Partner" as p1
|
||||
object "~#p2:Partner" as p2
|
||||
|
||||
p1 --> p2 : partner
|
||||
p2 --> p1 : partner
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/partner3.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
15
oppgavetekster/oving4/img/partner3.puml
Normal file
@@ -0,0 +1,15 @@
|
||||
@startuml partner3
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#p1:Partner" as p1
|
||||
object "~#p2:Partner" as p2
|
||||
object "~#p3:Partner" as p3
|
||||
object "~#p4:Partner" as p4
|
||||
|
||||
p1 --> p2 : partner
|
||||
p2 --> p1 : partner
|
||||
p3 --> p4 : partner
|
||||
p4 --> p3 : partner
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/partner4.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
13
oppgavetekster/oving4/img/partner4.puml
Normal file
@@ -0,0 +1,13 @@
|
||||
@startuml partner4
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#p1:Partner" as p1
|
||||
object "~#p2:Partner" as p2
|
||||
object "~#p3:Partner" as p3
|
||||
object "~#p4:Partner" as p4
|
||||
|
||||
p1 --> p3 : partner
|
||||
p3 --> p1 : partner
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/person1.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
9
oppgavetekster/oving4/img/person1.puml
Normal file
@@ -0,0 +1,9 @@
|
||||
@startuml person1
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#hallvard:Person" as hallvard
|
||||
object "~#marit:Person" as marit
|
||||
object "~#jens:Person" as jens
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/person2.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
14
oppgavetekster/oving4/img/person2.puml
Normal file
@@ -0,0 +1,14 @@
|
||||
@startuml person2
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#hallvard:Person" as hallvard
|
||||
object "~#marit:Person" as marit
|
||||
object "~#jens:Person" as jens
|
||||
|
||||
marit --> jens : children
|
||||
jens --> marit : mother
|
||||
jens --> hallvard : father
|
||||
hallvard --> jens : children
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/person3.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
16
oppgavetekster/oving4/img/person3.puml
Normal file
@@ -0,0 +1,16 @@
|
||||
@startuml person3
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#hallvard:Person" as hallvard
|
||||
object "~#marit:Person" as marit
|
||||
object "~#jens:Person" as jens
|
||||
object "~#torkel:Person" as torkel
|
||||
object "~#jorunn:Person" as jorunn
|
||||
|
||||
marit --> jens : children
|
||||
jens -u-> marit : mother
|
||||
jens --> hallvard : father
|
||||
hallvard --> jens : children
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/person4.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
16
oppgavetekster/oving4/img/person4.puml
Normal file
@@ -0,0 +1,16 @@
|
||||
@startuml person4
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#hallvard:Person" as hallvard
|
||||
object "~#marit:Person" as marit
|
||||
object "~#jens:Person" as jens
|
||||
object "~#torkel:Person" as torkel
|
||||
object "~#jorunn:Person" as jorunn
|
||||
|
||||
jorunn --> jens : children
|
||||
jens -u-> jorunn : mother
|
||||
jens --> torkel : father
|
||||
torkel --> jens : children
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/twitter1.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
20
oppgavetekster/oving4/img/twitter1.puml
Normal file
@@ -0,0 +1,20 @@
|
||||
@startuml twitter1
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#a:Tweet" as a {
|
||||
text = "Kvitre-kvitre"
|
||||
}
|
||||
|
||||
object "~#b:Tweet" as b {
|
||||
text = "Kvitre-kvitre"
|
||||
}
|
||||
|
||||
object "~#c:Tweet" as c {
|
||||
text = "Kvitre-kvitre"
|
||||
}
|
||||
|
||||
b --> a : originalTweet
|
||||
c --> a : originalTweet
|
||||
|
||||
@enduml
|
||||
BIN
oppgavetekster/oving4/img/twitter2.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
20
oppgavetekster/oving4/img/twitter2.puml
Normal file
@@ -0,0 +1,20 @@
|
||||
@startuml twitter2
|
||||
|
||||
skinparam dpi 400
|
||||
|
||||
object "~#a:Tweet" as a {
|
||||
text = "Kvitre-kvitre"
|
||||
}
|
||||
|
||||
object "~#b:Tweet" as b {
|
||||
text = "Kvitre-kvitre"
|
||||
}
|
||||
|
||||
object "~#c:Tweet" as c {
|
||||
text = "Kvitre-kvitre"
|
||||
}
|
||||
|
||||
b --> a : originalTweet
|
||||
c --> b : originalTweet
|
||||
|
||||
@enduml
|
||||
0
src/main/java/oving4/card/.gitkeep
Normal file
0
src/main/java/oving4/stopwatch/.gitkeep
Normal file
73
src/main/java/oving4/testing/CoffeeCup.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package oving4.testing;
|
||||
|
||||
public class CoffeeCup {
|
||||
|
||||
private double capacity;
|
||||
private double currentVolume;
|
||||
|
||||
public CoffeeCup() {
|
||||
this.capacity = 0.0;
|
||||
this.currentVolume = 0.0;
|
||||
}
|
||||
|
||||
public CoffeeCup(double capacity, double currentVolume) {
|
||||
if (!CoffeeCup.isValidCapacity(capacity)) {
|
||||
throw new IllegalArgumentException("Illegal capacity given.");
|
||||
}
|
||||
this.capacity = capacity;
|
||||
|
||||
if (!this.isValidVolume(currentVolume)) {
|
||||
throw new IllegalArgumentException("Illegal volume given.");
|
||||
}
|
||||
this.currentVolume = currentVolume;
|
||||
}
|
||||
|
||||
public double getCapacity() {
|
||||
return this.capacity;
|
||||
}
|
||||
|
||||
public double getCurrentVolume() {
|
||||
return this.currentVolume;
|
||||
}
|
||||
|
||||
private static boolean isValidCapacity(double capacity) {
|
||||
return capacity >= 0.0;
|
||||
}
|
||||
|
||||
public void increaseCupSize(double biggerCapacity) {
|
||||
if (CoffeeCup.isValidCapacity(biggerCapacity)) {
|
||||
this.capacity += biggerCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidVolume(double volume) {
|
||||
return volume <= this.capacity && volume >= 0.0;
|
||||
}
|
||||
|
||||
private boolean canDrink(double volume) {
|
||||
return volume <= this.currentVolume;
|
||||
}
|
||||
|
||||
public void drinkCoffee(double volume) {
|
||||
if (!this.isValidVolume(volume) || !this.canDrink(volume)) {
|
||||
throw new IllegalArgumentException("You cannot drink that much coffee!");
|
||||
}
|
||||
|
||||
this.currentVolume -= volume;
|
||||
}
|
||||
|
||||
public void fillCoffee(double volume) {
|
||||
if (!this.isValidVolume(this.currentVolume + volume)) {
|
||||
throw new IllegalArgumentException(
|
||||
"You just poured coffee all over the table. Good job.");
|
||||
}
|
||||
|
||||
this.currentVolume += volume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[CoffeeCup capacity=%.2f, currentVolume=%.2f]", this.capacity,
|
||||
this.currentVolume);
|
||||
}
|
||||
}
|
||||
0
src/main/java/oving4/twitter/.gitkeep
Normal file
74
src/test/java/oving4/PartnerTest.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package oving4;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
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 PartnerTest {
|
||||
|
||||
private Partner p1;
|
||||
private Partner p2;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
p1 = new Partner("P1");
|
||||
p2 = new Partner("P2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that the constructor initializes correctly")
|
||||
public void testConstructor() {
|
||||
assertNull(p1.getPartner());
|
||||
assertNull(p2.getPartner());
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Partner(null);
|
||||
}, "Name cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that P1 and P2 are partners after p1.setPartner(p2)")
|
||||
public void simplePartnership() {
|
||||
assertNull(p1.getPartner());
|
||||
assertNull(p2.getPartner());
|
||||
|
||||
p1.setPartner(p2);
|
||||
assertEquals(p1.getPartner(), p2, "P1 should be partner to P2");
|
||||
assertEquals(p2.getPartner(), p1, "P2 should be partner to P1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that one can split up a partnership")
|
||||
public void partnershipWithDivorce() {
|
||||
p1.setPartner(p2);
|
||||
assertEquals(p1.getPartner(), p2, "P1 should be partner to P2");
|
||||
assertEquals(p2.getPartner(), p1, "P2 should be partner to P1");
|
||||
|
||||
p1.setPartner(null);
|
||||
assertNull(p1.getPartner());
|
||||
assertNull(p2.getPartner());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that combined breakup followed by the creation of a new partnership works")
|
||||
public void swinger() {
|
||||
Partner p3 = new Partner("P3");
|
||||
Partner p4 = new Partner("P4");
|
||||
|
||||
p1.setPartner(p2);
|
||||
p3.setPartner(p4);
|
||||
assertEquals(p1.getPartner(), p2, "P1 should be the partner of P2");
|
||||
assertEquals(p2.getPartner(), p1, "P2 should be the partner of P1");
|
||||
assertEquals(p3.getPartner(), p4, "P3 should be the partner of P4");
|
||||
assertEquals(p4.getPartner(), p3, "P4 should be the partner of P3");
|
||||
|
||||
p1.setPartner(p4);
|
||||
assertEquals(p1.getPartner(), p4, "P4 should be the partner of P1");
|
||||
assertEquals(p4.getPartner(), p1, "P1 should be the partner of P4");
|
||||
assertNull(p2.getPartner());
|
||||
assertNull(p3.getPartner());
|
||||
}
|
||||
}
|
||||
333
src/test/java/oving4/PersonTest.java
Normal file
@@ -0,0 +1,333 @@
|
||||
package oving4;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class PersonTest {
|
||||
|
||||
private Person anne;
|
||||
private Person hallvard;
|
||||
private Person jens;
|
||||
private Person marit;
|
||||
|
||||
private static void hasChildren(Person person, Collection<Person> children) {
|
||||
assertEquals(children.size(), person.getChildCount());
|
||||
|
||||
for (Person child : children) {
|
||||
boolean found = false;
|
||||
int i = 0;
|
||||
|
||||
while (i < person.getChildCount()) {
|
||||
if (child == person.getChild(i)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
assertTrue(found);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
anne = new Person("Anne", 'F');
|
||||
hallvard = new Person("Hallvard", 'M');
|
||||
jens = new Person("Jens", 'M');
|
||||
marit = new Person("Marit", 'F');
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Constructor")
|
||||
public void testConstructor() {
|
||||
assertEquals("Anne", anne.getName());
|
||||
assertEquals('F', anne.getGender());
|
||||
assertEquals(0, anne.getChildCount());
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Person(null, 'M');
|
||||
}, "Name cannot be null");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Person("Anne", 'X');
|
||||
}, "X is not a valid gender");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Child cannot be null")
|
||||
public void testAddChildException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
anne.addChild(null);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Woman cannot be father")
|
||||
public void testFatherException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
jens.setFather(marit);
|
||||
});
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
anne.setFather(marit);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Man cannot be mother")
|
||||
public void testMotherException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
jens.setMother(hallvard);
|
||||
});
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
anne.setMother(hallvard);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Man cannot be his own father")
|
||||
public void testSelfFatherException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
jens.setFather(jens);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Woman cannot be her own mother")
|
||||
public void testSelfMotherException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
anne.setMother(anne);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Setting father with setFather")
|
||||
public void testSetFather() {
|
||||
jens.setFather(hallvard);
|
||||
|
||||
// Check state of Hallvard
|
||||
assertEquals(null, hallvard.getFather());
|
||||
assertEquals(null, hallvard.getMother());
|
||||
PersonTest.hasChildren(hallvard, List.of(jens));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(hallvard, jens.getFather());
|
||||
assertEquals(null, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
anne.setFather(hallvard);
|
||||
|
||||
// Check state of Hallvard
|
||||
assertEquals(null, hallvard.getFather());
|
||||
assertEquals(null, hallvard.getMother());
|
||||
PersonTest.hasChildren(hallvard, List.of(jens, anne));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(hallvard, jens.getFather());
|
||||
assertEquals(null, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
// Check state of Anne
|
||||
assertEquals(hallvard, anne.getFather());
|
||||
assertEquals(null, anne.getMother());
|
||||
assertEquals(0, anne.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Setting father with addChild")
|
||||
public void testFatherAddChild() {
|
||||
hallvard.addChild(jens);
|
||||
|
||||
// Check state of Hallvard
|
||||
assertEquals(null, hallvard.getFather());
|
||||
assertEquals(null, hallvard.getMother());
|
||||
PersonTest.hasChildren(hallvard, List.of(jens));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(hallvard, jens.getFather());
|
||||
assertEquals(null, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
hallvard.addChild(anne);
|
||||
|
||||
// Check state of Hallvard
|
||||
assertEquals(null, hallvard.getFather());
|
||||
assertEquals(null, hallvard.getMother());
|
||||
PersonTest.hasChildren(hallvard, List.of(jens, anne));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(hallvard, jens.getFather());
|
||||
assertEquals(null, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
// Check state of Anne
|
||||
assertEquals(hallvard, anne.getFather());
|
||||
assertEquals(null, anne.getMother());
|
||||
assertEquals(0, anne.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Setting mother with setMother")
|
||||
public void testSetMother() {
|
||||
jens.setMother(marit);
|
||||
|
||||
// Check state of Marit
|
||||
assertEquals(null, marit.getFather());
|
||||
assertEquals(null, marit.getMother());
|
||||
PersonTest.hasChildren(marit, List.of(jens));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(null, jens.getFather());
|
||||
assertEquals(marit, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
anne.setMother(marit);
|
||||
|
||||
// Check state of Marit
|
||||
assertEquals(null, marit.getFather());
|
||||
assertEquals(null, marit.getMother());
|
||||
PersonTest.hasChildren(marit, List.of(jens, anne));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(null, jens.getFather());
|
||||
assertEquals(marit, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
// Check state of Anne
|
||||
assertEquals(null, anne.getFather());
|
||||
assertEquals(marit, anne.getMother());
|
||||
assertEquals(0, anne.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Setting mother with addChild")
|
||||
public void testMotherAddChild() {
|
||||
marit.addChild(jens);
|
||||
|
||||
// Check state of Marit
|
||||
assertEquals(null, marit.getFather());
|
||||
assertEquals(null, marit.getMother());
|
||||
PersonTest.hasChildren(marit, List.of(jens));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(null, jens.getFather());
|
||||
assertEquals(marit, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
marit.addChild(anne);
|
||||
|
||||
// Check state of Marit
|
||||
assertEquals(null, marit.getFather());
|
||||
assertEquals(null, marit.getMother());
|
||||
PersonTest.hasChildren(marit, List.of(jens, anne));
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(null, jens.getFather());
|
||||
assertEquals(marit, jens.getMother());
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
// Check state of Anne
|
||||
assertEquals(null, anne.getFather());
|
||||
assertEquals(marit, anne.getMother());
|
||||
assertEquals(0, anne.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Change father with setFather")
|
||||
public void testChangeFatherSetFather() {
|
||||
anne.setFather(jens);
|
||||
// Check state of Anne
|
||||
assertEquals(jens, anne.getFather());
|
||||
|
||||
// Check state of Jens
|
||||
PersonTest.hasChildren(jens, List.of(anne));
|
||||
|
||||
anne.setFather(hallvard);
|
||||
|
||||
// Check state of Anne
|
||||
assertEquals(hallvard, anne.getFather());
|
||||
|
||||
// Check state of Jens
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
// Check state of Hallvard
|
||||
PersonTest.hasChildren(hallvard, List.of(anne));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Change father with addChild")
|
||||
public void testChangeFatherAddChild() {
|
||||
jens.addChild(anne);
|
||||
|
||||
// Check state of anne
|
||||
assertEquals(jens, anne.getFather());
|
||||
|
||||
// Check state of jens
|
||||
PersonTest.hasChildren(jens, List.of(anne));
|
||||
|
||||
hallvard.addChild(anne);
|
||||
|
||||
// Check state of anne
|
||||
assertEquals(hallvard, anne.getFather());
|
||||
|
||||
// Check state of jens
|
||||
assertEquals(0, jens.getChildCount());
|
||||
|
||||
// Check state of hallvard
|
||||
PersonTest.hasChildren(hallvard, List.of(anne));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Change mother with setMother")
|
||||
public void testChangeMotherSetMother() {
|
||||
jens.setMother(anne);
|
||||
|
||||
// Check state of jens
|
||||
assertEquals(anne, jens.getMother());
|
||||
|
||||
// Check state of anne
|
||||
PersonTest.hasChildren(anne, List.of(jens));
|
||||
|
||||
jens.setMother(marit);
|
||||
|
||||
// Check state of jens
|
||||
assertEquals(marit, jens.getMother());
|
||||
|
||||
// Check state of anne
|
||||
assertEquals(0, anne.getChildCount());
|
||||
|
||||
// Check state of marit
|
||||
PersonTest.hasChildren(marit, List.of(jens));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Change mother with addChild")
|
||||
public void testChangeMotherAddChild() {
|
||||
anne.addChild(jens);
|
||||
|
||||
// Check state of jens
|
||||
assertEquals(anne, jens.getMother());
|
||||
|
||||
// Check state of anne
|
||||
PersonTest.hasChildren(anne, List.of(jens));
|
||||
|
||||
marit.addChild(jens);
|
||||
|
||||
// Check state of jens
|
||||
assertEquals(marit, jens.getMother());
|
||||
|
||||
// Check state of anne
|
||||
assertEquals(0, anne.getChildCount());
|
||||
|
||||
// Check state of marit
|
||||
PersonTest.hasChildren(marit, List.of(jens));
|
||||
}
|
||||
}
|
||||
59
src/test/java/oving4/card/CardDeckTest.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package oving4.card;
|
||||
|
||||
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 CardDeckTest {
|
||||
|
||||
private CardDeck cardDeck;
|
||||
|
||||
private static void checkDeck(CardDeck deck, String deckAsString) {
|
||||
String[] toStrings = deckAsString.split(",");
|
||||
assertEquals(toStrings.length, deck.getCardCount(),
|
||||
"CardDeck does not have the correct size");
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (String toString : toStrings) {
|
||||
Card card = deck.getCard(i);
|
||||
String cardString = String.valueOf(card.getSuit()) + card.getFace();
|
||||
assertEquals(toString, cardString,
|
||||
String.format("Card at position %d was incorrect. CardDeck should have been %s",
|
||||
i + 1, toStrings));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
cardDeck = new CardDeck(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that CardDeck is initialized to S1,S2,H1,H2,D1,D2,C1,C2")
|
||||
public void testConstructor() {
|
||||
CardDeckTest.checkDeck(cardDeck, "S1,S2,H1,H2,D1,D2,C1,C2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that CardDeck is shuffled to S1,D1,S2,D2,H1,C1,H2,C2")
|
||||
public void testShufflePerfectly() {
|
||||
cardDeck.shufflePerfectly();
|
||||
CardDeckTest.checkDeck(cardDeck, "S1,D1,S2,D2,H1,C1,H2,C2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that deal gives out the last three cards")
|
||||
public void testDeal() {
|
||||
CardHand hand = new CardHand();
|
||||
cardDeck.deal(hand, 3);
|
||||
CardDeckTest.checkDeck(cardDeck, "S1,S2,H1,H2,D1");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
cardDeck.deal(null, 1);
|
||||
}, "Cannot deal to null");
|
||||
}
|
||||
}
|
||||
75
src/test/java/oving4/card/CardHandTest.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package oving4.card;
|
||||
|
||||
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 CardHandTest {
|
||||
|
||||
private CardHand cardHand;
|
||||
|
||||
private static void checkHand(CardHand hand, String deckAsString) {
|
||||
String[] toStrings = deckAsString.split(",");
|
||||
assertEquals(toStrings.length, hand.getCardCount(),
|
||||
"The number of cards in the hand was incorrect");
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (String toString : toStrings) {
|
||||
Card card = hand.getCard(i);
|
||||
String cardString = String.valueOf(card.getSuit()) + card.getFace();
|
||||
assertEquals(toString, cardString, String.format(
|
||||
"The card at position %d was incorrect. The hand should have contained %s",
|
||||
i + 1, toStrings));
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
cardHand = new CardHand();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that CardHand is initialized to empty")
|
||||
public void testConstructor() {
|
||||
CardDeck deck = new CardDeck(2);
|
||||
deck.deal(cardHand, 3);
|
||||
CardHandTest.checkHand(cardHand, "C2,C1,D2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test the addCard method")
|
||||
public void testAddCard() {
|
||||
CardDeck deck = new CardDeck(2);
|
||||
deck.deal(cardHand, 3);
|
||||
CardHandTest.checkHand(cardHand, "C2,C1,D2");
|
||||
|
||||
cardHand.addCard(new Card('H', 1));
|
||||
CardHandTest.checkHand(cardHand, "C2,C1,D2,H1");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
cardHand.addCard(null);
|
||||
}, "Cannot add null card");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test the deal and play methods")
|
||||
public void testDealPlay() {
|
||||
CardDeck deck = new CardDeck(2);
|
||||
deck.deal(cardHand, 3);
|
||||
CardHandTest.checkHand(cardHand, "C2,C1,D2");
|
||||
|
||||
cardHand.play(1);
|
||||
CardHandTest.checkHand(cardHand, "C2,D2");
|
||||
|
||||
cardHand.play(0);
|
||||
CardHandTest.checkHand(cardHand, "D2");
|
||||
|
||||
cardHand.play(0);
|
||||
assertEquals(cardHand.getCardCount(), 0);
|
||||
}
|
||||
}
|
||||
46
src/test/java/oving4/card/CardTest.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package oving4.card;
|
||||
|
||||
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 CardTest {
|
||||
|
||||
private static void checkCard(Card card, char suit, int face) {
|
||||
assertEquals(card.getSuit(), suit);
|
||||
assertEquals(card.getFace(), face);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that the constructor creates Card objects with correct values")
|
||||
public void testConstructor() {
|
||||
CardTest.checkCard(new Card('S', 1), 'S', 1);
|
||||
CardTest.checkCard(new Card('S', 13), 'S', 13);
|
||||
CardTest.checkCard(new Card('H', 1), 'H', 1);
|
||||
CardTest.checkCard(new Card('H', 13), 'H', 13);
|
||||
CardTest.checkCard(new Card('D', 1), 'D', 1);
|
||||
CardTest.checkCard(new Card('D', 13), 'D', 13);
|
||||
CardTest.checkCard(new Card('C', 1), 'C', 1);
|
||||
CardTest.checkCard(new Card('C', 13), 'C', 13);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Card('X', 1);
|
||||
}, "Should not be able to create a card of type X");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Card('S', 0);
|
||||
}, "Should not be able to create a card with value 0");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Card('C', 14);
|
||||
}, "Should not be able to create a card with value 14");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that toString works as expected")
|
||||
public void testToString() {
|
||||
assertEquals("S1", new Card('S', 1).toString());
|
||||
assertEquals("H13", new Card('H', 13).toString());
|
||||
}
|
||||
}
|
||||
111
src/test/java/oving4/stopwatch/StopWatchManagerTest.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package oving4.stopwatch;
|
||||
|
||||
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 StopWatchManagerTest {
|
||||
|
||||
private StopWatchManager manager;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
manager = new StopWatchManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Create new StopWatch")
|
||||
public void testNewStopWatch() {
|
||||
StopWatch sw1 = manager.newStopWatch("SW1");
|
||||
StopWatch sw2 = manager.newStopWatch("SW2");
|
||||
assertEquals(sw1, manager.getStopWatch("SW1"));
|
||||
assertEquals(sw2, manager.getStopWatch("SW2"));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
manager.newStopWatch(null);
|
||||
}, "Name cannot be null");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
manager.newStopWatch("SW1");
|
||||
}, "Name already exists");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Ticker")
|
||||
public void testTicks() {
|
||||
StopWatch sw1 = manager.newStopWatch("SW1");
|
||||
StopWatch sw2 = manager.newStopWatch("SW2");
|
||||
|
||||
manager.tick(1);
|
||||
assertEquals(1, sw1.getTicks());
|
||||
assertEquals(1, sw2.getTicks());
|
||||
|
||||
manager.tick(4);
|
||||
assertEquals(5, sw1.getTicks());
|
||||
assertEquals(5, sw2.getTicks());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Remove StopWatches")
|
||||
public void testRemoveStopWatches() {
|
||||
assertEquals(0, manager.getAllWatches().size());
|
||||
|
||||
StopWatch sw1 = manager.newStopWatch("SW1");
|
||||
assertEquals(1, manager.getAllWatches().size());
|
||||
assertEquals(sw1, manager.getStopWatch("SW1"));
|
||||
|
||||
StopWatch sw2 = manager.newStopWatch("SW2");
|
||||
assertEquals(2, manager.getAllWatches().size());
|
||||
assertEquals(sw1, manager.getStopWatch("SW1"));
|
||||
assertEquals(sw2, manager.getStopWatch("SW2"));
|
||||
|
||||
manager.removeStopWatch("SW1");
|
||||
assertEquals(1, manager.getAllWatches().size());
|
||||
assertEquals(null, manager.getStopWatch("SW1"));
|
||||
|
||||
manager.removeStopWatch("SW2");
|
||||
assertEquals(0, manager.getAllWatches().size());
|
||||
assertEquals(null, manager.getStopWatch("SW1"));
|
||||
assertEquals(null, manager.getStopWatch("SW2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Starting and stopping StopWatches")
|
||||
public void testStartedStoppedWatches() {
|
||||
assertEquals(0, manager.getStartedWatches().size());
|
||||
|
||||
manager.newStopWatch("SW1").start();
|
||||
assertEquals(1, manager.getStartedWatches().size());
|
||||
assertEquals(0, manager.getStoppedWatches().size());
|
||||
assertTrue(manager.getStartedWatches().contains(manager.getStopWatch("SW1")));
|
||||
assertTrue(manager.getStopWatch("SW1").isStarted());
|
||||
|
||||
manager.newStopWatch("SW2").start();
|
||||
assertEquals(2, manager.getStartedWatches().size());
|
||||
assertEquals(0, manager.getStoppedWatches().size());
|
||||
assertTrue(manager.getStartedWatches().contains(manager.getStopWatch("SW1")));
|
||||
assertTrue(manager.getStopWatch("SW1").isStarted());
|
||||
assertFalse(manager.getStopWatch("SW1").isStopped());
|
||||
assertTrue(manager.getStartedWatches().contains(manager.getStopWatch("SW2")));
|
||||
assertTrue(manager.getStopWatch("SW2").isStarted());
|
||||
assertFalse(manager.getStopWatch("SW2").isStopped());
|
||||
|
||||
manager.getStopWatch("SW2").stop();
|
||||
assertEquals(1, manager.getStoppedWatches().size());
|
||||
assertFalse(manager.getStoppedWatches().contains(manager.getStopWatch("SW1")));
|
||||
assertFalse(manager.getStopWatch("SW1").isStopped());
|
||||
assertTrue(manager.getStoppedWatches().contains(manager.getStopWatch("SW2")));
|
||||
assertTrue(manager.getStopWatch("SW2").isStopped());
|
||||
|
||||
manager.getStopWatch("SW1").stop();
|
||||
assertEquals(2, manager.getStoppedWatches().size());
|
||||
assertTrue(manager.getStoppedWatches().contains(manager.getStopWatch("SW1")));
|
||||
assertTrue(manager.getStopWatch("SW1").isStopped());
|
||||
assertTrue(manager.getStoppedWatches().contains(manager.getStopWatch("SW2")));
|
||||
assertTrue(manager.getStopWatch("SW2").isStopped());
|
||||
}
|
||||
}
|
||||
159
src/test/java/oving4/stopwatch/StopWatchTest.java
Normal file
@@ -0,0 +1,159 @@
|
||||
package oving4.stopwatch;
|
||||
|
||||
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("Check that a newly created StopWatch object has correct values")
|
||||
public void testConstructor() {
|
||||
assertFalse(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
assertEquals(0, stopWatch.getTicks());
|
||||
assertEquals(-1, stopWatch.getTime());
|
||||
assertEquals(-1, stopWatch.getLapTime());
|
||||
assertEquals(-1, stopWatch.getLastLapTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that tick() without start does not change the time")
|
||||
public void testTicksWithoutStart() {
|
||||
stopWatch.tick(1);
|
||||
assertEquals(-1, stopWatch.getTime());
|
||||
assertEquals(1, stopWatch.getTicks());
|
||||
|
||||
stopWatch.tick(4);
|
||||
assertEquals(-1, stopWatch.getTime());
|
||||
assertEquals(5, stopWatch.getTicks());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Start and stop the StopWatch and check that the time is correct")
|
||||
public void testStartTickStop() {
|
||||
stopWatch.start();
|
||||
assertEquals(0, stopWatch.getTime());
|
||||
assertEquals(0, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
stopWatch.start();
|
||||
}, "Cannot start an already running StopWatch");
|
||||
|
||||
stopWatch.tick(3);
|
||||
assertEquals(3, stopWatch.getTime());
|
||||
assertEquals(3, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
stopWatch.tick(5);
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(8, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
stopWatch.stop();
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(8, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertTrue(stopWatch.isStopped());
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
stopWatch.stop();
|
||||
}, "Cannot stop a StopWatch that is already stopped");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Start and stop the StopWatch, and call tick() while it is not started")
|
||||
public void testTickStartTickStopTick() {
|
||||
stopWatch.tick(2);
|
||||
assertEquals(-1, stopWatch.getTime());
|
||||
assertEquals(2, stopWatch.getTicks());
|
||||
assertFalse(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
stopWatch.start();
|
||||
assertEquals(0, stopWatch.getTime());
|
||||
assertEquals(2, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
stopWatch.tick(3);
|
||||
assertEquals(3, stopWatch.getTime());
|
||||
assertEquals(5, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
stopWatch.tick(5);
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(10, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertFalse(stopWatch.isStopped());
|
||||
|
||||
stopWatch.stop();
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(10, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertTrue(stopWatch.isStopped());
|
||||
|
||||
stopWatch.tick(3);
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(13, stopWatch.getTicks());
|
||||
assertTrue(stopWatch.isStarted());
|
||||
assertTrue(stopWatch.isStopped());
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
stopWatch.tick(-1);
|
||||
}, "Time should not be able to go backward");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that laps work as expected")
|
||||
public void testLaps() {
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
stopWatch.lap();
|
||||
}, "Should not be able to start a new lap without starting the StopWatch");
|
||||
|
||||
stopWatch.start();
|
||||
assertEquals(0, stopWatch.getTime());
|
||||
assertEquals(0, stopWatch.getLapTime());
|
||||
assertEquals(-1, stopWatch.getLastLapTime());
|
||||
|
||||
stopWatch.tick(3);
|
||||
assertEquals(3, stopWatch.getTime());
|
||||
assertEquals(3, stopWatch.getLapTime());
|
||||
assertEquals(-1, stopWatch.getLastLapTime());
|
||||
|
||||
stopWatch.lap();
|
||||
assertEquals(3, stopWatch.getTime());
|
||||
assertEquals(0, stopWatch.getLapTime());
|
||||
assertEquals(3, stopWatch.getLastLapTime());
|
||||
|
||||
stopWatch.tick(5);
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(5, stopWatch.getLapTime());
|
||||
assertEquals(3, stopWatch.getLastLapTime());
|
||||
|
||||
stopWatch.stop();
|
||||
assertEquals(8, stopWatch.getTime());
|
||||
assertEquals(0, stopWatch.getLapTime());
|
||||
assertEquals(5, stopWatch.getLastLapTime());
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
stopWatch.lap();
|
||||
}, "Should not be able to start a new lap with a stopped StopWatch");
|
||||
}
|
||||
}
|
||||
0
src/test/java/oving4/testing/.gitkeep
Normal file
93
src/test/java/oving4/twitter/TweetTest.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package oving4.twitter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
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 TweetTest {
|
||||
|
||||
private Tweet retweet1;
|
||||
private Tweet tweet;
|
||||
private TwitterAccount kari;
|
||||
private TwitterAccount nils;
|
||||
private TwitterAccount ole;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
nils = new TwitterAccount("Nils");
|
||||
ole = new TwitterAccount("Ole");
|
||||
kari = new TwitterAccount("Kari");
|
||||
tweet = new Tweet(nils, "Kvitre!");
|
||||
retweet1 = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that the constructor initializes correctly")
|
||||
public void testNullInConstructors() {
|
||||
assertEquals("Kvitre!", tweet.getText());
|
||||
assertEquals(nils, tweet.getOwner());
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Tweet(null, "Kvitre!");
|
||||
}, "The tweet must have an owner");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Tweet(nils, (String) null);
|
||||
}, "The tweet must have text");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Tweet(null, tweet);
|
||||
}, "The tweet must have an owner");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Tweet(nils, (Tweet) null);
|
||||
}, "The tweet must have an original tweet");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorNewTweet() {
|
||||
assertEquals("Kvitre!", tweet.getText(),
|
||||
"The constructor initialized the tweet with incorrect text");
|
||||
assertEquals(nils, tweet.getOwner(),
|
||||
"The constructor initialized the tweet with the wrong owner");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that retweet has the same text but a different owner")
|
||||
public void constructorRetweet() {
|
||||
retweet1 = new Tweet(ole, tweet);
|
||||
assertEquals("Kvitre!", retweet1.getText());
|
||||
assertEquals(ole, retweet1.getOwner());
|
||||
|
||||
assertThrows(RuntimeException.class, () -> {
|
||||
new Tweet(nils, tweet);
|
||||
}, "A person should not be able to retweet themselves");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that the original tweet is always correct")
|
||||
public void getOriginalTweet() {
|
||||
assertNull(tweet.getOriginalTweet());
|
||||
|
||||
retweet1 = new Tweet(ole, tweet);
|
||||
assertEquals(tweet, retweet1.getOriginalTweet());
|
||||
assertEquals(retweet1.getOriginalTweet().getText(), retweet1.getText());
|
||||
assertEquals(tweet, retweet1.getOriginalTweet());
|
||||
assertEquals(retweet1.getOriginalTweet().getText(), retweet1.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that retweet count increases when a tweet is retweeted")
|
||||
public void getRetweetCount() {
|
||||
assertEquals(0, tweet.getRetweetCount());
|
||||
|
||||
new Tweet(ole, tweet);
|
||||
assertEquals(1, tweet.getRetweetCount());
|
||||
|
||||
new Tweet(kari, tweet);
|
||||
assertEquals(2, tweet.getRetweetCount());
|
||||
}
|
||||
}
|
||||
152
src/test/java/oving4/twitter/TwitterAccountTest.java
Normal file
@@ -0,0 +1,152 @@
|
||||
package oving4.twitter;
|
||||
|
||||
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 TwitterAccountTest {
|
||||
|
||||
private TwitterAccount nils;
|
||||
private TwitterAccount ole;
|
||||
|
||||
private static void checkFollow(TwitterAccount accountA, TwitterAccount accountB,
|
||||
boolean AfollowsB, boolean BfollowsA) {
|
||||
if (AfollowsB) {
|
||||
assertTrue(accountA.isFollowing(accountB), String.format("%s should have followed %s",
|
||||
accountA.getUserName(), accountB.getUserName()));
|
||||
assertTrue(accountB.isFollowedBy(accountA),
|
||||
String.format("%s should have been followed by %s", accountB.getUserName(),
|
||||
accountA.getUserName()));
|
||||
} else {
|
||||
assertFalse(accountA.isFollowing(accountB),
|
||||
String.format("%s should not have followed %s", accountA.getUserName(),
|
||||
accountB.getUserName()));
|
||||
assertFalse(accountB.isFollowedBy(accountA),
|
||||
String.format("%s should not have been followed by %s", accountB.getUserName(),
|
||||
accountA.getUserName()));
|
||||
}
|
||||
if (BfollowsA) {
|
||||
assertTrue(accountB.isFollowing(accountA), String.format("%s should have followed %s",
|
||||
accountB.getUserName(), accountA.getUserName()));
|
||||
assertTrue(accountA.isFollowedBy(accountB),
|
||||
String.format("%s should have been followed by %s", accountA.getUserName(),
|
||||
accountB.getUserName()));
|
||||
} else {
|
||||
assertFalse(accountB.isFollowing(accountA),
|
||||
String.format("%s should not have followed %s", accountB.getUserName(),
|
||||
accountA.getUserName()));
|
||||
assertFalse(accountA.isFollowedBy(accountB),
|
||||
String.format("%s should not have been followed by %s", accountA.getUserName(),
|
||||
accountB.getUserName()));
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
nils = new TwitterAccount("Nils");
|
||||
ole = new TwitterAccount("Ole");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that the constructor initializes correctly")
|
||||
public void testConstructor() {
|
||||
assertEquals("Nils", nils.getUserName());
|
||||
assertEquals(0, nils.getTweetCount());
|
||||
assertEquals("Ole", ole.getUserName());
|
||||
assertEquals(0, ole.getTweetCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test that follow is implemented correctly")
|
||||
public void testFollow() {
|
||||
nils.follow(ole);
|
||||
TwitterAccountTest.checkFollow(nils, ole, true, false);
|
||||
|
||||
ole.follow(nils);
|
||||
TwitterAccountTest.checkFollow(nils, ole, true, true);
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
nils.follow(nils);
|
||||
}, "Should not be able to follow oneself");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
nils.follow(null);
|
||||
}, "Should not be able to follow null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnfollow() {
|
||||
TwitterAccountTest.checkFollow(nils, ole, false, false);
|
||||
|
||||
nils.follow(ole);
|
||||
TwitterAccountTest.checkFollow(nils, ole, true, false);
|
||||
|
||||
nils.unfollow(ole);
|
||||
TwitterAccountTest.checkFollow(nils, ole, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewTweet() {
|
||||
nils.tweet("Kvitre!");
|
||||
assertEquals(1, nils.getTweetCount(), "The tweet count of Nils should be 1");
|
||||
assertEquals("Kvitre!", nils.getTweet(1).getText(), "The text should have been 'Kvitre'");
|
||||
|
||||
nils.tweet("Kvitre igjen!");
|
||||
assertEquals(2, nils.getTweetCount());
|
||||
assertEquals("Kvitre igjen!", nils.getTweet(1).getText());
|
||||
assertEquals("Kvitre!", nils.getTweet(2).getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIllegalTweet() {
|
||||
assertThrows(RuntimeException.class, () -> {
|
||||
nils.getTweet(1);
|
||||
}, "Should not be able to get a tweet that does not exist");
|
||||
|
||||
assertThrows(RuntimeException.class, () -> {
|
||||
nils.getTweet(-1);
|
||||
}, "Should not be able to get a tweet that does not exist");
|
||||
|
||||
nils.tweet("Kvitre!");
|
||||
|
||||
assertThrows(RuntimeException.class, () -> {
|
||||
nils.getTweet(2);
|
||||
}, "Should not be able to get a tweet that does not exist");
|
||||
|
||||
assertThrows(RuntimeException.class, () -> {
|
||||
nils.getTweet(-1);
|
||||
}, "Should not be able to get a tweet that does not exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Check that the retweet count is correct, also when retweeting a retweet")
|
||||
public void testRetweet() {
|
||||
TwitterAccount kari = new TwitterAccount("Kari");
|
||||
|
||||
nils.tweet("Kvitre!");
|
||||
assertEquals(1, nils.getTweetCount());
|
||||
assertEquals("Kvitre!", nils.getTweet(1).getText());
|
||||
|
||||
ole.retweet(nils.getTweet(1));
|
||||
assertEquals(1, nils.getTweetCount());
|
||||
assertEquals(1, nils.getRetweetCount());
|
||||
assertEquals(1, ole.getTweetCount());
|
||||
assertEquals(0, ole.getRetweetCount());
|
||||
assertEquals("Kvitre!", ole.getTweet(1).getText());
|
||||
assertEquals(nils.getTweet(1), ole.getTweet(1).getOriginalTweet());
|
||||
|
||||
kari.retweet(ole.getTweet(1));
|
||||
assertEquals(1, nils.getTweetCount());
|
||||
assertEquals(2, nils.getRetweetCount());
|
||||
assertEquals(1, ole.getTweetCount());
|
||||
assertEquals(0, ole.getRetweetCount());
|
||||
assertEquals(1, kari.getTweetCount());
|
||||
assertEquals(0, kari.getRetweetCount());
|
||||
assertEquals("Kvitre!", kari.getTweet(1).getText());
|
||||
assertEquals(nils.getTweet(1), kari.getTweet(1).getOriginalTweet());
|
||||
}
|
||||
}
|
||||