diff --git a/Kotlin/README.md b/Kotlin/README.md index 01b53561..83f52fac 100644 --- a/Kotlin/README.md +++ b/Kotlin/README.md @@ -22,6 +22,8 @@ You should make sure the gradle commands shown above work when you execute them There are instructions in the [TextTest Readme](../texttests/README.md) for setting up TextTest. What's unusual for the Java version is there are two executables listed in [config.gr](../texttests/config.gr) for Java. One uses Gradle wrapped in a python script, the other relies on your CLASSPATH being set correctly in [environment.gr](../texttests/environment.gr). +## Starting Point + ### Project info 1. Code is highly unreadable and impossible to extend 2. Algorithm and test cases are unknown, I do not trust that `TexttestFixtures` covers all paths so I want to validate it with jacoco @@ -33,7 +35,8 @@ There are instructions in the [TextTest Readme](../texttests/README.md) for sett 3. understand algorithm and propose better solution -### Execution +## Execution +### Testing TexttestFixtures TexttestFixtures indeed do not cover all paths, based on jacoco coverage: ![img.png](img.png) @@ -43,3 +46,13 @@ There are two ways to advance: I prefer to go with (2) because it seems to me to be more resilient way. I need to check if execution time is not bloated. Also minimize number of test cases. + +### Test cases generation +I copied GildedRose class to PlatinumRose and implemented a test which executes both app +and then compare results. Time of execution is under 500ms which is great. Also all paths are covered: + +![img_1.png](img_1.png) + +One noticeable fact i that items are declared as `var` which makes it possible to update during/after execution. +In my opinion this is a design issue, code-smell and I'm going to change it to `val`. +So far I only changed Item to data class so that object comparison is easier, also it seems to be DTO/record so it suppose to be a data class diff --git a/Kotlin/img_1.png b/Kotlin/img_1.png new file mode 100644 index 00000000..769ec791 Binary files /dev/null and b/Kotlin/img_1.png differ diff --git a/Kotlin/src/main/kotlin/com/gildedrose/Item.kt b/Kotlin/src/main/kotlin/com/gildedrose/Item.kt index eaf62a40..5059f949 100644 --- a/Kotlin/src/main/kotlin/com/gildedrose/Item.kt +++ b/Kotlin/src/main/kotlin/com/gildedrose/Item.kt @@ -1,6 +1,6 @@ package com.gildedrose -open class Item(var name: String, var sellIn: Int, var quality: Int) { +data class Item(var name: String, var sellIn: Int, var quality: Int) { override fun toString(): String { return this.name + ", " + this.sellIn + ", " + this.quality } diff --git a/Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt b/Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt new file mode 100644 index 00000000..dc478702 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt @@ -0,0 +1,58 @@ +package com.gildedrose + +class PlatinumRose(var items: List) { + + fun updateQuality() { + for (i in items.indices) { + if (items[i].name != "Aged Brie" && items[i].name != "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].quality > 0) { + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].quality = items[i].quality - 1 + } + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + + if (items[i].name == "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].sellIn < 11) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + + if (items[i].sellIn < 6) { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + } + } + } + + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].sellIn = items[i].sellIn - 1 + } + + if (items[i].sellIn < 0) { + if (items[i].name != "Aged Brie") { + if (items[i].name != "Backstage passes to a TAFKAL80ETC concert") { + if (items[i].quality > 0) { + if (items[i].name != "Sulfuras, Hand of Ragnaros") { + items[i].quality = items[i].quality - 1 + } + } + } else { + items[i].quality = items[i].quality - items[i].quality + } + } else { + if (items[i].quality < 50) { + items[i].quality = items[i].quality + 1 + } + } + } + } + } + +} + diff --git a/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt b/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt new file mode 100644 index 00000000..b259b9ab --- /dev/null +++ b/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt @@ -0,0 +1,43 @@ +package com.gildedrose + +import java.util.* + + +fun generateTestCasesInRanger(names: List, sellInRange: IntRange, qualityRange: IntRange): List { + val items = LinkedList() + for (name in names) { + for (sellIn in sellInRange) { + for (quality in qualityRange) { + items.add(Item(name, sellIn, quality)) + } + } + } + return items +} + +fun main(args: Array) { + val names = listOf( + "Aged Brie", + "Backstage passes to a TAFKAL80ETC concert", + "Sulfuras, Hand of Ragnaros", + "new none-existing on code name" + ) + val sellInRange = -100..100 + val qualityRange = -100..100 + val allTestCases = generateTestCasesInRanger(names, sellInRange, qualityRange) + + + var testCasesCounter = 0 + for (testCase in allTestCases) { + val app = GildedRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality))) + app.updateQuality() + + + if (testCase.sellIn != app.items[0].sellIn || testCase.quality != app.items[0].quality) { + //println("$testCasesCounter: $testCase vs ${app.items[0]}") + testCasesCounter += 1 + } + } + println("Found $testCasesCounter testCasesCounter of out ${allTestCases.size} allTestCases") + +} diff --git a/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt b/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt index 72f27b9e..b6f83333 100644 --- a/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt +++ b/Kotlin/src/main/kotlin/com/gildedrose/TexttestFixture.kt @@ -6,15 +6,15 @@ fun main(args: Array) { val items = listOf( Item("+5 Dexterity Vest", 10, 20), // - Item("Aged Brie", 2, 0), // - Item("Elixir of the Mongoose", 5, 7), // - Item("Sulfuras, Hand of Ragnaros", 0, 80), // - Item("Sulfuras, Hand of Ragnaros", -1, 80), - Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), - Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), - Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), - // this conjured item does not work properly yet - Item("Conjured Mana Cake", 3, 6) + Item("Aged Brie", 2, 0), // + Item("Elixir of the Mongoose", 5, 7), // + Item("Sulfuras, Hand of Ragnaros", 0, 80), // + Item("Sulfuras, Hand of Ragnaros", -1, 80), + Item("Backstage passes to a TAFKAL80ETC concert", 15, 20), + Item("Backstage passes to a TAFKAL80ETC concert", 10, 49), + Item("Backstage passes to a TAFKAL80ETC concert", 5, 49), + // this conjured item does not work properly yet + Item("Conjured Mana Cake", 3, 6) ) val app = GildedRose(items) diff --git a/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt b/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt index 52af8f7d..bf805dc3 100644 --- a/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt +++ b/Kotlin/src/test/kotlin/com/gildedrose/GildedRoseTest.kt @@ -20,7 +20,16 @@ internal class GildedRoseTest { val app = GildedRose(items) app.updateQuality() assertEquals("foo", app.items[0].name) + } + @Test + fun `should execute without exception for empty list`() { + val items = listOf() + val app = GildedRose(items) + app.updateQuality() + assertAll( + { assertEquals(items, app.items) } + ) } @ParameterizedTest @@ -53,7 +62,6 @@ internal class GildedRoseTest { ) } } - } diff --git a/Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt b/Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt new file mode 100644 index 00000000..290a3262 --- /dev/null +++ b/Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt @@ -0,0 +1,48 @@ +package com.gildedrose + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.util.* + +class PlatinumRoseTest { + + @Test + fun `should update quality for generated test cases and compare with GlidedRose`() { + val names = listOf( + "Aged Brie", + "Backstage passes to a TAFKAL80ETC concert", + "Sulfuras, Hand of Ragnaros", + "new none-existing on code name" + ) + val sellInRange = -100..100 + val qualityRange = -100..100 + val allTestCases = generateTestCasesInRanger(names, sellInRange, qualityRange) + + + for (testCase in allTestCases) { + val platinumRose = PlatinumRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality))) + val gildedRose = GildedRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality))) + + platinumRose.updateQuality() + gildedRose.updateQuality() + + assertEquals(gildedRose.items, platinumRose.items) + } + } + + private fun generateTestCasesInRanger( + names: List, + sellInRange: IntRange, + qualityRange: IntRange + ): List { + val items = LinkedList() + for (name in names) { + for (sellIn in sellInRange) { + for (quality in qualityRange) { + items.add(Item(name, sellIn, quality)) + } + } + } + return items + } +} \ No newline at end of file