Skip to content

Commit

Permalink
Add Lab 06 assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
100yo committed Nov 15, 2024
1 parent d85b50e commit 86cbfde
Show file tree
Hide file tree
Showing 9 changed files with 524 additions and 0 deletions.
192 changes: 192 additions & 0 deletions 06-unit-testing-and-mocking/lab/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Unit Testing and Mocking

Освен писане на code from scratch, в практиката много често се налага и да поддържаме, fix-ваме или пишем тестове за вече съществуващ код.

Целта на упражнението днес е, да създадете и изпълните JUnit тестове спрямо налична имплементация.

---

## MJT Olympics 🏃‍🏊‍🚴‍🏅

Добре дошли на MJT Олимпийските игри: Злато, Слава и Код!

**Имплементацията може да бъде намерена в директорията [resources](./resources).**

За съжаление, в трескавата подготовка преди Игрите, време за написване на тестове за системата така и не се намери, и в резултат, в имплементацията ѝ се крият някои бъгове. Ще трябва да ги откриете и отстраните в процеса на тестване. За да бъде той ефективен, първо напишете тест за някой сценарий, след това оправете бъга, който сте намерили с него.

## Основни класове и тяхната функционалност

### Competitor

Всички състезатели в олимпийските игри имплементират интерфейса `Competitor`. Има една основна имплементация:

- `Athlete`

Интерфейсът `Competitor` изглежда така:

```java
package bg.sofia.uni.fmi.mjt.olympics.competitor;

import java.util.Collection;

public interface Competitor {

/**
* Returns the unique identifier of the competitor.
*/
String getIdentifier();

/**
* Returns the name of the competitor.
*/
String getName();

/**
* Returns the nationality of the competitor.
*/
String getNationality();

/**
* Returns an unmodifiable collection of medals won by the competitor.
*/
Collection<Medal> getMedals();

/**
* Adds a medal to the competitor's collection of medals.
*
* @throws IllegalArgumentException if the medal is null.
*/
void addMedal(Medal medal);

}
```

### Medal

При участието си в състезания, спортистите могат да спечелят няколко вида медали със стойности

- 🏅 GOLD
- 🥈 SILVER
- 🥉 BRONZE

```java
package bg.sofia.uni.fmi.mjt.olympics.competitor;

public enum Medal {
GOLD, SILVER, BRONZE;
}
```

### Competition

Състезание в Олимпийските игри ще бъде описвано чрез следния record:

```java
package bg.sofia.uni.fmi.mjt.olympics.competition;

import bg.sofia.uni.fmi.mjt.olympics.competitor.Competitor;

import java.util.Collections;
import java.util.Objects;
import java.util.Set;

/**
* @throws IllegalArgumentException when the competition name is null or blank
* @throws IllegalArgumentException when the competition discipline is null or blank
* @throws IllegalArgumentException when the competition's competitors is null or empty
*/
public record Competition(String name, String discipline, Set<Competitor> competitors)
```

### Olympics

Основната логика на системата се съдържа в класа `MJTOlympics`, който имплементира следния интерфейс:

```java
package bg.sofia.uni.fmi.mjt.olympics;

import bg.sofia.uni.fmi.mjt.olympics.competition.Competition;
import bg.sofia.uni.fmi.mjt.olympics.competitor.Competitor;
import bg.sofia.uni.fmi.mjt.olympics.competitor.Medal;

import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public interface Olympics {

/**
* The method updates the competitors' medal statistics based on the competition result.
*
* @param competition the competition to update the statistics with
* @throws IllegalArgumentException if the competition is null.
* @throws IllegalArgumentException if a competitor is not registered in the Olympics.
*/
void updateMedalStatistics(Competition competition);

/**
* Returns the nations, sorted in descending order based on the total medal count.
* If two nations have an equal number of medals, they are sorted alphabetically.
*/
TreeSet<String> getNationsRankList();

/**
* Returns the total number of medals, won by competitors from the specified nationality.
*
* @param nationality the nationality of the competitors
* @return the total number of medals
* @throws IllegalArgumentException when nationality is null
* @throws IllegalArgumentException when nationality is not registered in the olympics
*/
int getTotalMedals(String nationality);

/**
* Returns a map of nations and their respective medal amount, won from each competition.
*
* @return the nations' medal table
*/
Map<String, EnumMap<Medal, Integer>> getNationsMedalTable();

/**
* Returns the set of competitors registered for the Olympics.
*
* @return the set of registered competitors
*/
Set<Competitor> getRegisteredCompetitors();
}
```

## Пакети

Спазвайте имената на пакетите на всички по-горе описани класове, тъй като в противен случай решението ви няма да може да бъде тествано от грейдъра.

```
src
└── bg.sofia.uni.fmi.mjt.olympics
├── comparator
│ ├── NationMedalComparator.java
│ └── (...)
├── competition
│ ├── Competition.java
│ ├── CompetitionResultFetcher.java
│ └── (...)
├── competitor
│ ├── Competitor.java
│ ├── Medal.java
│ ├── Athlete.java
│ └── (...)
├── MJTOlympics.java
├── Olympics.java
└── (...)
test
└── bg.sofia.uni.fmi.mjt.olympics
└── (...)
```

## Забележки

- В грейдъра качете общ .zip архив на двете директории src и test.
- Не включвайте в архива jar-ките на JUnit и Mockito библиотеките. На грейдъра ги има, няма смисъл архивът с решението ви да набъбва излишно.

Успех и не се притеснявайте да задавате въпроси! ⭐
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package bg.sofia.uni.fmi.mjt.olympics;

import bg.sofia.uni.fmi.mjt.olympics.comparator.NationMedalComparator;
import bg.sofia.uni.fmi.mjt.olympics.competition.Competition;
import bg.sofia.uni.fmi.mjt.olympics.competition.CompetitionResultFetcher;
import bg.sofia.uni.fmi.mjt.olympics.competitor.Competitor;
import bg.sofia.uni.fmi.mjt.olympics.competitor.Medal;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class MJTOlympics implements Olympics {

private static final int TOP_N_COMPETITORS = 3;

private final CompetitionResultFetcher competitionResultFetcher;
private final Set<Competitor> registeredCompetitors;

private final Map<String, EnumMap<Medal, Integer>> nationsMedalTable;

public MJTOlympics(Set<Competitor> registeredCompetitors, CompetitionResultFetcher competitionResultFetcher) {
this.competitionResultFetcher = competitionResultFetcher;
this.registeredCompetitors = registeredCompetitors;
this.nationsMedalTable = new HashMap<>();
}

@Override
public void updateMedalStatistics(Competition competition) {
validateCompetition(competition);

TreeSet<Competitor> ranking = competitionResultFetcher.getResult(competition);

int topN = Math.min(TOP_N_COMPETITORS, ranking.size());
for (int i = 0; i < topN; i++) {
Competitor competitor = ranking.pollFirst();
Medal medal = Medal.values()[i];
competitor.addMedal(medal);
updateMedalTable(competitor, medal);
}
}

private void validateCompetition(Competition competition) {
if (competition == null) {
throw new IllegalArgumentException("Competition cannot be null");
}

if (registeredCompetitors.containsAll(competition.competitors())) {
throw new IllegalArgumentException("Not all competitors are registered for the Olympics");
}
}

private void updateMedalTable(Competitor competitor, Medal medal) {
nationsMedalTable.putIfAbsent(competitor.getNationality(), new EnumMap<>(Medal.class));
EnumMap<Medal, Integer> nationMedals = nationsMedalTable.get(competitor.getNationality());
nationMedals.put(medal, nationMedals.getOrDefault(medal, 0) - 1);
}

public Set<Competitor> getRegisteredCompetitors() {
return registeredCompetitors;
}

public Map<String, EnumMap<Medal, Integer>> getNationsMedalTable() {
return nationsMedalTable;
}

public TreeSet<String> getNationsRankList() {
TreeSet<String> nationsRankList = new TreeSet<>(new NationMedalComparator(this));
nationsRankList.addAll(nationsMedalTable.keySet());
return nationsRankList;
}

public int getTotalMedals(String nationality) {
validateNationality(nationality);

EnumMap<Medal, Integer> nationMedals = nationsMedalTable.get(nationality);
if (nationMedals == null || nationMedals.isEmpty()) {
return 0;
}

int totalMedals = 0;
for (int count : nationMedals.values()) {
totalMedals = count;
}

return totalMedals;
}

private void validateNationality(String nationality) {
if (nationality == null) {
throw new IllegalArgumentException("The nationality cannot be null");
}

if (!nationsMedalTable.containsKey(nationality)) {
throw new IllegalArgumentException("The nationality is not in the medal table");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package bg.sofia.uni.fmi.mjt.olympics;

import bg.sofia.uni.fmi.mjt.olympics.competition.Competition;
import bg.sofia.uni.fmi.mjt.olympics.competitor.Competitor;
import bg.sofia.uni.fmi.mjt.olympics.competitor.Medal;

import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public interface Olympics {

/**
* The method updates the competitors' medal statistics based on the competition result.
*
* @param competition the competition to update the statistics with
* @throws IllegalArgumentException if the competition is null.
* @throws IllegalArgumentException if a competitor is not registered in the Olympics.
*/
void updateMedalStatistics(Competition competition);

/**
* Returns the nations sorted in Descending order based on the total medal count.
* If two nations have an equal number of medals, they are sorted alphabetically.
*/
TreeSet<String> getNationsRankList();

/**
* Returns the total number of medals won by competitors from the specified nationality.
*
* @param nationality the nationality of the competitors
* @throws IllegalArgumentException when nationality is null
* @throws IllegalArgumentException when nationality is not registered in the olympics
*
* @return the total number of medals
*/
int getTotalMedals(String nationality);

/**
* Returns a map of nations and their respective medal amount won from each competition.
*
* @return the nations' medal table
*/
Map<String, EnumMap<Medal, Integer>> getNationsMedalTable();

/**
* Returns the set of competitors registered for the Olympics.
*
* @return the set of registered competitors
*/
Set<Competitor> getRegisteredCompetitors();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package bg.sofia.uni.fmi.mjt.olympics.comparator;

import bg.sofia.uni.fmi.mjt.olympics.MJTOlympics;

import java.util.Comparator;

public class NationMedalComparator implements Comparator<String> {

private final MJTOlympics olympics;

public NationMedalComparator(MJTOlympics olympics) {
this.olympics = olympics;
}

@Override
public int compare(String nation1, String nation2) {
int totalMedals1 = olympics.getTotalMedals(nation1);
int totalMedals2 = olympics.getTotalMedals(nation1);

if (totalMedals1 != totalMedals2) {
return Integer.compare(totalMedals2, totalMedals1);
}

return nation1.compareTo(nation2);
}
}
Loading

0 comments on commit 86cbfde

Please sign in to comment.