Compare commits

9 Commits
main ... master

Author SHA1 Message Date
361cee798c oving4 2026-02-09 20:07:16 +01:00
661cb35325 Merge branch 'main' 2026-02-09 17:08:59 +01:00
8e08785db0 øving3 2026-02-09 17:03:32 +01:00
Andreas Omholt Olsen
250553c438 Merge pull request #6 from tdt4100/add-oving5
Add oving 5
2026-02-09 15:30:55 +01:00
Andreas Omholt Olsen
55c36a603a Add oving 5 2026-02-09 15:28:09 +01:00
af8cbf6768 update nix development shell 2026-02-04 01:40:21 +01:00
Andreas Omholt Olsen
6193e26ba1 Merge pull request #5 from tdt4100/add-oving4
Add oving 4
2026-02-02 11:00:40 +01:00
Andreas Omholt Olsen
7dd68c1ed8 Add oving 4 2026-02-02 10:57:55 +01:00
a732c16c12 initial solutions 2026-01-30 01:44:23 +01:00
98 changed files with 4093 additions and 51 deletions

3
.gitignore vendored
View File

@@ -14,4 +14,5 @@ target/
out/
# JDT-specific (Eclipse Java Development Tools)
# .classpath
.classpath
.project

View File

@@ -8,3 +8,5 @@ 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 |
| [Øving 5](./oppgavetekster/oving5/README.md) | Grensesnitt |

27
flake.lock generated Normal file
View File

@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1770141374,
"narHash": "sha256-yD4K/vRHPwXbJf5CK3JkptBA6nFWUKNX/jlFp2eKEQc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "41965737c1797c1d83cfb0b644ed0840a6220bd1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

20
flake.nix Normal file
View File

@@ -0,0 +1,20 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs =
{ nixpkgs, ... }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
in
{
devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [
maven
javaPackages.compiler.openjdk25
];
};
};
}

View 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).

View 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**:
![partner1](./img/partner1.png)
**Etter kall**:
![partner2](./img/partner2.png)
### 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**:
![partner2](./img/partner2.png)
**Etter kall**:
![partner1](./img/partner1.png)
### 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**:
![partner3](./img/partner3.png)
**Etter kall**:
![partner4](./img/partner4.png)
## 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).

View 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**:
![person1](./img/person1.png)
**Etter kall**:
![person2](./img/person2.png)
### 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**:
![person1](./img/person1.png)
**Etter kall**:
![person2](./img/person2.png)
### 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**:
![person2](./img/person2.png)
**Etter kall**:
![person1](./img/person1.png)
### 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**:
![person2](./img/person2.png)
**Etter kall**:
![person1](./img/person1.png)
### 5. Fjerning og oppretting av kobling med `setMother` og `setFather`, en slags "adoption"
**Kall**:
`jens.setFather(torkel)`
`jens.setMother(jorunn)`
**Før kall**:
![person3](./img/person3.png)
**Etter kall**:
![person4](./img/person4.png)
## 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).

View 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.

View 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).

View 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:
![twitter1](./img/twitter1.png)
**Feil objektstrutur**, når B er en retweet av A og C er en retweet av B:
![twitter2](./img/twitter2.png)
## 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?

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,8 @@
@startuml partner1
skinparam dpi 400
object "~#p1:Partner" as p1
object "~#p2:Partner" as p2
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View 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

View File

@@ -0,0 +1,45 @@
# Interface - BinaryComputingIterator
Denne oppgaven handler om en meta-iterator som kombinerer par av verdier fra to iteratorer til en ny iterator-sekvens ved hjelp av en gitt binær operasjon.
Meta-iteratorer er iteratorer som bruker andre iteratorer som datakilder.
Du skal lage en ny klasse `BinaryComputingIterator`, som er en *implementasjon* av det innebygde `Iterator<Double>`-grensesnittet. Konstruktøren til `BinaryComputingIterator` skal ta inn to iteratorer og en binær operator, samt to valgfrie standardverdier. Når `next()`-metoden til `BinaryComputingIterator` blir kalt, skal en verdi hentes fra hver av de to iteratorene, den binære operasjonen utføres på disse to verdiene og resultatet returneres. Den binære operasjonen skal spesifiseres ved hjelp av det innebygde `BinaryOperator<Double>`-grensesnittet, som har en `apply`-metode som lar en beregne resultatet av den binære operasjonen. Hvordan en kan opprette funksjonelle grensesnitt kan du lese mer om på [wikisiden om dette](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8).
## BinaryComputingIterator
Klassen skal ha to konstruktører, en med og en uten standardverdier:
- `BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, BinaryOperator<Double> operator)`. Ingen av argumentene kan være `null`.
- `BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, Double default1, Double default2, BinaryOperator<Double> operator)`.
Her er `iterator1` og `iterator2` iteratorene som blir kombinert av `BinaryComputingIterator`, og `default1` og `default2` er standardverdier for de respektive iteratorene. Klassen må ha følgende metoder, begge spesifisert av `Iterator<Double>`-grensesnittet: Hvordan en lager en `BinaryOperator` klasse kan du se mer om lenger ned i oppgaveteksten.
- `boolean hasNext()` - returnerer true dersom det kan beregnes flere verdier, altså hvis begge iteratorene enten har flere verdier eller har en tilhørende standardverdi. Men merk at `hasNext()` returnerer uansett `false` hvis begge iteratorene er tomme.
- `Double next()` - returnerer resultatet av å bruke binæroperasjonen operator på de neste verdiene fra sub-iteratorene, og bruker standardverdier dersom en av iteratorene ikke har flere verdier.
Husk at ved hjelp av Java 8-syntaks kan en implementasjon av BinaryOperator skrives som `(num1, num2) -> <uttrykk>`, der `<uttrykk>` er et Java-uttrykk som brukes `num1` og `num2`. Hvis BinaryComputerIterator henter verdier fra to iteratorer med hhv. verdiene `1`, `2`, `3` og `3`, `4`, `5` og den binære operatoren er `(num1, num2) -> num1 + num2`, så skal sekvensen en får ut være `4`, `6`, `8`.
For å håndtere tilfellet hvor den ene iteratoren gir ut flere verdier enn den andre, så skal det være mulig å gi standardverdier hhv. `default1` og `default2` for `iterator1` og `iterator2`, som vil bli brukt for å fylle inn manglende verdier. Hvis `BinaryComputerIterator` henter verdier fra to iteratorer med hhv. verdiene `6`, `3`, `0` og `3`, `4` og den binære operatoren er `(num1, num2) -> num1 - num2` og `default2` er `2`, så skal sekvensen en får ut være `3`, `-1`, `-2`.
### Eksempel
`BinaryOperator`-implementasjoner kan lett skrive ved hjelp av Java 8 sin funksjonsnotasjon. Dersom man for eksempel vil bruke en addisjonsoperator kan det se slik ut:
```java
Iterator<Double> iterator1 = List.of(2.0, 3.0).iterator();
Iterator<Double> iterator2 = List.of(5.0).iterator();
BinaryOperator<Double> addition = (a, b) -> a + b;
// Opprett en ny BinaryComputingIterator som tar inn iterator1 og iterator2 og utfører addisjon
// på verdiene.
BinaryComputingIterator binaryIterator = new BinaryComputingIterator(iterator1, iterator2, null, 10.0, addition);
binaryIterator.next(); // 7.0
binaryIterator.hasNext(); // true
binaryIterator.next(); // 13.0
binaryIterator.hasNext(); // false
```
Testkode for BinaryComputingIterator er her: [oving5/BinaryComputingIteratorTest.java](../../src/test/java/oving5/BinaryComputingIteratorTest.java).

View File

@@ -0,0 +1,38 @@
# Interface - CardComparison-oppgave
Denne oppgaven hander om to måter å håndtere sortering av `Card`-objekter, med grensesnittene `Comparable` og `Comparator`, som er innebygd i Java (`java.util.Comparable`, `java.util.Comparator`).
Vi tar i denne oppgaven utgangspunkt i `Card`-klassen fra [Innkapsling - Card-oppgave](../oving3/Card.md). Et `Card`-objekt har en kortfarge (av typen `char`) og verdi (`int`), og sortering gjøres på disse verdiene, ved at en først sorterer på kortfarge og så på verdi. Siden Java har sorteringsmetoder innebygd trenger vi ikke bry oss om selve sorteringsalgoritmen. Vi fokuserer her på logikken for *sammenligning* av `Card`-objekter, altså hvilke `Card` som skal komme før/etter andre.
Dersom du ikke har gjort `Card`-oppgaven 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/oving3/card/Card.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 3.
Filen i denne oppgaven skal ligge i [`oving5/card`](../../src/main/java/oving5/card).
## Del 1
La `Card`-klassen implementere `Comparable` med følgende sammenligningslogikk
- `compareTo`-metoden skal sammenligne et kort med et annet, slik at:
- Spar kommer etter hjerter.
- Hjerter kommer etter ruter.
- Ruter kommer etter kløver.
- Ved lik kortfarge skal verdien brukes i stigende rekkefølge, altså $1$ (ess) kommer før $2$, kommer før $3$ osv. til og med $11$ (knekt), $12$ (dame) og $13$ (konge).
Skriv testkode som sorterer kort i en liste vha. `Collections.sort` og `Comparable`-logikken, og verifiser at sammenligningslogikken er riktig implementert.
*Hint:* Returner `-1` for å sette kortet `this` før kortet som blir gitt inn, `0` dersom de er like, og `1` dersom `this` skal komme etter det gitte kortet.
## Del 2
For å kunne sortere `Card`-objekter med annen logikk, så kan en bruke grensesnittet `Comparator`, som er et objekt som kan sammenligne objekter parvise. Implementer en `Comparator` (dvs. lag en klasse som *implements* `Comparator`) kalt `CardComparator`, som kan konfigureres (stilles inn) til å sortere med ess som høyeste kort og med en bestemt kortfarge som trumf, altså en kortfarge som regnes som høyere enn de andre.
- `CardComparator` må ha en konstruktør som tar inn en `boolean` og en `char`. Det første argumentet sier om ess skal regnes som størst (`true`) eller minst (`false`), mens det andre argumentet angir hvilke kortfarge som er trumf. F.eks. skal et `CardComparator`-objekt laget med `new CardComparator(true, ' ')` rangere ess høyere enn konge og bruke standard rangering av kortfarger (siden trumf-argumentet ikke er en av kortfargene), og et `CardComparator`-objekt laget med `new CardComparator(false, 'C')` rangerer ess lavest og kløver (`'C'` = clubs) høyest av kortfargene (de andre kortfargene har standard rangering med spar over hjerter over ruter).
Skriv testkode som sorterer kort i en liste vha. `Collections.sort` og `Comparator-logikken`, og verifiser at sammenligningslogikken er riktig implementert.
Testkode for del 1 og del 2 finner du her: [oving5/card/CardComparatorTest.java](../../src/test/java/oving5/card/CardComparatorTest.java).
## Valgfri Ekstraoppgave
Utvid `CardComparator` slik at den kan konfigureres med en annen rangering av kortfargene, f.eks. slik at kløver er høyere enn ruter.
Merk at denne fortsatt skal overstyres av evt. trumf. Nytten er altså at en kan endre rangeringsrekkefølgen på alle på én gang.

View File

@@ -0,0 +1,30 @@
# Interface - CardContainer-oppgave
Denne oppgaven handler om å lage et felles grensesnitt for `CardDeck`- og `CardHand`-klassene, laget i oppgavene [Innkapsling - Card-oppgave](../oving3/Card.md) og [Objektstrukturer - Card-oppgave del 2](../oving4/Card.md). Her skal du lage og implementere et grensenitt kalt `CardContainer`, som spesifiserer metoder for lesing av samlinger av Card-objekter.
Dersom du ikke har gjort `Card`-oppgaven 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/oving4/card), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 4.
Filene i denne oppgaven skal ligge i [`oving5/card`](../../src/main/java/oving5/card).
## Del 1 - CardContainer interface
Definer et `CardContainer`-grensesnitt, med metodene som er felles for `CardHand` og `CardDeck`:
- `int getCardCount()` - returnerer antall kort som dette objektet inneholder.
- `Card getCard(int n)` - returnerer kort nr. `n` i dette objektet.
Gjør nødvendig endringer i `CardHand`- og `CardDeck`-klassene for å implementere `CardContainer`-grensesnittet.
## Del 2 - Iterator for CardContainer
Lag en klasse kalt `CardContainerIterator`, som er en [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable) for alle klasser som implementerer `CardContainer`-grensesnittet. `CardContainerIterator` må ha en konstruktør som tar inn en instans av (en klasse som implementerer) `CardContainer`. Du kan gjøre dette ved å ta inn et argument av typen `CardContainer`, altså `CardContainerIterator(CardContainer cardContainer) { ... }`.
*Hint*: Merk at `CardContainerIterator` ikke vet om `CardContainer`-objektet er et `CardDeck`-objekt, et `CardHand`-objekt eller et annet objekt som implementerer `CardContainer`. Den har derfor ikke tilgang til de interne listene i `CardHand` og `CardDeck`. Hvilke metoder må alle klasser som implementerer `CardContainer` ha, og hvordan kan disse metodene brukes for å lage en [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)?
Testkode for oppgaven finner du her: [oving5/card/CardContainerIteratorTest.java](../../src/test/java/oving5/card/CardContainerIteratorTest.java).
## Del 3 - Iterable
La `CardContainer`-grensesnittet utvide (`extends`) `Iterable<Card>` og la `iterator()`-metoden som dermed kreves, returnere en korrekt konfigurert instans av `CardContainerIterator`.
Testkode for oppgaven finner du her: [oving5/card/CardDeckTest.java](../../src/test/java/oving5/card/CardDeckTest.java) og [oving5/card/CardHandTest.java](../../src/test/java/oving5/card/CardHandTest.java).

View File

@@ -0,0 +1,19 @@
# Interface - CardPredicate-oppgave
Denne oppgaven handler om hvordan en kan bruke det funksjonelle `Predicate<T>`-grensesnittet, sammen med `CardDeck`-klassen. Vi tar i denne oppgaven utgangspunkt i `CardDeck`-klassen fra [Innkapsling - Card-oppgave](../oving3/Card.md). Et `CardDeck`-objekt har en liste med `Card`-objekter. `Card` har en kortfarge (av typen `char`) og verdi (`int`), og vi ønsker å lage metoder i `CardDeck` som søker opp `Card`-objekter som tilfredsstiller visse kriterier, f.eks. sjekker om spar dame finnes, teller antall hjerter eller henter ut alle ess. For å representere selve kriteriet brukes [`Predicate<T>`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/function/Predicate.html)-grensesnittet, som handler om å teste/sjekke om et objekt tilfredsstiller visse kriterium.
Dersom du ikke har gjort `Card`-oppgaven 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/oving3/card/Card.java), som kommer til å være tilgjengelig etter siste demonstrasjonsfrist for øving 3.
Filen i denne oppgaven skal ligge i [`oving5/card`](../../src/main/java/oving5/card).
**Implementer** følgende metoder i `CardDeck`-klassen:
- `List<Card> getCards(Predicate<Card> predicate)` - Skal returnere en liste med de kortene som tilfredsstiller `predicate`. Argumentet kan ikke være `null` for noen av metodene.
- `int getCardCount(Predicate<Card> predicate)` - Skal returnere hvor mange kort som tilfredsstiller `predicate`.
- `boolean hasCard(Predicate<Card> predicate)` - Skal returnere `true` dersom det finnes et kort som tilfredsstiller `predicate`, `false` ellers.
Lag også din egen `main()`-metode hvor du prøver hver av de tre metodene over. Du skal altså sjekke om spar dame finnes, telle antall hjerter og hente ut alle ess.
Testkode for oppgaven finner du her: [oving5/card/CardPredicateTest.java](../../src/test/java/oving5/card/CardPredicateTest.java).
Hvordan en kan opprette funksjonelle grensesnitt kan du se på [wikisiden](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8) om dette.

View File

@@ -0,0 +1,38 @@
# Interface - Named-oppgave
Denne oppgaven handler om et grensnitt (interface) for person-navn og hvordan slike implementeres og sorteres med grensesnittet `Comparator`.
I denne oppgaven tar vi utgangspunkt i at en person har et navn (`String`) bestående av fornavn ("given name") og etternavn ("family name") som sammen gir personens fulle navn ("full name").
Filene i denne oppgaven skal ligge i [`oving5/named`](../../src/main/java/oving5/named).
## Del 1
Definer et grensesnitt `Named` med følgende metoder:
- `void setGivenName(String)` og `String getGivenName()` for å sette og hente fornavn.
- `void setFamilyName(String)` og `String getFamilyName()` for å sette og hente etternavn.
- `void setFullName(String)` og `String getFullName()` for å sette og hente personens hele navn. Argumentet til set-metoden skal være fornavn og etternavn skilt med mellomrom. Tilsvarende skal get-metoden returnere fornavn og etternavn skilt med mellomrom.
## Del 2
Lag klassene `Person1` og `Person2` som begge implementerer grensesnittet `Named`. `Person1`-klassen skal ha felter for for- og etternavn (altså `givenName` og `familyName`) og en konstruktør som tar inn to tilsvarende argumenter. `Person2` skal ha ett felt for fullt navn (`fullName`) og en konstruktør som tar inn det fulle navnet. Begge skal imidlertid implementere samme logikk, dvs. ha get- og set-metoder for fornavn, etternavn og fullt navn. Man kan anta at brukeren oppretter `Person1` og `Person2`-objekter med gyldige navn, altså trenger man ikke å implementere valideringsmetoder.
## Del 3
For å kunne sammenligne `Named`-objekter, f.eks. for å sortere en kontaktliste, kan du lage en klasse kalt `NamedComparator`, som implementerer grensesnittet [`Comparator`](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator). `NamedComparator`-objektet skal brukes for å sammenligne navn parvis: Først på etternavn og deretter på fornavn om etternavnene er like. Dette kan gjøres ved å la `NamedComparator`-klassen implementere metoden `int compare(Named named1, Named named2)` med følgende logikk:
- Dersom etternavnene er ulike skal metoden:
- returnere et negativt heltall om det første etternavnet er alfabetisk ordnet før det andre,
- eller et positivt heltall i motsatt tilfelle.
- Dersom etternavnene er like skal metoden gjøre det samme på fornavnene. Dersom også fornavnene er like skal metoden returnere 0.
Skriv testkode som bruker `Collections.sort`-metoden på en `ArrayList<Named>`, for å teste om `NamedComparator`-klassen har implementert [`Comparator`](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator) riktig.
[Hint om sammenligning av strenger](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#compareTo(java.lang.String)).
Testkode for oppgavene finner du her:
- [oving5/named/NamedComparatorTest.java](../../src/test/java/oving5/named/NamedComparatorTest.java),
- [oving5/named/Person1Test.java](../../src/test/java/oving5/named/Person1Test.java),
- [oving5/named/Person2Test.java](../../src/test/java/oving5/named/Person2Test.java).

View File

@@ -0,0 +1,49 @@
# Øving 5: Grensesnitt
## Øvingsmål
- Lære hva grensesnitt er og hvordan disse defineres
- Lære hvordan man implementerer et grensesnitt
- Kjenne til grunnleggende funksjonelle grensesnitt
## Øvingskrav
- Kunne lage grensesnitt og implementere disse med andre klasser
- Kunne bruke grensesnittene `Comparable<T>` og `Comparator<T>`
- Kunne bruke grensesnittene `Iterable<T>` og `Iterator<T>`
## Dette må du gjøre
### Del 1: Programmering
Gjør **minst to** av oppgavene under, for å få 2 poeng må det gjøres **minst fire** av oppgavene under, og minst en av dem må være markert som vanskelig. Oppgavene skal lagres i [`src/main/java/oving5`](../../src/main/java/oving5).
- [TicketControl](./TicketControl.md) (Lett)
- [CardContainer](./CardContainer.md) (Lett)
- [CardPredicate](./CardPredicate.md) (Lett)
- [CardComparison](./CardComparison.md) (Medium)
- [Twitter](./Twitter.md) (Medium)
- [Named](./Named.md) (Medium)
- [BinaryComputingIterator](./BinaryComputingIterator.md) (Vanskelig)
- [StringGrid](./StringGrid.md) (Vanskelig)
- [RPNKalkulator med funksjonelle grensesnitt](./RPNCalc.md) (Vanskelig)
Alle oppgavene er høyst eksamensrelevante. Vi har imidlertid valgt å trappe ned kravene for `Iterator`, så spesielt `StringGrid` går dypere i temaet enn dere kan forvente å se på eksamen.
### Del 2: Debugging
Gjør følgende oppgave om debugging og vis frem løsningen til studass på sal:
- [StringMergingIterator](./StringMergingIterator.md)
### Del 3: Sekvensdiagram
Lag et [sekvensdiagram](https://www.ntnu.no/wiki/display/tdt4100/Sekvensdiagrammer) som viser samhandlingen mellom et `StringMergingIterator`-objekt og dens argumenter. Dvs. lag et [sekvensdiagram](https://www.ntnu.no/wiki/display/tdt4100/Sekvensdiagrammer) som viser hvordan `StringMergingIterator` gjennom metodekall fletter verdiene fra de to gitte iteratorene (som blir tatt inn som argumentene til `StringMergingIterator`-objektet). Du trenger ikke å levere inn diagrammet på Blackboard.
### 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,25 @@
# Interface - RPNKalkulator med funksjonelle grensesnitt
Denne oppgaven handler om å gjøre det enklere å utvide en kalkulator ved å bruke det innebygde funksjonelle grensesnittet `BinaryOperator<T>`, sammen med `RPNCalc`-klassen.
Vi tar i denne oppgaven utgangspunkt i `RPNCalc`-klassen fra [Øving 3 - RPN-kalkulator](../oving3/RPNCalc.md)-oppgave, men det er ikke strengt nødvendig å ha gjort denne på forhånd. Et `RPNCalc`-objekt består av en stack med tall, metoder for å håndtere stacken, og `performOperation(char)` metoden, som utfører en operasjon gitt av parameteret. For eksempel vil kallet `performOperation('*')` fjerne de to øverste tallene fra stacken, multiplisere dem, og pushe resultatet på toppen av stacken. Om operasjonene hardkodes i metoden, vil det være vanskelig å endre hva kalkulatoren kan gjøre under kjøring. Denne oppgaven går derfor ut på å gjøre det mulig å legge til og fjerne operasjoner fra kalkulatoren. Operasjoner på en og to flyttall kan representeres ved bruk av henholdsvis `UnaryOperator<Double>`- og `BinaryOperator<Double>`-grensesnittene, og operasjoner uten operander (f.eks. pi) kan representeres ved bruk av `Supplier<Double>`.
Implementer følgende metoder i `RPNCalc`-klassen:
- `boolean addOperator(char, BinaryOperator<Double>)` - legger til en operator som virker på to tall (f.eks `+`) hvis operatoren ikke allerede er lagt til. Returner `true` hvis den nye operatoren blir lagt til. Operatoren kan ikke være `null`.
- `void removeOperator(char operator)` - fjerner operatoren med tegn `operator`.
Du må også oppdatere `performOperation(char)` til å bruke operatorene som legges til via metodene over. Om man prøver å kalle `performOperation` med en operator som ikke er lagt til skal det utløses et `UnsupportedOperationException`-unntak.
## Valgfri Ekstraoppgave
Utvid programmet til å kunne ta inn operatorer som tar inn et parameter (`UnaryOperator<Double>`, f.eks. `|` (absoluttverdi)) og ingen parametre (`Supplier<Double>`, f.eks. `p` (pi)). Husk at du må håndtere forsøk på å legge til samme operator i flere kategorier (f.eks. om man prøver å legge til `+` som både `UnaryOperator` og `BinaryOperator` må det håndteres på en god måte).
Dette vil innebære å legge til metodene:
- `boolean addOperator(char, UnaryOperator<Double>)`
- `boolean addOperator(char, Supplier<Double>)`
som fungerer på samme måte som `addOperator(char, BinaryOperator<Double>)`, samt å oppdatere resten av koden til å fungere med de forskjellige operatortypene.
Testkode finner du her: [oving5/RPNCalcTest.java](../../src/test/java/oving5/RPNCalcTest.java).

View File

@@ -0,0 +1,73 @@
# Interface - StringGrid-oppgave
Denne oppgaven handler om et grensnitt (interface) for rutenett som holder strenger (StringGrid), hvordan slike implementeres og hvordan en kan iterere gjennom et slikt rutenett ved hjelp av en [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable).
I denne oppgaven tar vi utgangspunkt i et `StringGrid`-grensesnitt som definerer metodene til et rutenett som holder strenger. Et rutenett er firkantet og består av et antall rader og kolonner. Det skal være mulig å spørre rutenettet hvilken streng som er på angitt rad og kolonne i tillegg til å endre strengen på angitt rad og kolonne. Denne oppførselen er oppsummert i det definerte `StringGrid`-grensesnittet under:
Filene i denne oppgaven skal ligge i [`oving5/stringgrid`](../../src/main/java/oving5/stringgrid).
```java
package oving5.stringgrid;
/**
* An interface with methods for managing the content of a String grid. The grid has a number of
* rows (the grid's height) and columns (the grid's width). In each cell in the grid there is a
* String that can be set with the setElement method and read with the getElement method.
*/
public interface StringGrid {
// Returns the number of rows in this StringGrid
int getRowCount();
// Returns the number of columns in this StringGrid
int getColumnCount();
// Returns the String at the given row and column. Throws an IllegalArgumentException if the
// row or column is out of range
String getElement(int row, int column);
// Sets the String at the given row and column. Throws an IllegalArgumentException if the row
// or column is out of range
void setElement(int row, int column, String element);
}
```
Alle klasser som implementerer **StringGrid**-grensesnittet må støtte de fire definerte metodene.
## Del 1 - StringGrid-grensesnitt og implementerende StringGridImpl-klasse
Lag en `StringGridImpl`-klasse som implementerer `StringGrid`-grensesnittet definert over. Merk at grensesnitt ikke kan brukes til å spesifisere konstruktører, så du må selv definere en eller flere egnede konstruktører. Det er imidlertid nødvendig å implementere en konstruktør som tilsvarer den JUnit-testen forventer:
- `StringGridImpl(int rows, int columnCount)` - konstruktør som tar inn antall rader som `rows` og antall kolonner som `columnCount`.
Du står fritt til å velge hvordan metodene definert av grensesnittet skal implementeres så lenge de tilfredsstiller den definerte oppførselen.
**Hint:** Bruk en enkel `ArrayList<String>` eller en dobbel `ArrayList<ArrayList<String>>` (se wiki-siden om [todimensjonale matriser](https://www.ntnu.no/wiki/display/tdt4100/Todimensjonale+matriser)). Du kan også bruke en enkel `String[]` eller en todimensjonal `String[][]` for å lagre strengene. Implementasjonen er helt opp til deg, så lenge den oppfører seg som beskrevet i grensesnittet.
## Del 2 - StringGridIterator-klasse
Det er hensiktmessig å kunne iterere over alle elementene i et rutenett som implementerer grensesnittet `StringGrid`, f.eks. når en bygger en streng i en `toString()`-metode eller skal sjekke om et spill har blitt avsluttet/vunnet. I denne deloppgaven skal du lage en slik [`Iterator`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)-implementasjon, kalt `StringGridIterator`. Denne klassen må implementere grensesnittet `Iterator<String>`, siden `StringGrid` inneholder `String`-objekter. I tillegg til metodene som er definert i `Iterator`-grensesnittet, må `StringGridIterator` ha en konstruktør som tar imot hvilken `StringGrid` det skal itereres over og i hvilken rekkefølge elementene skal returneres i. Disse verdiene må huskes, så koden i `Iterator`-metodene kan brukes dem til å styre iterasjonen. `StringGridIterator`-klassen må altså støtte følgende konstruktør/metoder:
- `StringGridIterator(StringGrid, boolean rowMajor)` - konstruktør som tar inn `StringGrid`-objektet som `StringGridIterator`-klassen skal iterere over i tillegg til en logisk verdi som angir om iterasjonen skal være bortover først (`rowMajor` er `true`) eller nedover først (`rowMajor` er `false`).
- `boolean hasNext()` - returnerer `true` så lenge det er flere `String`-objekter igjen i `StringGrid`-objektet som ikke ennå er blitt iterert over (med andre ord, sjekk om du har kommet til siste rute i rutenettet).
- `String next()` - returnerer det neste `String`-objektet i rutenettet. Hvilken `String` som er den neste, avhenger av hvordan rutenettet skal itereres (med andre ord, hvorvidt om `rowMajor` er `true` eller `false`).
- `void remove()` - denne metoden skal bare utløse et unntak av typen `UnsupportedOperationException` siden det ikke skal være mulig å fjerne `String`-objekter fra rutenettet.
## Del 3 - Iterable-grensesnittet
Endre `StringGrid`-grensesnittet slik at det utvider (med `extends`) [`Iterable<String>`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable). Dette skal gjøre det mulig å skrive for-setningen under, for å gå gjennom alle elementene i rutenettet.
```java
StringGrid stringGrid = ... // her initialiseres stringGrid
// gå gjennom alle elementene i stringGrid
for (String s : stringGrid) {
// gjør noe med s her
}
```
Rekkefølgen som en slik `for`-løkke går gjennom elementene på, skal være hele første rad, så hele andre rad osv. til og med siste rad.
Hva slags følger får det for `StringGridImpl`? Gjør nødvendige endringer i den også, og test at det virker!
Testkode for del 1 og del 2 finner du her: [oving5/stringgrid/StringGridTest.java](../../src/test/java/oving5/stringgrid/StringGridTest.java).

View File

@@ -0,0 +1,22 @@
# Debugging - StringMergingIterator-oppgave
Oppgaven handler om feilsøking ("debugging") av en Iterator-implementasjon ved bruk av [**debuggeren**](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=235996724) i VS Code.
Les først denne artikkelen om bruk av debugger i VS Code om du ikke er kjent med dette: [Kjøring av kode og debugging i VS Code](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=235996724)
Klassen `StringMergingIterator` implementerer grensesnittet [`Iterator<String>`](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable), og leverer annenhver verdi fra to andre iteratorer av typen `Iterator<String>`. Denne iteratoren fletter altså verdiene fra to andre gitte iteratorer, og er altså en meta-iterator. Meta-iteratorer er iteratorer som bruker andre iteratorer som datakilder.
`StringMergingIterator` har følgende konstruktør:
- `StringMergingIterator(Iterator<String> first, Iterator<String> second)`
Siden klassen implementerer `Iterator<String>` har den også følgende metoder:
- `boolean hasNext()` - returnerer `true` dersom iteratoren har flere verdier, `false` dersom det ikke er flere verdier.
- `String next()` - returnerer den neste verdien fra iteratoren, eller utløser et `NoSuchElementException` dersom iteratoren er tom.
I denne oppgaven blir en implementasjon av `StringMergingIterator` sammen med et testprogram utdelt, men i implementasjonen av klassen har vi plantet en eller flere feil. Målet for oppgaven er å finne feilene i implementasjonen ved hjelp av [debuggeren](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=235996724) i VS Code. Kjør programklassen `StringMergingIteratorProgram` i debug-modus, og bruk dette til å finne ut hvor `StringMergeIterator` gjør feil. Dersom programklassen lykkes med å få en flettet strøm med verdier har du funnet alle feilene.
Merk at du *ikke* skal gjøre noen endringer `StringMergingIteratorProgram`, men bruke dette programmet til å teste logikken i `StringMergingIterator`.
Programmet du skal feilsøke er `StringMergingIteratorProgram`, og du finner koden for denne i [oving5/debugging/StringMergingIteratorProgram.java](../../src/main/java/oving5/debugging/StringMergingIteratorProgram.java). Koden for `StringMergingIterator` finner du i [oving5/debugging/StringMergingIterator.java](../../src/main/java/oving5/debugging/StringMergingIterator.java).

View File

@@ -0,0 +1,27 @@
# Interface - TicketControl-oppgave
Denne oppgaven handler om å lage en representasjon av billetter. Du skal lage et grensesnitt for billetter, og to klasser som implementer dette; en som representerer en enkeltbillett og en som representerer en periodebillett.
Filene i denne oppgaven skal ligge i [`oving5/ticket`](../../src/main/java/oving5/ticket).
## Del 1 - Ticket-grensesnitt
Lag et grensesnitt kalt `Ticket`, med følgende metoder:
- `boolean scan()` - Denne metoden kalles hver gang billetten scannes. Dersom billetten er gyldig, skal metoden returnere `true`, ellers `false`. Legg merke til at du **ikke** skal implementere metoden her, du skal bare definere grensesnittet.
Tanken her er at vi kan senere lage en kontrollørklasse som kan bruke `scan()`-metoden for å sjekke om en billett er gyldig.
## Del 2 - SingleTicket-klasse
Lag en klasse kalt `SingleTicket` som implementerer `Ticket`-grensesnittet. `SingleTicket` skal implementere `scan()`-metoden slik at den kun returnerer `true` den første gangen metoden blir kalt.
**Hint:** Du kan bruke en `boolean`-variabel for å holde styr på om billetten er gyldig eller ikke.
Testkode for oppgaven finner du her: [oving5/ticket/SingleTicketTest.java](../../src/test/java/oving5/ticket/SingleTicketTest.java).
## Del 3 - PeriodTicket-klasse
Lag en klasse kalt `PeriodTicket` som implementerer `Ticket`-grensesnittet. `PeriodTicket` skal implementere `scan()`-metoden slik at den returnerer `true` dersom det nåværende tidspunktet er mellom to gitte tidspunkter. `PeriodTicket` skal ha en konstruktør som tar inn to `LocalDateTime`-objekter som representerer start- og sluttidspunktet for gyldigheten til billetten. Legg til validering der det gir mening.
Testkode for oppgaven finner du her: [oving5/ticket/PeriodTicketTest.java](../../src/test/java/oving5/ticket/PeriodTicketTest.java).

View File

@@ -0,0 +1,55 @@
# Interface - Sortering av TwitterAccount-objekter ved bruk av Comparator
Denne oppgaven handler om sortering av `TwitterAccount`-objekter, ved bruk av grensesnittet `Comparator`. Oppgaven illustrerer hvordan man kan sortere objekter av samme klasse på ulike måter, ved hjelp av ulike implementasjoner av `Comparator`.
Vi tar i denne oppgaven utgangspunkt i `TwitterAccount`- og `Tweet`-klassen fra [Øving 4 - Twitter-oppgave](../oving4/Twitter.md). Et `TwitterAccount`-objekt har et brukernavn, en liste over andre brukere som følges, en liste over brukere som følger denne brukeren (dette `TwitterAccount`-objektet), og en liste med tweets. Vi ønsker å kunne sortere `TwitterAccount`-objekter på tre ulike parametre:
1. Brukernavn
2. Antall følgere
3. Antall tweet
Dersom du ikke har gjort `Twitter`-oppgaven allerede, bør du gjøre denne først. Løsningsforslaget kommer til å være tilgjengelig [her](https://git.ntnu.no/tdt4100/tdt4100-lf-25/blob/main/src/main/java/oving4/twitter) etter siste demonstrasjonsfrist for øving 4.
Filene i denne oppgaven skal ligge i [`oving5/twitter`](../../src/main/java/oving5/twitter).
## Del 1
I denne delen av oppgaven skal du lage tre ulike implementasjoner av `Comparator`-grensesnittet. `Comparator`-grensesnittet inneholder én metode: `int compare(Object o1, Object o2)`. Implementasjonen av denne metoden skal returnere:
- Et negativt tall dersom objektet o1 skal komme før objektet o2 i en sortert rekkefølge.
- Et positivt tall dersom objektet o1 skal komme etter objektet o2 i en sortert rekkefølge.
- $0$ om det er likegyldig hvilken rekkefølge objektene har (dvs. de er like hverandre for den parameteren/de paremetrene de sorteres på).
De tre klassene du skal lage er som følger:
- `UserNameComparator`: Sammenligner `TwitterAccount`-objektene på brukernavn, slik at brukeren “Apekatten” vil komme før “Bjørnen” som igjen vil komme før “Cameleonen” (dvs. leksikalsk rekkefølge - tenk rekkefølgene brukernavnene ville stått i et leksikon eller en ordbok).
- `FollowersCountComparator`: Sammenligner `TwitterAccount`-objektene på antall følgere, slik at brukeren med flest følgere havner først.
- `TweetsCountComparator`: Sammenligner `TwitterAccount`-objektene på antall tweets, slik at brukeren med flest tweets havner først.
Alle klassene skal implementere `Comparator<TwitterAccount>`.
[Hint om sammenligning av strenger](<https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html#compareTo(java.lang.String)>).
[Hint om sammenligning av tall](<https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html#compareTo(java.lang.Integer)>).
## Del 2
I denne delen av oppgaven skal du legge til en funksjon i `TwitterAccount`-klassen som lar deg hente ut en sortert versjon av følgerene til dette (`this`) `TwitterAccount`-objektet. Funksjonen du skal implementere er som følger:
- `List<TwitterAccount> getFollowers(Comparator<TwitterAccount>)` - skal returnere en sortert kopi av følgere-listen til dette `TwitterAccount`-objektet. Objektene skal sorteres ved å bruke det `Comparator`-objektet som tas inn som parameter. Dersom parameteren er `null` skal du returnere den orginale (usorterte) versjonen av følgere-listen. Du skal ikke skrive din egen sorteringsalgoritme, men bruke `Collections.sort`-funksjonen fra `java.utils`-biblioteket. Merk at den opprinnelige følgere-listen skal være uforandret etter at denne funksjonen har blitt kjørt.
Testkode for oppgaven finner du her:
- [oving5/twitter/TwitterAccountTest.java](../../src/test/java/oving5/twitter/TwitterAccountTest.java)
- [oving5/twitter/FollowersCountComparatorTest.java](../../src/test/java/oving5/twitter/FollowersCountComparatorTest.java)
- [oving5/twitter/TweetsCountComparatorTest.java](../../src/test/java/oving5/twitter/TweetsCountComparatorTest.java)
- [oving5/twitter/UserNameComparatorTest.java](../../src/test/java/oving5/twitter/UserNameComparatorTest.java)
## Valgfri Ekstraoppgave
Lag en klasse `TwitterAccountComparator` som implementerer `Comparator<TwitterAccount>` og sammenligner `TwitterAccount`-objekter på
følgende måte:
- `TwitterAccount`-objektet med flest følgere skal komme først.
- Dersom to `TwitterAccount`-objekter har like mange følgere skal det `TwitterAccount`-objektet med flest tweets komme først.
- Dersom to `TwitterAccount`-objekter har like mange følgere og tweets skal `TwitterAccount`-objektene sammenlignes på brukernavn.

View File

@@ -24,6 +24,11 @@
<artifactId>junit-jupiter</artifactId>
<version>6.0.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.5.0-jre</version>
</dependency>
</dependencies>
<build>

View File

@@ -7,4 +7,5 @@ open module ovinger {
requires javafx.controls;
requires javafx.fxml;
requires javafx.graphics;
requires com.google.common;
}

View File

@@ -0,0 +1,4 @@
package oving1;
public class Account {
}

View File

@@ -0,0 +1,64 @@
package oving1;
public class LineEditor {
String text = "";
int insertionIndex = 0;
LineEditor() {
}
void left() {
insertionIndex -= 1;
}
void right() {
insertionIndex += 1;
}
void insertString(String s) {
String a = text.substring(0, insertionIndex - 1);
String b = text.substring(insertionIndex, text.length());
text = a + s + b;
}
void deleteLeft() {
if (insertionIndex < 1) {
return;
}
String a = text.substring(0, insertionIndex - 2);
String b = text.substring(insertionIndex, text.length());
text = a + b;
}
void deleteRight() {
if (insertionIndex >= text.length() - 1) {
return;
}
String a = text.substring(0, insertionIndex - 1);
String b = text.substring(insertionIndex + 1, text.length());
text = a + b;
}
String getText() {
return text;
}
void setText(String s) {
text = s;
}
int getInsertionIndex() {
return insertionIndex;
}
void setInsertionIndex(int i) {
insertionIndex = i;
}
@Override
public String toString() {
String a = text.substring(0, insertionIndex - 1);
String b = text.substring(insertionIndex, text.length());
return a + "|" + b;
}
}

View File

@@ -0,0 +1,4 @@
package oving1;
public class Location {
}

View File

@@ -0,0 +1,6 @@
// class Main {
// public static void main(String[] args) {
// System.out.println("Hello, World!");
// LineEditor line_editor = new LineEditor("test", 0);
// }
// }

View File

@@ -0,0 +1,87 @@
package oving1;
public class Rectangle {
int x1;
int y1;
int x2;
int y2;
public Rectangle(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
int getMinX() {
return Math.min(x1, x2);
}
int getMinY() {
return Math.min(y1, y2);
}
int getMaxX() {
return Math.max(x1, x2);
}
int getMaxY() {
return Math.max(y1, y2);
}
int getWidth() {
return getMaxX() - getMinX();
}
int getHeight() {
return getMaxY() - getMinY();
}
boolean isEmpty() {
return getWidth() == 0 || getHeight() == 0;
}
boolean contains(int x, int y) {
return x >= getMinX() && x <= getMaxX() && y >= getMinY() && y <= getMaxY();
}
boolean contains(Rectangle rect) {
return contains(rect.x1, rect.y1) && contains(rect.x2, rect.y1) && contains(rect.x1, rect.y2)
&& contains(rect.x2, rect.y2);
}
boolean add(int x, int y) {
int px1 = x1;
int px2 = x2;
int py1 = y1;
int py2 = y2;
x1 = Math.min(x, x1);
x2 = Math.max(x, x2);
y1 = Math.min(y, y1);
y2 = Math.max(y, y2);
return px1 == x1 && px2 == x2 && py1 == y1 && py2 == y2;
}
boolean add(Rectangle rect) {
return add(rect.x1, rect.y1) || add(rect.x2, rect.y1) || add(rect.x1, rect.y2) || add(rect.x2, rect.y2);
}
Rectangle union(Rectangle rect) {
int px1 = x1;
int px2 = x2;
int py1 = y1;
int py2 = y2;
add(rect);
Rectangle r = new Rectangle(x1, y1, x2, y2);
x1 = px1;
x2 = px2;
y1 = py1;
y2 = py2;
return r;
}
@Override
public String toString() {
return "";
}
}

View File

@@ -0,0 +1,63 @@
package oving1;
public class StopWatch {
boolean active = false;
int time = 0;
int ticks = 0;
int lapTime = 0;
int lap = 0;
int lastLapTime = -1;
public StopWatch() {
}
boolean isStarted() {
return active;
}
boolean isStopped() {
return !active;
}
int getTicks() {
return ticks;
}
int getTime() {
return time;
}
int getLapTime() {
return lapTime;
}
int getLastLapTime() {
return lastLapTime;
}
void tick(int ticks) {
this.ticks += ticks;
if (isStopped()) {
return;
}
}
void start() {
active = true;
}
void lap() {
lap += 1;
lastLapTime = lapTime;
lapTime = 0;
}
void stop() {
active = false;
}
@Override
public String toString() {
return "";
}
}

View File

@@ -0,0 +1,4 @@
package oving1;
public class UpOrDownCounter {
}

View File

@@ -0,0 +1,7 @@
package oving2;
class Main {
public static void main(String[] args) {
Person p = new Person();
}
}

View File

@@ -0,0 +1,119 @@
package oving2;
import java.util.Date;
import com.google.common.net.InternetDomainName;
public class Person {
private String name;
private String email;
private Date birthday;
private char gender;
private String SSN;
public Person() {
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
public Date getBirthday() {
return birthday;
}
public char getGender() {
return gender;
}
public String getSSN() {
return SSN;
}
public void setName(String name) {
int i = name.indexOf(' ');
if (i == -1) {
throw new IllegalArgumentException("Did not find both prename and surname.");
}
String prename = name.substring(0, i);
String surname = name.substring(i + 1, name.length());
if (prename.contains(" ") || surname.contains(" ")) {
throw new IllegalArgumentException("There should only be one space which separates prename and surname.");
}
if (prename.length() < 2 || surname.length() < 2) {
throw new IllegalArgumentException("Prename or surname should not be less than 2 characters.");
}
if (!isAlphabetic(prename) || !isAlphabetic(surname)) {
throw new IllegalArgumentException("Prename and surname should only contain letters.");
}
this.name = name;
}
private boolean isAlphabetic(String s) {
char[] chars = name.toCharArray();
for (char c : chars) {
if (!Character.isLetter(c)) {
return false;
}
}
return true;
}
public void setEmail(String email) {
if (email != null) {
throw new IllegalArgumentException("Email is already set.");
}
if (name == null) {
throw new IllegalArgumentException("Name needs to be set before email.");
}
int i = email.indexOf('@');
if (i == -1) {
throw new IllegalArgumentException("Email should contain 1 @");
}
String[] names = name.split(" ");
String first = email.substring(0, i);
String second = email.substring(i + 1, email.length());
if (first.contains("@") || second.contains("@")) {
throw new IllegalArgumentException("Email should contain 1 @");
}
if (first != names[0] + "." + names[1]) {
throw new IllegalArgumentException("Email username should be on the form: \"prename.surname\".");
}
boolean isDomain = true;
try {
InternetDomainName d = InternetDomainName.from(second);
isDomain = d.hasPublicSuffix();
} catch (IllegalArgumentException e) {
isDomain = false;
}
if (!isDomain) {
throw new IllegalArgumentException("Email domain is not on the form: \"local.domain\"");
}
this.email = email;
}
void setBirthday(Date date) {
Date now = new Date();
if (now.before(date)) {
throw new IllegalArgumentException("Birthday cannot be set to a future date.");
}
birthday = date;
}
void setGender(char c) {
if (c != 'M' && c != 'F' && c != '\0') {
throw new IllegalArgumentException("Invalid gender value.");
}
gender = c;
}
void setSSN(String s) {
}
}

View File

@@ -0,0 +1,65 @@
package oving2;
public class Vehicle {
private char vehicleType;
private char fuelType;
private String registrationNumber;
Vehicle(char vehicleType, char fuelType, String regstrationNumber) {
setVehicleType(vehicleType);
setFuelType(fuelType);
setRegistrationNumber(registrationNumber);
}
char getVehicleType() {
return vehicleType;
}
char getFuelType() {
return fuelType;
}
String getRegistrationNumber() {
return registrationNumber;
}
void setVehicleType(char vehicleType) {
if (vehicleType != 'M' && vehicleType != 'C') {
throw new IllegalArgumentException("Invalid vehicle type, found: \"" + vehicleType + "\"");
}
this.vehicleType = vehicleType;
}
void setFuelType(char fuelType) {
if (fuelType != 'H' && fuelType != 'E' && fuelType != 'D' && fuelType != 'G') {
throw new IllegalArgumentException("Invalid vehicle type, found: \"" + fuelType + "\"");
}
this.fuelType = vehicleType;
}
void setRegistrationNumber(String registrationNumber) {
String code = registrationNumber.substring(0, 2);
if (fuelType == 'E' && code != "EL" && code != "EK") {
throw new IllegalArgumentException(
"Electric vehicles should have registraition number starting with EL or EK, got: "
+ registrationNumber + "\"");
} else if (fuelType == 'H' && code != "HY") {
throw new IllegalArgumentException(
"Hydrogen vehicles should have registraition number starting with HY, got: " + registrationNumber
+ "\"");
} else if ((fuelType == 'D' || fuelType == 'G') && (code == "EL" || code == "EK" || code == "HY")) {
throw new IllegalArgumentException(
"Diesel or gas vehicles should have reigstartion number not starting with EL, EK, or HY.");
}
if (vehicleType == 'C' && registrationNumber.length() != 7) {
throw new IllegalArgumentException(
"Cars have a registrationNumber of length 7" + registrationNumber + "\"");
}
if (vehicleType == 'M' && registrationNumber.length() != 6) {
throw new IllegalArgumentException(
"Motorbikes have a registrationNumber of length 6" + registrationNumber + "\"");
}
this.registrationNumber = registrationNumber;
}
}

View File

@@ -0,0 +1,12 @@
# Teori
- Hva er en synlighetsmodifikator?
En synlighetsmodifikator endrer på hvem som har tilgang på noe. Enten det er en attributt, metode, eller klasse.
- Hva er forskjellen på private og public og når brukes de?
Hvis man setter en attributt til private vil den kun være direkte tilgjengelig innad i den klassen den blir definert. Hvis den blir satt til public er ikke dette lenger tilfellet og man kan direkte modifisere og lese den utenfor klassen slik som når man har en instans av klassen.
# Oppgaver
## Person
### Del 2
- Navnet kan være delt i fornavn og fullt navn.
- Man kan enten gjøre det slik at man må gi inn begge deler samtidig, eller at man må gi inn det ene, og at man da finner ut av den andre basert på den ene. Man kan og ha at man angir det hver for seg uten at man antar at de er sammmenkoblet.

View File

@@ -0,0 +1,63 @@
package oving3;
import java.util.Stack;
public class RPNCalc {
private Stack<Double> stack = new Stack<>();
RPNCalc() {
}
void push(double n) {
stack.push(n);
}
double pop() {
if (stack.isEmpty()) {
return Double.NaN;
}
return stack.pop();
}
double peek(int i) {
int id = stack.size() - i;
if (id < 0 || id >= stack.size()) {
return Double.NaN;
}
return stack.get(id);
}
int getSize() {
return stack.size();
}
void performOperation(char c) {
if (stack.size() <= 1) {
return;
}
double a = stack.pop();
double b = stack.pop();
switch (c) {
case '+':
stack.push(a + b);
break;
case '-':
stack.push(a - b);
break;
case '*':
stack.push(a * b);
break;
case '/':
stack.push(a / b);
break;
default:
throw new IllegalArgumentException("Invalid operation");
}
}
@Override
public String toString() {
return stack.toString();
}
}

View File

@@ -8,57 +8,62 @@ package oving3.card;
*/
public class Card {
// TODO: Add fields here
private char suit;
private int face;
/**
* The constructor of the {@code Card} class initializes the suit and face of
* the card with the
* first and second arguments, respectively.
*
* @param suit the suit of the card, one of {@code 'S'} (spades), {@code 'H'}
* (hearts),
* {@code 'D'} (diamonds), or {@code 'C'} (clubs)
* @param face the face of the card, an integer between {@code 1} (ace) and
* {@code 13} (king)
* (both inclusive)
* @throws IllegalArgumentException if the suit or face is illegal
*
* @see CardTest#testConstructor()
*/
public Card(char suit, int face) {
// TODO: Implement this constructor
}
/**
* The constructor of the {@code Card} class initializes the suit and face of
* the card with the
* first and second arguments, respectively.
*
* @param suit the suit of the card, one of {@code 'S'} (spades), {@code 'H'}
* (hearts),
* {@code 'D'} (diamonds), or {@code 'C'} (clubs)
* @param face the face of the card, an integer between {@code 1} (ace) and
* {@code 13} (king)
* (both inclusive)
* @throws IllegalArgumentException if the suit or face is illegal
*
* @see CardTest#testConstructor()
*/
public Card(char suit, int face) {
if (suit != 'S' && suit != 'H' && suit != 'D' && suit != 'C') {
throw new IllegalArgumentException("Illegal value for suit");
}
if (face < 1 || face > 13) {
throw new IllegalArgumentException("Illegal value for face");
}
this.suit = suit;
this.face = face;
}
/**
* @return the suit of the card
*/
public char getSuit() {
// TODO: Implement this method
return '\0';
}
/**
* @return the suit of the card
*/
public char getSuit() {
return suit;
}
/**
* @return the face of the card
*/
public int getFace() {
// TODO: Implement this method
return 0;
}
/**
* @return the face of the card
*/
public int getFace() {
return face;
}
/**
* @return the value of the card of the form {@code <suit><face>}. For example,
* the ace of
* spades should return {@code "S1"}
*
* @see CardTest#testToString()
*/
@Override
public String toString() {
// TODO: Implement this method
return null;
}
/**
* @return the value of the card of the form {@code <suit><face>}. For example,
* the ace of
* spades should return {@code "S1"}
*
* @see CardTest#testToString()
*/
@Override
public String toString() {
return suit + Integer.toString(face);
}
public static void main(String[] args) {
public static void main(String[] args) {
}
}
}

View File

@@ -0,0 +1,43 @@
package oving3.card;
import java.util.ArrayList;
public class CardDeck {
private ArrayList<Card> deck = new ArrayList<>();
CardDeck(int n) {
if (n < 0 || n > 13) {
throw new IllegalArgumentException("value of n needs to be >= 1 and <= 13");
}
char[] suits = { 'S', 'H', 'D', 'C' };
for (char suit : suits) {
for (int face = 1; face <= n; face++) {
deck.add(new Card(suit, face));
}
}
}
int getCardCount() {
return deck.size();
}
Card getCard(int n) {
return new Card('S', 1);
}
void shufflePerfectly() {
int n = deck.size();
if (n < 2) {
return;
}
ArrayList<Card> shuffledDeck = new ArrayList<>();
for (int i = 0; i < n / 2; i++) {
shuffledDeck.add(deck.get(i));
shuffledDeck.add(deck.get(n / 2 + i));
}
if (n % 2 != 0) {
shuffledDeck.add(deck.get(n - 1));
}
deck = shuffledDeck;
}
}

View File

@@ -42,7 +42,9 @@ public class CoffeeCup {
public void drinkCoffee(double volume) {
if (!this.isValidVolume(volume) || !this.canDrink(volume)) {
throw new IllegalArgumentException("You cannot drink that much coffee!");
throw new IllegalArgumentException(
"You cannot drink that much coffee! Trying to drink: " + volume + ", cup can hold: " + capacity
+ ", cup has: " + currentVolume);
}
this.currentVolume -= volume;

View File

@@ -21,10 +21,13 @@ public class CoffeeCupProgram {
this.cup.fillCoffee(32.5);
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9));
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42));
this.cup.increaseCupSize(17);
this.cup.increaseCupSize(17 + 300);
this.cup.fillCoffee(35);
this.cup.drinkCoffee(40);
this.cup.fillCoffee(42 + 20.5);
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42));
this.cup.drinkCoffee(Math.floor(this.r.nextDouble() * 20.5));
this.cup.increaseCupSize(170);
this.cup.fillCoffee(32.5);
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9));
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 42));
@@ -34,6 +37,8 @@ public class CoffeeCupProgram {
private void part2() {
this.cup = new CoffeeCup(40.0, 20.5);
this.r = new Random(987_654_321L);
this.cup.increaseCupSize(10000);
this.cup.fillCoffee(9000);
this.cup.drinkCoffee(Math.floor(this.r.nextDouble() * 20.5));
this.cup.fillCoffee(Math.floor(this.r.nextDouble() * 30));
this.cup.drinkCoffee(Math.ceil(this.r.nextDouble() * 38.9));

View File

@@ -0,0 +1,20 @@
# Teori
## RPN
### Del 2
Hvilken type unntak vil det være naturlig å bruke?
IllegalStateException
Hvilke fordeler og ulemper ser du for dette alternativet?
Resistans til at programmet krasjer, men problemet er at alternativet er dårlig data.
Hva vil tilsvarende verdi for manglende operand for *-operasjonen (multiplikasjon) være? Hva med for / (divisjon)?
1 og 1. Det vil føre til ingen endring.
Hvordan kan du endre (evt. har du endret) grensesnittet for stack-operasjonene for å gjøre implementasjonen av disse enklere?
Ta inn en default.
Også her er et alternativ å utløse unntak. Hva tror du om det?
IllegalStateException kan brukes, men det er avhengig om man hvordan man ønsker at grensesnittet skal fungere, og hvor strengt det skal være. Det viktigste er at man er konsekvent.

View File

@@ -0,0 +1,89 @@
package oving4;
import java.util.ArrayList;
public class Person {
private String name;
private char gender;
private Person mother;
private Person father;
private ArrayList<Person> children;
Person() {
}
String getName() {
return name;
}
char getGender() {
return gender;
}
Person getMother() {
return mother;
}
Person getFather() {
return father;
}
int getChildCount() {
return children.size();
}
Person getChild(int n) {
if (n < 0 || n >= children.size()) {
throw new IllegalArgumentException("invalid index");
}
return children.get(n);
}
void addChild(Person p) {
if (isMale()) {
p.father = this;
} else {
p.mother = this;
}
children.add(p);
}
void removeChild(Person p) {
for (int i = 0; i < children.size(); i++) {
if (p.name == children.get(i).name) {
children.remove(i);
return;
}
}
if (p.isMale() && this.father == p) {
this.father = null;
return;
}
if (p.isFemale() && this.mother == p) {
this.mother = null;
return;
}
}
void setMother(Person p) {
mother = p;
}
void setFather(Person p) {
father = p;
}
boolean isMale() {
if (gender == 'M') {
return true;
}
if (gender == 'F') {
return false;
}
throw new IllegalArgumentException("Illegal gender");
}
boolean isFemale() {
return !isMale();
}
}

View File

View File

View 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);
}
}

View File

@@ -0,0 +1,37 @@
package oving4.testing;
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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class CoffeeCupTest {
private CoffeeCup coffee;
@BeforeEach
public void setUp() {
coffee = new CoffeeCup();
}
@Test
@DisplayName("Constructor")
public void testConstructor() {
assertEquals(0, coffee.getCapacity());
assertEquals(0, coffee.getCurrentVolume());
coffee.increaseCupSize(10);
assertEquals(10, coffee.getCapacity());
coffee.fillCoffee(10);
assertEquals(10, coffee.getCurrentVolume());
coffee.drinkCoffee(10);
assertEquals(0, coffee.getCurrentVolume());
}
@Test
@DisplayName("volume cannot be null")
public void testAddChildException() {
}
}

View File

View File

View File

View File

View File

View File

View 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());
}
}

View 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));
}
}

View 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");
}
}

View 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);
}
}

View 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());
}
}

View 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());
}
}

View 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");
}
}

View File

View 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());
}
}

View 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());
}
}

View File

@@ -0,0 +1,100 @@
package oving5;
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.Collections;
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 BinaryComputingIteratorTest {
private Iterator<Double> iterator1;
private Iterator<Double> iterator2;
private Iterator<Double> iteratorShort;
@BeforeEach
public void setUp() {
iterator1 = List.of(0.5, -2.0).iterator();
iterator2 = List.of(5.0, 3.0).iterator();
iteratorShort = List.of(5.0).iterator();
}
@Test
@DisplayName("Check that the constructor sets up the iterator correctly")
public void testConstructor() {
assertThrows(IllegalArgumentException.class, () -> {
new BinaryComputingIterator(null, iterator2, (a, b) -> a * b);
}, "Iterator1 cannot be null");
assertThrows(IllegalArgumentException.class, () -> {
new BinaryComputingIterator(iterator1, null, (a, b) -> a * b);
}, "Iterator2 cannot be null");
assertThrows(IllegalArgumentException.class, () -> {
new BinaryComputingIterator(iterator1, iterator2, null);
}, "Operator cannot be null");
}
@Test
@DisplayName("Check BinaryComputingIterator with multiplication")
public void testMultiplication() {
BinaryComputingIterator binaryIt =
new BinaryComputingIterator(iterator1, iterator2, (a, b) -> a * b);
assertEquals(2.5, binaryIt.next(), "The first number was incorrect");
assertTrue(binaryIt.hasNext());
assertEquals(-6.0, binaryIt.next(), "The second number was incorrect");
assertFalse(binaryIt.hasNext());
}
@Test
@DisplayName("Check BinaryComputingIterator with addition")
public void testAddition() {
BinaryComputingIterator binaryIt =
new BinaryComputingIterator(iterator1, iterator2, (a, b) -> a + b);
assertEquals(5.5, binaryIt.next(), "The first number was incorrect");
assertTrue(binaryIt.hasNext());
assertEquals(1.0, binaryIt.next(), "The second number was incorrect");
assertFalse(binaryIt.hasNext());
}
@Test
@DisplayName("Test multiplication with only one number")
public void testShortIterator() {
BinaryComputingIterator binaryIt =
new BinaryComputingIterator(iterator1, iteratorShort, (a, b) -> a * b);
assertEquals(2.5, binaryIt.next(), "The first number was incorrect");
assertFalse(binaryIt.hasNext());
}
@Test
@DisplayName("Test with default value, both a number and null")
public void testShortIteratorAndDefault() {
BinaryComputingIterator binaryIt =
new BinaryComputingIterator(iterator1, iteratorShort, null, 2.0, (a, b) -> a * b);
assertEquals(2.5, binaryIt.next(), "The first number was incorrect");
assertTrue(binaryIt.hasNext());
assertEquals(-4.0, binaryIt.next(), "The second number was incorrect");
assertFalse(binaryIt.hasNext());
}
@Test
@DisplayName("Test with an empty iterator")
public void testEmptyIterator() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(Collections.emptyIterator(),
Collections.emptyIterator(), (a, b) -> a * b);
assertFalse(binaryIt.hasNext(), "An empty iterator should not have next");
}
@Test
@DisplayName("Test an empty iterator with default value")
public void testEmptyIteratorAndDefault() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(Collections.emptyIterator(),
Collections.emptyIterator(), 1.0, 2.0, (a, b) -> a * b);
assertFalse(binaryIt.hasNext(), "An empty iterator should not have next");
}
}

View File

@@ -0,0 +1,71 @@
package oving5;
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.function.BinaryOperator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class RPNCalcTest {
private RPNCalc calc;
@BeforeEach
public void setUp() {
calc = new RPNCalc();
}
@Test
@DisplayName("Test operation without operands")
public void testPerformOperationWithoutOperation() {
assertThrows(UnsupportedOperationException.class, () -> {
calc.performOperation('+');
});
}
@Test
@DisplayName("Test execution of a simple operation")
public void testPerformOperation() {
calc.addOperator('+', (a, b) -> a * b); // Use "incorrect" definition to filter out cheating
calc.addOperator('l', (a, b) -> a * (a + b));
calc.push(4);
calc.push(3);
calc.performOperation('+');
assertEquals(12.0, calc.pop(), "The result of the calculation was incorrect");
assertEquals(Double.NaN, calc.pop());
calc.push(4);
calc.push(3);
calc.performOperation('l');
assertEquals(28.0, calc.pop(), "The result of the calculation was incorrect");
assertEquals(Double.NaN, calc.pop());
}
@Test
@DisplayName("Test adding operators")
public void testAddOperator() {
assertTrue(calc.addOperator('+', (a, b) -> a + b), "You should be able to add operators");
assertTrue(calc.addOperator('-', (a, b) -> a - b), "You should be able to add operators");
assertFalse(calc.addOperator('+', (a, b) -> a + b),
"You should not be able to add the same operator twice");
assertFalse(calc.addOperator('-', (a, b) -> a * b),
"You should not be able to add the same operator twice");
assertFalse(calc.addOperator('.', (BinaryOperator<Double>) null),
"You should not be able to add a null operator");
}
@Test
@DisplayName("Check that you can remove operators")
public void testRemoveOperator() {
calc.addOperator('+', (a, b) -> a + b);
calc.removeOperator('+');
assertThrows(UnsupportedOperationException.class, () -> {
calc.performOperation('+');
}, "The operator should have been removed");
}
}

View File

@@ -0,0 +1,78 @@
package oving5.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
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 CardComparatorTest {
private Card s1;
private Card h1;
private Card d1;
private Card c1;
private Card s13;
private Card h13;
private Card d13;
private Card c13;
private Collection<Card> expected;
private List<Card> cards;
private static void testCards(Collection<Card> actualCards, Collection<Card> expectedCards) {
Iterator<Card> actual = actualCards.iterator();
Iterator<Card> expected = expectedCards.iterator();
while (expected.hasNext()) {
assertTrue(actual.hasNext());
Card actualCard = actual.next();
Card expectedCard = expected.next();
assertEquals(expectedCard.getSuit(), actualCard.getSuit(), String.format(
"The card deck should have been %s, but was %s", expectedCards, actualCards));
assertEquals(expectedCard.getFace(), actualCard.getFace(), String.format(
"The card deck should have been %s, but was %s", expectedCards, actualCards));
}
}
@BeforeEach
public void setUp() {
s1 = new Card('S', 1);
h1 = new Card('H', 1);
d1 = new Card('D', 1);
c1 = new Card('C', 1);
s13 = new Card('S', 13);
h13 = new Card('H', 13);
d13 = new Card('D', 13);
c13 = new Card('C', 13);
cards = new ArrayList<>(List.of(s1, s13, h1, h13, d1, d13, c1, c13));
}
@Test
@DisplayName("Check that the deck is sorted with aces as the lowest")
public void testNormal() {
expected = List.of(c1, c13, d1, d13, h1, h13, s1, s13);
cards.sort(new CardComparator(false, ' '));
CardComparatorTest.testCards(cards, expected);
}
@Test
@DisplayName("Check that the deck is sorted with aces as the highest")
public void testAceIsHighest() {
expected = List.of(c13, c1, d13, d1, h13, h1, s13, s1);
cards.sort(new CardComparator(true, ' '));
CardComparatorTest.testCards(cards, expected);
}
@Test
@DisplayName("Check that the deck is sorted correctly with diamonds as trump")
public void testDiamondIsTrump() {
expected = List.of(c1, c13, h1, h13, s1, s13, d1, d13);
cards.sort(new CardComparator(false, 'D'));
CardComparatorTest.testCards(cards, expected);
}
}

View File

@@ -0,0 +1,69 @@
package oving5.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 CardContainerIteratorTest {
private Card c1;
private Card c2;
private Card d1;
private Card d2;
private Card h1;
private Card h2;
private Card s1;
private Card s2;
private CardContainerIterator iterator;
private static void testCards(Iterator<Card> actual, Iterator<Card> expected) {
while (expected.hasNext()) {
assertTrue(actual.hasNext());
Card actualCard = actual.next();
Card expectedCard = expected.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() {
iterator = new CardContainerIterator(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);
}
@Test
@DisplayName("Check that the iterator for a new deck of cards outputs [S1, S2, H1, H2, D1, D2, C1, C2]")
public void testConstructor() {
CardContainerIteratorTest.testCards(iterator,
List.of(s1, s2, h1, h2, d1, d2, c1, c2).iterator());
}
@Test
@DisplayName("Check that the first card in the iterator is correct")
public void testNext() {
Card nextCard = iterator.next();
assertEquals(s1.toString(), nextCard.toString());
}
@Test
@DisplayName("Check that the deck contains at least one card")
public void testHasNext() {
boolean hasNext = iterator.hasNext();
assertTrue(hasNext);
}
}

View File

@@ -0,0 +1,86 @@
package oving5.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
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 CardDeckTest {
private Card s1;
private Card h1;
private Card d1;
private Card c1;
private Card s2;
private Card h2;
private Card d2;
private Card c2;
private CardDeck deck;
private Collection<Card> expected;
private static void testCards(Iterable<Card> actual, Iterator<Card> expected) {
Iterator<Card> actualIt = actual.iterator();
while (expected.hasNext()) {
assertTrue(actualIt.hasNext());
Card expectedCard = expected.next();
Card actualCard = actualIt.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));
}
}
private static void testCards(CardContainer it, Collection<Card> expected) {
assertEquals(expected.size(), it.getCardCount());
Iterator<Card> expectedIt = expected.iterator();
int i = 0;
while (expectedIt.hasNext()) {
Card expectedCard = expectedIt.next();
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++;
}
}
@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 ArrayList<>(List.of(s1, s2, h1, h2, d1, d2, c1, c2));
}
@Test
@DisplayName("Checks that CardContainer works with CardDeck")
public void testCardContainer() {
CardDeckTest.testCards(deck, expected);
}
@Test
@DisplayName("Checks that the iterator works with CardDeck")
public void testDeckIterator() {
CardDeckTest.testCards(deck, expected.iterator());
}
}

View File

@@ -0,0 +1,78 @@
package oving5.card;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
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 s1;
private Card c2;
private CardHand hand;
private Collection<Card> expected;
private static void testCards(CardContainer it, Collection<Card> expected) {
assertEquals(expected.size(), it.getCardCount());
Iterator<Card> expectedIt = expected.iterator();
int i = 0;
while (expectedIt.hasNext()) {
Card expectedCard = expectedIt.next();
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(Iterable<Card> actual, Iterator<Card> expected) {
Iterator<Card> actualIt = actual.iterator();
while (expected.hasNext()) {
assertTrue(actualIt.hasNext());
Card expectedCard = expected.next();
Card actualCard = actualIt.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() {
s1 = new Card('S', 1);
c2 = new Card('C', 2);
hand = new CardHand();
expected = new ArrayList<>(List.of(s1, c2));
}
@Test
@DisplayName("Checks that CardContainer works with CardHand")
public void testCardContainer() {
hand.addCard(s1);
hand.addCard(c2);
CardHandTest.testCards(hand, expected);
}
@Test
@DisplayName("Checks that the iterator works with CardHand")
public void testDeckIterator() {
hand.addCard(s1);
hand.addCard(c2);
CardHandTest.testCards(hand, expected.iterator());
}
}

View File

@@ -0,0 +1,63 @@
package oving5.card;
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 CardPredicateTest {
private CardDeck deck;
@BeforeEach
public void setUp() {
deck = new CardDeck(10);
}
@Test
@DisplayName("Check that hasCard() works as expected")
public void testHasCard() {
assertThrows(IllegalArgumentException.class, () -> {
deck.hasCard(null);
}, "Predicate cannot be null");
assertTrue(deck.hasCard(c -> c.getSuit() == 'S'));
assertFalse(deck.hasCard(c -> c.getFace() == 13));
assertTrue(deck.hasCard(c -> c.getSuit() == 'S' && c.getFace() == 8));
}
@Test
@DisplayName("Check that getCardCount() works as expected")
public void testGetCardCount() {
assertThrows(IllegalArgumentException.class, () -> {
deck.getCardCount(null);
}, "Predicate cannot be null");
assertEquals(10, deck.getCardCount(c -> c.getSuit() == 'S'));
assertEquals(4, deck.getCardCount(c -> c.getFace() == 4));
assertEquals(1, deck.getCardCount(c -> c.getFace() == 4 && c.getSuit() == 'H'));
}
@Test
@DisplayName("Check that getCards() works as expected")
public void testGetCards() {
assertThrows(IllegalArgumentException.class, () -> {
deck.getCards(null);
}, "Predicate cannot be null");
Card card = new Card('S', 4);
Card card2 = new Card('S', 5);
List<Card> matching = List.of(card, card2);
assertEquals(matching.size(),
deck.getCards(c -> (c.getFace() == 4 || c.getFace() == 5) && c.getSuit() == 'S')
.size(),
"getCards should have returned two cards that were spades and had the numbers 4 "
+ "or 5");
assertEquals(10, deck.getCards(c -> c.getSuit() == 'S').size(),
"getCards should have returned 10 cards of the spades suit");
}
}

View File

@@ -0,0 +1,50 @@
package oving5.named;
import static org.junit.jupiter.api.Assertions.assertEquals;
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 NamedComparatorTest {
private NamedComparator comparator;
private Person1 p1;
private Person2 p2;
@BeforeEach
public void setUp() {
comparator = new NamedComparator();
p1 = new Person1("Aleksander", "Vestlund");
p2 = new Person2("Dan Vestlund");
}
@Test
@DisplayName("Check that people with the same name are equivalent")
public void testSameFullName() {
assertEquals(0, comparator.compare(p1, p1));
assertEquals(0, comparator.compare(p2, p2));
}
@Test
@DisplayName("Check that given names are compared when the family names are the same")
public void testSameFamilyName() {
// Return negative since first givenName is before second
assertTrue(comparator.compare(p1, p2) < 0, "Aleksander should come before Dan");
// Return positive since first givenName is after second
assertTrue(comparator.compare(p2, p1) > 0, "Dan should come after Aleksander");
}
@Test
@DisplayName("Check that family names are compared correctly")
public void testDifferentFamilyName() {
p2.setFamilyName("Bertelsen");
// Return negative since first familyName is before second
assertTrue(comparator.compare(p2, p1) < 0, "Bertelsen should come before Vestlund");
// Return positive since first familyName is after second
assertTrue(comparator.compare(p1, p2) > 0, "Vestlund should come after Bertelsen");
}
}

View File

@@ -0,0 +1,58 @@
package oving5.named;
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 Person1Test {
private Person1 person;
private String given;
private String family;
private static void testName(Person1 person, String givenName, String familyName) {
assertEquals(givenName, person.getGivenName());
assertEquals(familyName, person.getFamilyName());
assertEquals(String.format("%s %s", givenName, familyName), person.getFullName());
}
@BeforeEach
public void setUp() {
given = "Hallvard";
family = "Trætteberg";
person = new Person1(given, family);
}
@Test
@DisplayName("Check that the constructor assigns the correct name to the person")
public void testConstructor() {
Person1Test.testName(person, given, family);
}
@Test
@DisplayName("Check that setGivenName() assigns the correct name")
public void testSetGivenName() {
String newGiven = "Jens";
person.setGivenName(newGiven);
Person1Test.testName(person, newGiven, family);
}
@Test
@DisplayName("Check that setFamilyName() assigns the correct name")
public void testSetFamilyName() {
String newFamily = "Olsen";
person.setFamilyName(newFamily);
Person1Test.testName(person, given, newFamily);
}
@Test
@DisplayName("Check that setFullName() assigns the correct name")
public void testSetFullName() {
String newGiven = "Lisa";
String newFamily = "Eriksen";
String newFull = String.format("%s %s", newGiven, newFamily);
person.setFullName(newFull);
Person1Test.testName(person, newGiven, newFamily);
}
}

View File

@@ -0,0 +1,58 @@
package oving5.named;
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 Person2Test {
private Person2 person;
private String given;
private String family;
@BeforeEach
public void setUp() {
given = "Hallvard";
family = "Trætteberg";
person = new Person2(String.format("%s %s", given, family));
}
private static void testName(Person2 person, String givenName, String familyName) {
assertEquals(givenName, person.getGivenName());
assertEquals(familyName, person.getFamilyName());
assertEquals(String.format("%s %s", givenName, familyName), person.getFullName());
}
@Test
@DisplayName("Check that the constructor assigns the correct name to the person")
public void testConstructor() {
Person2Test.testName(person, given, family);
}
@Test
@DisplayName("Check that setGivenName() assigns the correct name")
public void testSetGivenName() {
String newGiven = "Jens";
person.setGivenName(newGiven);
Person2Test.testName(person, newGiven, family);
}
@Test
@DisplayName("Check that setFamilyName() assigns the correct name")
public void testSetFamilyName() {
String newFamily = "Olsen";
person.setFamilyName(newFamily);
Person2Test.testName(person, given, newFamily);
}
@Test
@DisplayName("Check that setFullName() assigns the correct name")
public void testSetFullName() {
String newGiven = "Lisa";
String newFamily = "Eriksen";
String newFull = String.format("%s %s", newGiven, newFamily);
person.setFullName(newFull);
Person2Test.testName(person, newGiven, newFamily);
}
}

View File

@@ -0,0 +1,111 @@
package oving5.stringgrid;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
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 StringGridTest {
private StringGrid grid;
@BeforeEach
public void setUp() {
grid = new StringGridImpl(2, 3);
}
@Test
@DisplayName("Test the size of the grid")
public void testSize() {
assertEquals(2, grid.getRowCount(), "The number of rows was incorrect");
assertEquals(3, grid.getColumnCount(), "The number of columns was incorrect");
}
@Test
@DisplayName("Test that setElement sets the correct grid element")
public void testGrid() {
grid.setElement(0, 0, "0, 0");
grid.setElement(0, 1, "0, 1");
grid.setElement(0, 2, "0, 2");
grid.setElement(1, 0, "1, 0");
grid.setElement(1, 1, "1, 1");
grid.setElement(1, 2, "1, 2");
assertEquals("0, 0", grid.getElement(0, 0));
assertEquals("0, 1", grid.getElement(0, 1));
assertEquals("0, 2", grid.getElement(0, 2));
assertEquals("1, 0", grid.getElement(1, 0));
assertEquals("1, 1", grid.getElement(1, 1));
assertEquals("1, 2", grid.getElement(1, 2));
}
@Test
@DisplayName("Check the elements in the grid with row-first order")
public void testGridIteratorRowMajor() {
StringGridIterator iterator = new StringGridIterator(grid, true);
grid.setElement(0, 0, "0, 0");
grid.setElement(0, 1, "0, 1");
grid.setElement(0, 2, "0, 2");
grid.setElement(1, 0, "1, 0");
grid.setElement(1, 1, "1, 1");
grid.setElement(1, 2, "1, 2");
assertTrue(iterator.hasNext());
assertEquals("0, 0", iterator.next());
assertTrue(iterator.hasNext());
assertEquals("0, 1", iterator.next());
assertTrue(iterator.hasNext());
assertEquals("0, 2", iterator.next());
assertTrue(iterator.hasNext());
assertEquals("1, 0", iterator.next());
assertTrue(iterator.hasNext());
assertEquals("1, 1", iterator.next());
assertTrue(iterator.hasNext());
// False after going through the last one
assertEquals("1, 2", iterator.next());
assertFalse(iterator.hasNext());
}
@Test
@DisplayName("Check the elements in the grid with column-first order")
public void testGridIteratorColumnMajor() {
StringGridIterator iterator = new StringGridIterator(grid, false);
grid.setElement(0, 0, "0, 0");
grid.setElement(0, 1, "0, 1");
grid.setElement(0, 2, "0, 2");
grid.setElement(1, 0, "1, 0");
grid.setElement(1, 1, "1, 1");
grid.setElement(1, 2, "1, 2");
assertTrue(iterator.hasNext());
assertEquals("0, 0", iterator.next(), "A cell was incorrect");
assertTrue(iterator.hasNext(), "Incorrect number of cells in the grid");
assertEquals("1, 0", iterator.next(), "A cell was incorrect");
assertTrue(iterator.hasNext(), "Incorrect number of cells in the grid");
assertEquals("0, 1", iterator.next(), "A cell was incorrect");
assertTrue(iterator.hasNext(), "Incorrect number of cells in the grid");
assertEquals("1, 1", iterator.next(), "A cell was incorrect");
assertTrue(iterator.hasNext(), "Incorrect number of cells in the grid");
assertEquals("0, 2", iterator.next(), "A cell was incorrect");
assertTrue(iterator.hasNext(), "Incorrect number of cells in the grid");
// False after going through the last one
assertEquals("1, 2", iterator.next());
assertFalse(iterator.hasNext(), "Incorrect number of cells in the grid");
}
}

View File

@@ -0,0 +1,51 @@
package oving5.ticket;
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.time.LocalDateTime;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class PeriodTicketTest {
@Test
@DisplayName("Check that the ticket is valid within the period")
public void testIsValidWhenWithinPeriod() {
LocalDateTime start = LocalDateTime.now().minusDays(1);
LocalDateTime end = LocalDateTime.now().plusDays(1);
PeriodTicket ticket = new PeriodTicket(start, end);
assertTrue(ticket.scan(), "The ticket should be valid within the period");
}
@Test
@DisplayName("Check that the ticket is not valid before the period")
public void testIsNotValidWhenBeforePeriod() {
LocalDateTime start = LocalDateTime.now().plusDays(1);
LocalDateTime end = LocalDateTime.now().plusDays(2);
PeriodTicket ticket = new PeriodTicket(start, end);
assertFalse(ticket.scan(), "The ticket should not be valid before the period");
}
@Test
@DisplayName("Check that the ticket is not valid after the period")
public void testIsNotValidWhenAfterPeriod() {
LocalDateTime start = LocalDateTime.now().minusDays(2);
LocalDateTime end = LocalDateTime.now().minusDays(1);
PeriodTicket ticket = new PeriodTicket(start, end);
assertFalse(ticket.scan(), "The ticket should not be valid after the period");
}
@Test
@DisplayName("Check that the ticket throws IllegalArgumentException when the start date is "
+ "after the end date")
public void testThrowsExceptionWhenStartIsAfterEnd() {
LocalDateTime start = LocalDateTime.now().plusDays(1);
LocalDateTime end = LocalDateTime.now().minusDays(1);
assertThrows(IllegalArgumentException.class, () -> {
new PeriodTicket(start, end);
}, "The ticket should throw IllegalArgumentException when the start date is after the "
+ "end date");
}
}

View File

@@ -0,0 +1,41 @@
package oving5.ticket;
import static org.junit.jupiter.api.Assertions.assertFalse;
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 SingleTicketTest {
private Ticket ticket;
@BeforeEach
public void setUp() {
ticket = new SingleTicket();
}
@Test
@DisplayName("Check that the ticket returns true when scanned for the first time")
public void testReturnsTrueWhenScanned() {
assertTrue(this.ticket.scan(),
"The ticket should return true when scanned for the first time");
}
@Test
@DisplayName("Check that the ticket returns false when scanned for the second time")
public void testReturnsFalseWhenScannedTwice() {
ticket.scan();
assertFalse(ticket.scan(),
"The ticket should return false when scanned for the second time");
}
@Test
@DisplayName("Check that the ticket returns true when scanned for the first time after reset")
public void testReturnsFalseWhenScannedTwiceAfterReset() {
ticket.scan();
ticket = new SingleTicket();
assertTrue(ticket.scan(),
"The ticket should return true when scanned for the first time after reset");
}
}

View File

@@ -0,0 +1,35 @@
package oving5.twitter;
import static org.junit.jupiter.api.Assertions.assertEquals;
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 FollowersCountComparatorTest {
private TwitterAccount aaron;
private TwitterAccount ben;
private TwitterAccount charlie;
private FollowersCountComparator comparator;
@BeforeEach
public void SetUp() {
aaron = new TwitterAccount("Aaron");
ben = new TwitterAccount("Ben");
charlie = new TwitterAccount("Charlie");
comparator = new FollowersCountComparator();
}
@Test
@DisplayName("Check that the comparison is based on followers")
public void testCompare() {
aaron.follow(ben);
ben.follow(aaron);
assertEquals(0, comparator.compare(aaron, ben), "Aaron and Ben should be equal");
charlie.follow(ben);
assertTrue(comparator.compare(aaron, ben) > 0, "Aaron should come after Ben");
assertTrue(comparator.compare(ben, aaron) < 0, "Ben should come before Aaron");
}
}

View File

@@ -0,0 +1,38 @@
package oving5.twitter;
import static org.junit.jupiter.api.Assertions.assertEquals;
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 TweetsCountComparatorTest {
private TwitterAccount mostTweet;
private TwitterAccount lessTweet1;
private TwitterAccount lessTweet2;
private TweetsCountComparator comparator;
@BeforeEach
public void SetUp() {
mostTweet = new TwitterAccount("Aaron");
lessTweet1 = new TwitterAccount("Ben");
lessTweet2 = new TwitterAccount("Charlie");
comparator = new TweetsCountComparator();
}
@Test
@DisplayName("Check comparison based on tweets")
public void testCompare() {
mostTweet.tweet("Tweet");
mostTweet.tweet("Tweet");
lessTweet1.tweet("Tweet");
lessTweet2.tweet("Tweet");
assertTrue(comparator.compare(mostTweet, lessTweet1) < 0,
"The account with the most tweets should come first");
assertTrue(comparator.compare(lessTweet1, mostTweet) > 0,
"The account with the fewest tweets should come last");
assertEquals(0, comparator.compare(lessTweet1, lessTweet2),
"Two accounts with the same number of tweets should be equal");
}
}

View File

@@ -0,0 +1,146 @@
package oving5.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 be following %s",
accountA.getUserName(), accountB.getUserName()));
assertTrue(accountB.isFollowedBy(accountA), String.format("%s should be followed by %s",
accountB.getUserName(), accountA.getUserName()));
} else {
assertFalse(accountA.isFollowing(accountB),
String.format("%s should not be following %s", accountA.getUserName(),
accountB.getUserName()));
assertFalse(accountB.isFollowedBy(accountA),
String.format("%s should not be followed by %s", accountB.getUserName(),
accountA.getUserName()));
}
if (BfollowsA) {
assertTrue(accountB.isFollowing(accountA), String.format("%s should be following %s",
accountB.getUserName(), accountA.getUserName()));
assertTrue(accountA.isFollowedBy(accountB), String.format("%s should be followed by %s",
accountA.getUserName(), accountB.getUserName()));
} else {
assertFalse(accountB.isFollowing(accountA),
String.format("%s should not be following %s", accountB.getUserName(),
accountA.getUserName()));
assertFalse(accountA.isFollowedBy(accountB),
String.format("%s should not be 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 sets up the account correctly")
public void testConstructor() {
assertEquals("Nils", nils.getUserName());
assertEquals(0, nils.getTweetCount());
assertEquals("Ole", ole.getUserName());
assertEquals(0, ole.getTweetCount());
}
@Test
@DisplayName("Follow")
public void testFollow() {
nils.follow(ole);
TwitterAccountTest.checkFollow(nils, ole, true, false);
ole.follow(nils);
TwitterAccountTest.checkFollow(nils, ole, true, true);
}
@Test
@DisplayName("Unfollow")
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
@DisplayName("Tests that a new tweet is correct")
public void testNewTweet() {
nils.tweet("Kvitre!");
assertEquals(1, nils.getTweetCount(), "Nils' tweet count should be 1");
assertEquals("Kvitre!", nils.getTweet(1).getText(), "The text should be 'Tweet'");
nils.tweet("Kvitre igjen!");
assertEquals(2, nils.getTweetCount());
assertEquals("Kvitre igjen!", nils.getTweet(1).getText());
assertEquals("Kvitre!", nils.getTweet(2).getText());
}
@Test
@DisplayName("Tests exceptions for illegal tweets")
public void testIllegalTweet() {
assertThrows(RuntimeException.class, () -> {
nils.getTweet(1);
}, "Should not be able to retrieve a tweet that does not exist");
assertThrows(RuntimeException.class, () -> {
nils.getTweet(-1);
}, "Should not be able to retrieve a tweet that does not exist");
nils.tweet("Tweet!");
assertThrows(RuntimeException.class, () -> {
nils.getTweet(2);
}, "Should not be able to retrieve a tweet that does not exist");
assertThrows(RuntimeException.class, () -> {
nils.getTweet(-1);
}, "Should not be able to retrieve a tweet that does not exist");
}
@Test
@DisplayName("Check that retweet works, including 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());
}
}

View File

@@ -0,0 +1,32 @@
package oving5.twitter;
import static org.junit.jupiter.api.Assertions.assertEquals;
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 UserNameComparatorTest {
private TwitterAccount aaron1;
private TwitterAccount aaron2;
private TwitterAccount ben;
private UserNameComparator comparator;
@BeforeEach
public void setUp() {
aaron1 = new TwitterAccount("Aaron");
aaron2 = new TwitterAccount("Aaron");
ben = new TwitterAccount("Ben");
comparator = new UserNameComparator();
}
@Test
@DisplayName("Check comparison based on username")
public void testCompare() {
assertTrue(comparator.compare(aaron1, ben) < 0, "Aaron should be sorted before Ben");
assertTrue(comparator.compare(ben, aaron1) > 0, "Ben should be sorted after Aaron");
assertEquals(comparator.compare(aaron1, aaron2), 0,
"Two people with the same name should be equal");
}
}