From 2acc6969a5abaeb25ad7e87f008b79fec033d0ca Mon Sep 17 00:00:00 2001 From: MuTalKing Date: Sat, 11 Jun 2022 17:59:03 +0300 Subject: [PATCH 1/2] Add sqlLite, dbSteps and available object Test --- build.gradle.kts | 30 ++++++++++++++ gradle.properties | 2 + settings.gradle.kts | 1 - .../gogolev/seabattle/configuration/Config.kt | 4 +- .../seabattle/configuration/DbConnect.kt | 9 ++++ .../gogolev/seabattle/entity/ObjectEntity.kt | 27 ++++++++++++ .../gogolev/seabattle/fields/BattleField.kt | 18 +++++++- .../seabattle/fields/BattleFieldDelegate.kt | 23 +++++++---- .../ru/gogolev/seabattle/utils/DbFileUtils.kt | 28 +++++++++++++ .../ru/gogolev/seabattle/utils/DbUtils.kt | 41 +++++++++++++++++++ .../test/seabattle/FillingObjectTest.kt | 35 ++++++++++++++++ 11 files changed, 207 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/ru/gogolev/seabattle/configuration/DbConnect.kt create mode 100644 src/main/kotlin/ru/gogolev/seabattle/entity/ObjectEntity.kt create mode 100644 src/main/kotlin/ru/gogolev/seabattle/utils/DbFileUtils.kt create mode 100644 src/main/kotlin/ru/gogolev/seabattle/utils/DbUtils.kt create mode 100644 src/test/kotlin/ru/gogolev/test/seabattle/FillingObjectTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index eff0109..a5d5ffc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,23 +2,53 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") version "1.5.31" + id("io.qameta.allure") version "2.9.6" + id("jacoco") } group = "ru.gogolev" version = "1.0-SNAPSHOT" +val exposedVersion: String by project +val kotestVersion: String by project + +allure { + adapter { + autoconfigure.set(true) + } +} + +jacoco { + toolVersion = "0.8.7" +} + repositories { mavenCentral() } dependencies { testImplementation(kotlin("test")) + testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion") + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") + testImplementation("io.kotest:kotest-extensions-allure:4.4.3") + testImplementation("org.slf4j:slf4j-simple:1.7.32") + + implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") + implementation("org.xerial:sqlite-jdbc:3.36.0.3") } tasks.test { useJUnitPlatform() + ignoreFailures = true + finalizedBy(tasks.allureReport, tasks.jacocoTestReport) } tasks.withType() { kotlinOptions.jvmTarget = "11" +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 7fc6f1f..e230186 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,3 @@ kotlin.code.style=official +exposedVersion=0.36.1 +kotestVersion=5.0.1 diff --git a/settings.gradle.kts b/settings.gradle.kts index d55544c..f2591e1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,2 @@ - rootProject.name = "qaOtus-Kotlin" diff --git a/src/main/kotlin/ru/gogolev/seabattle/configuration/Config.kt b/src/main/kotlin/ru/gogolev/seabattle/configuration/Config.kt index dcec91e..fc2a6e8 100644 --- a/src/main/kotlin/ru/gogolev/seabattle/configuration/Config.kt +++ b/src/main/kotlin/ru/gogolev/seabattle/configuration/Config.kt @@ -9,4 +9,6 @@ const val HELP_COUNT = 1 const val SHIPS_LEN_1_COUNT = 8 const val SHIPS_LEN_2_COUNT = 8 const val SHIPS_LEN_3_COUNT = 0 -const val SHIPS_LEN_4_COUNT = 2 \ No newline at end of file +const val SHIPS_LEN_4_COUNT = 2 + +const val NEW_GAME = true \ No newline at end of file diff --git a/src/main/kotlin/ru/gogolev/seabattle/configuration/DbConnect.kt b/src/main/kotlin/ru/gogolev/seabattle/configuration/DbConnect.kt new file mode 100644 index 0000000..23a89a6 --- /dev/null +++ b/src/main/kotlin/ru/gogolev/seabattle/configuration/DbConnect.kt @@ -0,0 +1,9 @@ +package ru.gogolev.seabattle.configuration + +import org.jetbrains.exposed.sql.Database + +object DbConnect { + val connect by lazy { + Database.connect("jdbc:sqlite:db/seaBattle.db", driver = "org.sqlite.JDBC") + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/gogolev/seabattle/entity/ObjectEntity.kt b/src/main/kotlin/ru/gogolev/seabattle/entity/ObjectEntity.kt new file mode 100644 index 0000000..5d8629e --- /dev/null +++ b/src/main/kotlin/ru/gogolev/seabattle/entity/ObjectEntity.kt @@ -0,0 +1,27 @@ +package ru.gogolev.seabattle.entity + +import org.jetbrains.exposed.dao.IntEntity +import org.jetbrains.exposed.dao.IntEntityClass +import org.jetbrains.exposed.dao.id.EntityID +import org.jetbrains.exposed.dao.id.IntIdTable +import ru.gogolev.seabattle.objects.ObjectType + +object Objects : IntIdTable() { + val x = integer("x") + val y = integer("y") + val z = integer("z") + val objectType = enumeration("objectType", ObjectType::class) +} + +class Object(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(Objects) + + var x by Objects.x + var y by Objects.y + var z by Objects.z + var objectType by Objects.objectType + + override fun toString(): String { + return "Object(x=$x, y=$y, z=$z, type=$objectType)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/gogolev/seabattle/fields/BattleField.kt b/src/main/kotlin/ru/gogolev/seabattle/fields/BattleField.kt index be31995..dd1effb 100644 --- a/src/main/kotlin/ru/gogolev/seabattle/fields/BattleField.kt +++ b/src/main/kotlin/ru/gogolev/seabattle/fields/BattleField.kt @@ -3,7 +3,9 @@ package ru.gogolev.seabattle.fields import ru.gogolev.seabattle.configuration.DEPTH import ru.gogolev.seabattle.configuration.HEIGHT import ru.gogolev.seabattle.configuration.WIDTH +import ru.gogolev.seabattle.configuration.NEW_GAME import ru.gogolev.seabattle.objects.ObjectType +import ru.gogolev.seabattle.utils.DbUtils fun BattleField.renderLine(y: Int, z: Int): String { var line = "" @@ -37,8 +39,16 @@ fun BattleField.render() { /** Игровое поле - переопределены операторы get/set для работы с эмуляцией трехмерного массива */ -class BattleField { - var field = Array(DEPTH * WIDTH * HEIGHT, init = { ObjectType.EMPTY }) +class BattleField(depth: Int = DEPTH, width: Int = WIDTH, height: Int = HEIGHT) { + private val dbUtils = DbUtils() + private var field = Array(depth * width * height, init = { ObjectType.EMPTY}) + + fun prepareBattleField(){ + val objects = dbUtils.selectAllObjectTypes() + objects.forEach { + field[calculatePosition(Point(it.x, it.y, it.z))] = it.objectType + } + } operator fun get(x: Int, y: Int, z: Int): ObjectType { if (checkBoundaries(Point(x, y, z))) { @@ -49,6 +59,10 @@ class BattleField { operator fun set(x: Int, y: Int, z: Int, v: ObjectType) { if (checkBoundaries(Point(x, y, z)) && isAvailablePoint(Point(x, y, z))) { field[calculatePosition(Point(x, y, z))] = v + if(NEW_GAME) { + dbUtils.createDb() + dbUtils.insertObjectType(x, y, z, v) + } } else throw IllegalArgumentException("set incorrect position: $x,$y,$z $v") } diff --git a/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt b/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt index 221eb07..641ab62 100644 --- a/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt +++ b/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt @@ -2,6 +2,7 @@ package ru.gogolev.seabattle.fields import ru.gogolev.seabattle.configuration.* import ru.gogolev.seabattle.objects.* +import ru.gogolev.seabattle.utils.DbFileUtils import kotlin.random.Random import kotlin.reflect.KProperty @@ -127,17 +128,25 @@ class BattleFieldDelegate(val gameObjects: MutableList) { } operator fun getValue(thisRef: Any?, property: KProperty<*>): BattleField { + val dbFileUtils = DbFileUtils() if (battleField == null) { //заполнить поле случайным расположением battleField = BattleField() //заполняем объектами - println("Mines") - propagateMines() - println("Helps") - propogateHelp() - println("Ships") - propogateShips() - println("Battle field is filled") + if(NEW_GAME) { + dbFileUtils.createDirectory() + println("Mines") + propagateMines() + println("Helps") + propogateHelp() + println("Ships") + propogateShips() + println("Battle field is filled") + dbFileUtils.backupData() + } + else{ + battleField!!.prepareBattleField() + } } return battleField!! } diff --git a/src/main/kotlin/ru/gogolev/seabattle/utils/DbFileUtils.kt b/src/main/kotlin/ru/gogolev/seabattle/utils/DbFileUtils.kt new file mode 100644 index 0000000..6d332e6 --- /dev/null +++ b/src/main/kotlin/ru/gogolev/seabattle/utils/DbFileUtils.kt @@ -0,0 +1,28 @@ +package ru.gogolev.seabattle.utils + +import java.nio.file.Files +import kotlin.io.path.Path + +class DbFileUtils { + private val main = Path("db/seaBattle.db") + private val backup = Path("db/seaBattle-backup.db") + + fun backupData() { + if (Files.exists(main)) { + Files.deleteIfExists(backup) + Files.copy(main, backup) + } + } + + fun restoreData() { + if (Files.exists(backup)) { + Files.deleteIfExists(main) + Files.copy(backup, main) + } + } + + fun createDirectory() { + val dbPath = Path("db") + if (!Files.isDirectory(dbPath)) Files.createDirectory(dbPath) + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/gogolev/seabattle/utils/DbUtils.kt b/src/main/kotlin/ru/gogolev/seabattle/utils/DbUtils.kt new file mode 100644 index 0000000..0f22a18 --- /dev/null +++ b/src/main/kotlin/ru/gogolev/seabattle/utils/DbUtils.kt @@ -0,0 +1,41 @@ +package ru.gogolev.seabattle.utils + +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.transactions.transaction +import ru.gogolev.seabattle.configuration.DbConnect +import ru.gogolev.seabattle.entity.Object +import ru.gogolev.seabattle.entity.Objects +import ru.gogolev.seabattle.objects.ObjectType + +class DbUtils { + init { + DbConnect.connect + } + + fun createDb(){ + transaction { + SchemaUtils.createMissingTablesAndColumns(Objects) + } + } + + fun insertObjectType(x: Int, y: Int, z: Int, objectType: ObjectType){ + transaction { + Object.new { + this.x = x + this.y = y + this.z = z + this.objectType = objectType + } + } + } + + fun selectAllObjectTypes(): ArrayList { + val arrayList: ArrayList = arrayListOf() + transaction { + Object.all().forEach { + arrayList += it + } + } + return arrayList + } +} \ No newline at end of file diff --git a/src/test/kotlin/ru/gogolev/test/seabattle/FillingObjectTest.kt b/src/test/kotlin/ru/gogolev/test/seabattle/FillingObjectTest.kt new file mode 100644 index 0000000..c8a79b7 --- /dev/null +++ b/src/test/kotlin/ru/gogolev/test/seabattle/FillingObjectTest.kt @@ -0,0 +1,35 @@ +package ru.gogolev.test.seabattle + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.data.forAll +import io.kotest.data.row +import io.kotest.matchers.shouldBe +import ru.gogolev.seabattle.fields.BattleField +import ru.gogolev.seabattle.fields.Point +import ru.gogolev.seabattle.objects.ObjectType + +class FillingObjectTest : FreeSpec() { + init { + "Check filling an Object" - { + val battleField = BattleField() + forAll( + row(0, 0, 0), + row(4, 5, 3), + row(2, 4, 2), + row(7, 7, 7) + ) { x, y, z -> + "The object[$x, $y, $z] is available"{ + battleField.isAvailablePoint(Point(x, y, z)) shouldBe true + } + + "When the object[$x, $y, $z] is filled"{ + battleField[x, y, z] = ObjectType.MINE + } + + "Then the object[$x, $y, $z] is unavailable"{ + battleField.isAvailablePoint(Point(x, y, z)) shouldBe false + } + } + } + } +} From 0185600182c38d023c8b519d56d0922d8d42886a Mon Sep 17 00:00:00 2001 From: MuTalKing Date: Mon, 27 Jun 2022 20:34:38 +0300 Subject: [PATCH 2/2] add StateRecoveryTest --- .../seabattle/fields/BattleFieldDelegate.kt | 4 +- .../test/seabattle/StateRecoveryTest.kt | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/test/kotlin/ru/gogolev/test/seabattle/StateRecoveryTest.kt diff --git a/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt b/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt index 641ab62..c173d84 100644 --- a/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt +++ b/src/main/kotlin/ru/gogolev/seabattle/fields/BattleFieldDelegate.kt @@ -6,7 +6,7 @@ import ru.gogolev.seabattle.utils.DbFileUtils import kotlin.random.Random import kotlin.reflect.KProperty -class BattleFieldDelegate(val gameObjects: MutableList) { +class BattleFieldDelegate(val gameObjects: MutableList, var isNewGame: Boolean = NEW_GAME) { var battleField: BattleField? = null // возвращает true, если есть соседи @@ -133,7 +133,7 @@ class BattleFieldDelegate(val gameObjects: MutableList) { //заполнить поле случайным расположением battleField = BattleField() //заполняем объектами - if(NEW_GAME) { + if(isNewGame) { dbFileUtils.createDirectory() println("Mines") propagateMines() diff --git a/src/test/kotlin/ru/gogolev/test/seabattle/StateRecoveryTest.kt b/src/test/kotlin/ru/gogolev/test/seabattle/StateRecoveryTest.kt new file mode 100644 index 0000000..4e6497d --- /dev/null +++ b/src/test/kotlin/ru/gogolev/test/seabattle/StateRecoveryTest.kt @@ -0,0 +1,39 @@ +package ru.gogolev.test.seabattle + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.data.forAll +import io.kotest.data.row +import io.kotest.matchers.shouldBe +import ru.gogolev.seabattle.fields.BattleFieldDelegate +import ru.gogolev.seabattle.fields.Point +import ru.gogolev.seabattle.objects.GameObject +import ru.gogolev.seabattle.objects.ObjectType + +class StateRecoveryTest: FreeSpec() { + init{ + "Check state recovery" - { + val gameObjects = mutableListOf() + val battleField by BattleFieldDelegate(gameObjects, true) + forAll( + row(0, 0, 0) + ) { x, y, z -> + "The object[$x, $y, $z] is available" { + battleField.isAvailablePoint(Point(x, y, z)) shouldBe true + } + + "When the object[$x, $y, $z] is filled" { + battleField[x, y, z] = ObjectType.MINE + } + + "Then the object[$x, $y, $z] is unavailable" { + battleField.isAvailablePoint(Point(x, y, z)) shouldBe false + } + + "And state recovery is working"{ + val battleField2 by BattleFieldDelegate(gameObjects, false) + battleField2[x, y, z] shouldBe ObjectType.MINE + } + } + } + } +} \ No newline at end of file