Move test sources from jvmTest to commonTest

This commit is contained in:
Alinson S. Xavier 2026-04-08 05:27:33 -05:00
parent b37af7949c
commit a93e871daf
19 changed files with 336 additions and 318 deletions

View File

@ -93,6 +93,17 @@ interface UserFile {
* parent directory (or this file itself, if it represents a directory). * parent directory (or this file itself, if it represents a directory).
*/ */
fun resolve(child: String): UserFile fun resolve(child: String): UserFile
/**
* Returns the list of files and directories within this directory, or null
* if this path is not a directory or does not exist.
*/
suspend fun listFiles(): List<UserFile>?
/**
* Creates this directory and any necessary parent directories.
*/
suspend fun mkdirs()
} }
/** /**

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016-2025 Álinson Santos Xavier <git@axavier.org>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.platform.io
class ZipEntry(val name: String, val content: String)
expect class ZipReader(bytes: ByteArray) {
fun entries(): List<ZipEntry>
}

View File

@ -0,0 +1,4 @@
package org.isoron.platform.io
expect fun createTestFileOpener(): FileOpener
expect fun createTestDatabaseOpener(): DatabaseOpener

View File

@ -18,7 +18,14 @@
*/ */
package org.isoron.uhabits.core package org.isoron.uhabits.core
import kotlinx.coroutines.runBlocking
import org.isoron.platform.io.Database
import org.isoron.platform.io.DatabaseOpener
import org.isoron.platform.io.FileOpener
import org.isoron.platform.io.TestDatabaseHelper import org.isoron.platform.io.TestDatabaseHelper
import org.isoron.platform.io.UserFile
import org.isoron.platform.io.createTestDatabaseOpener
import org.isoron.platform.io.createTestFileOpener
import org.isoron.platform.time.LocalDate import org.isoron.platform.time.LocalDate
import org.isoron.platform.time.setToday import org.isoron.platform.time.setToday
import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CommandRunner
@ -35,6 +42,8 @@ open class BaseUnitTest {
protected lateinit var modelFactory: ModelFactory protected lateinit var modelFactory: ModelFactory
protected lateinit var taskRunner: SingleThreadTaskRunner protected lateinit var taskRunner: SingleThreadTaskRunner
protected open lateinit var commandRunner: CommandRunner protected open lateinit var commandRunner: CommandRunner
protected val fileOpener: FileOpener = createTestFileOpener()
protected val databaseOpener: DatabaseOpener = createTestDatabaseOpener()
@BeforeTest @BeforeTest
open fun setUp() { open fun setUp() {
@ -47,8 +56,28 @@ open class BaseUnitTest {
commandRunner = CommandRunner(taskRunner) commandRunner = CommandRunner(taskRunner)
} }
protected fun createTempDir(): UserFile = runBlocking {
val dir = fileOpener.openUserFile("test-temp-dir-${tempFileCounter++}")
dir.mkdirs()
dir
}
protected fun copyResourceToTempFile(resourcePath: String): UserFile = runBlocking {
val cleanPath = resourcePath.removePrefix("/")
val tempFile = fileOpener.openUserFile("test-temp-${tempFileCounter++}")
fileOpener.openResourceFile(cleanPath).copyTo(tempFile)
tempFile
}
protected fun openDatabaseResource(resourcePath: String): Database = runBlocking {
val tempFile = copyResourceToTempFile(resourcePath)
databaseOpener.open(tempFile.pathString)
}
companion object { companion object {
fun buildMemoryDatabase(): org.isoron.platform.io.Database { private var tempFileCounter = 0
fun buildMemoryDatabase(): Database {
return TestDatabaseHelper.createEmptyDatabase() return TestDatabaseHelper.createEmptyDatabase()
} }
} }

View File

@ -19,29 +19,24 @@
package org.isoron.uhabits.core.database.migrations package org.isoron.uhabits.core.database.migrations
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers
import org.hamcrest.Matchers.equalTo
import org.isoron.platform.io.Database import org.isoron.platform.io.Database
import org.isoron.platform.io.JavaFileOpener
import org.isoron.platform.io.migrateTo import org.isoron.platform.io.migrateTo
import org.isoron.platform.io.querySingle import org.isoron.platform.io.querySingle
import org.isoron.platform.io.run import org.isoron.platform.io.run
import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Test import kotlin.test.Test
import org.junit.jupiter.api.Assertions.assertThrows import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class Version22Test : JvmBaseUnitTest() { class Version22Test : BaseUnitTest() {
private lateinit var db: Database private lateinit var db: Database
@Throws(Exception::class)
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
db = openDatabaseResource("/databases/021.db") db = openDatabaseResource("/databases/021.db")
} }
private val fileOpener = JavaFileOpener()
private fun migrateTo(version: Int) = runBlocking { private fun migrateTo(version: Int) = runBlocking {
db.migrateTo(version) { v -> db.migrateTo(version) { v ->
val path = "migrations/%02d.sql".format(v) val path = "migrations/%02d.sql".format(v)
@ -52,10 +47,10 @@ class Version22Test : JvmBaseUnitTest() {
@Test @Test
fun testKeepValidReps() { fun testKeepValidReps() {
val before = db.querySingle("select count(*) from repetitions") { it.getInt(0) } val before = db.querySingle("select count(*) from repetitions") { it.getInt(0) }
assertThat(before, equalTo(3)) assertEquals(3, before)
migrateTo(22) migrateTo(22)
val after = db.querySingle("select count(*) from repetitions") { it.getInt(0) } val after = db.querySingle("select count(*) from repetitions") { it.getInt(0) }
assertThat(after, equalTo(3)) assertEquals(3, after)
} }
@Test @Test
@ -64,21 +59,21 @@ class Version22Test : JvmBaseUnitTest() {
val before = db.querySingle( val before = db.querySingle(
"select count(*) from repetitions where habit = 99999" "select count(*) from repetitions where habit = 99999"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(before, equalTo(1)) assertEquals(1, before)
migrateTo(22) migrateTo(22)
val after = db.querySingle( val after = db.querySingle(
"select count(*) from repetitions where habit = 99999" "select count(*) from repetitions where habit = 99999"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(after, equalTo(0)) assertEquals(0, after)
} }
@Test @Test
fun testDisallowNewRepsWithInvalidRef() { fun testDisallowNewRepsWithInvalidRef() {
migrateTo(22) migrateTo(22)
val exception = assertThrows(Exception::class.java) { val exception = assertFailsWith<Exception> {
db.run("insert into Repetitions(habit, timestamp, value) values (99999, 100, 2)") db.run("insert into Repetitions(habit, timestamp, value) values (99999, 100, 2)")
} }
assertThat(exception.message, Matchers.containsString("constraint")) assertContains(exception.message!!, "constraint")
} }
@Test @Test
@ -87,21 +82,21 @@ class Version22Test : JvmBaseUnitTest() {
val before = db.querySingle( val before = db.querySingle(
"select count(*) from repetitions where timestamp is null" "select count(*) from repetitions where timestamp is null"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(before, equalTo(1)) assertEquals(1, before)
migrateTo(22) migrateTo(22)
val after = db.querySingle( val after = db.querySingle(
"select count(*) from repetitions where timestamp is null" "select count(*) from repetitions where timestamp is null"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(after, equalTo(0)) assertEquals(0, after)
} }
@Test @Test
fun testDisallowNullTimestamp() { fun testDisallowNullTimestamp() {
migrateTo(22) migrateTo(22)
val exception = assertThrows(Exception::class.java) { val exception = assertFailsWith<Exception> {
db.run("insert into Repetitions(habit, value) values (0, 2)") db.run("insert into Repetitions(habit, value) values (0, 2)")
} }
assertThat(exception.message, Matchers.containsString("constraint")) assertContains(exception.message!!, "constraint")
} }
@Test @Test
@ -110,21 +105,21 @@ class Version22Test : JvmBaseUnitTest() {
val before = db.querySingle( val before = db.querySingle(
"select count(*) from repetitions where habit is null" "select count(*) from repetitions where habit is null"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(before, equalTo(1)) assertEquals(1, before)
migrateTo(22) migrateTo(22)
val after = db.querySingle( val after = db.querySingle(
"select count(*) from repetitions where habit is null" "select count(*) from repetitions where habit is null"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(after, equalTo(0)) assertEquals(0, after)
} }
@Test @Test
fun testDisallowNullHabit() { fun testDisallowNullHabit() {
migrateTo(22) migrateTo(22)
val exception = assertThrows(Exception::class.java) { val exception = assertFailsWith<Exception> {
db.run("insert into Repetitions(timestamp, value) values (5, 2)") db.run("insert into Repetitions(timestamp, value) values (5, 2)")
} }
assertThat(exception.message, Matchers.containsString("constraint")) assertContains(exception.message!!, "constraint")
} }
@Test @Test
@ -135,21 +130,21 @@ class Version22Test : JvmBaseUnitTest() {
val before = db.querySingle( val before = db.querySingle(
"select count(*) from repetitions where timestamp=100 and habit=0" "select count(*) from repetitions where timestamp=100 and habit=0"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(before, equalTo(3)) assertEquals(3, before)
migrateTo(22) migrateTo(22)
val after = db.querySingle( val after = db.querySingle(
"select count(*) from repetitions where timestamp=100 and habit=0" "select count(*) from repetitions where timestamp=100 and habit=0"
) { it.getInt(0) } ) { it.getInt(0) }
assertThat(after, equalTo(1)) assertEquals(1, after)
} }
@Test @Test
fun testDisallowNewDuplicateTimestamps() { fun testDisallowNewDuplicateTimestamps() {
migrateTo(22) migrateTo(22)
db.run("insert into repetitions(habit, timestamp, value)values (0, 100, 2)") db.run("insert into repetitions(habit, timestamp, value)values (0, 100, 2)")
val exception = assertThrows(Exception::class.java) { val exception = assertFailsWith<Exception> {
db.run("insert into repetitions(habit, timestamp, value)values (0, 100, 5)") db.run("insert into repetitions(habit, timestamp, value)values (0, 100, 5)")
} }
assertThat(exception.message, Matchers.containsString("constraint")) assertContains(exception.message!!, "constraint")
} }
} }

View File

@ -20,16 +20,14 @@
package org.isoron.uhabits.core.database.migrations package org.isoron.uhabits.core.database.migrations
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.platform.io.Database import org.isoron.platform.io.Database
import org.isoron.platform.io.JavaFileOpener
import org.isoron.platform.io.migrateTo import org.isoron.platform.io.migrateTo
import org.isoron.platform.io.query import org.isoron.platform.io.query
import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Test import kotlin.test.Test
import kotlin.test.assertEquals
class Version23Test : JvmBaseUnitTest() { class Version23Test : BaseUnitTest() {
private lateinit var db: Database private lateinit var db: Database
@ -38,8 +36,6 @@ class Version23Test : JvmBaseUnitTest() {
db = openDatabaseResource("/databases/022.db") db = openDatabaseResource("/databases/022.db")
} }
private val fileOpener = JavaFileOpener()
private fun migrateTo(version: Int) = runBlocking { private fun migrateTo(version: Int) = runBlocking {
db.migrateTo(version) { v -> db.migrateTo(version) { v ->
val path = "migrations/%02d.sql".format(v) val path = "migrations/%02d.sql".format(v)
@ -48,13 +44,13 @@ class Version23Test : JvmBaseUnitTest() {
} }
@Test @Test
fun `test migrate to 23 creates question column`() { fun testMigrateTo23CreatesQuestionColumn() {
migrateTo(23) migrateTo(23)
db.query("select question from Habits") {} db.query("select question from Habits") {}
} }
@Test @Test
fun `test migrate to 23 moves description to question column`() { fun testMigrateTo23MovesDescriptionToQuestionColumn() {
val descriptions = mutableListOf<String?>() val descriptions = mutableListOf<String?>()
db.query("select description from Habits") { stmt -> db.query("select description from Habits") { stmt ->
descriptions.add(stmt.getTextOrNull(0)) descriptions.add(stmt.getTextOrNull(0))
@ -68,15 +64,15 @@ class Version23Test : JvmBaseUnitTest() {
} }
for (i in descriptions.indices) { for (i in descriptions.indices) {
assertThat(questions[i], equalTo(descriptions[i])) assertEquals(descriptions[i], questions[i])
} }
} }
@Test @Test
fun `test migrate to 23 sets description to null`() { fun testMigrateTo23SetsDescriptionToNull() {
migrateTo(23) migrateTo(23)
db.query("select description from Habits") { stmt -> db.query("select description from Habits") { stmt ->
assertThat(stmt.getTextOrNull(0), equalTo("")) assertEquals("", stmt.getTextOrNull(0))
} }
} }
} }

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2016-2025 Álinson Santos Xavier <git@axavier.org>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.io
import kotlinx.coroutines.runBlocking
import org.isoron.platform.io.ZipReader
import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Habit
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.assertEquals
class HabitsCSVExporterTest : BaseUnitTest() {
override fun setUp() {
super.setUp()
habitList.add(fixtures.createShortHabit())
habitList.add(fixtures.createEmptyHabit())
}
@Test
fun testExportCSV() = runBlocking {
val selected: MutableList<Habit> = mutableListOf()
for (h in habitList) selected.add(h)
val exporter = HabitsCSVExporter(habitList, selected)
val bytes = exporter.writeArchive()
assertTrue(bytes.isNotEmpty())
val entries = ZipReader(bytes).entries()
val filesToCheck = arrayOf(
"001 Meditate/Checkmarks.csv",
"001 Meditate/Scores.csv",
"002 Wake up early/Checkmarks.csv",
"002 Wake up early/Scores.csv",
"Checkmarks.csv",
"Habits.csv",
"Scores.csv"
)
for (file in filesToCheck) {
val entry = entries.find { it.name == file }
assertNotNull(entry, "$file should exist in zip")
val expected = fileOpener.openResourceFile("csv_export/$file").lines()
assertEquals(expected, entry.content.trimEnd().lines(), "content mismatch for $file")
}
}
}

View File

@ -19,39 +19,27 @@
package org.isoron.uhabits.core.io package org.isoron.uhabits.core.io
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.IsEqual.equalTo
import org.isoron.platform.io.JavaFileOpener
import org.isoron.platform.io.JavaUserFile
import org.isoron.platform.time.LocalDate import org.isoron.platform.time.LocalDate
import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.models.Frequency import org.isoron.uhabits.core.models.Frequency
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitType import org.isoron.uhabits.core.models.HabitType
import org.junit.Before import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
import java.io.File
import java.io.IOException
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class ImportTest : JvmBaseUnitTest() { class ImportTest : BaseUnitTest() {
@Before
@Throws(Exception::class)
override fun setUp() {
super.setUp()
}
@Test @Test
@Throws(IOException::class)
fun testHabitBullCSV() { fun testHabitBullCSV() {
importFromFile("habitbull.csv") importFromFile("habitbull.csv")
assertThat(habitList.size(), equalTo(4)) assertEquals(4, habitList.size())
val habit = habitList.getByPosition(0) val habit = habitList.getByPosition(0)
assertThat(habit.name, equalTo("Breed dragons")) assertEquals("Breed dragons", habit.name)
assertThat(habit.description, equalTo("with love and fire")) assertEquals("with love and fire", habit.description)
assertThat(habit.frequency, equalTo(Frequency.DAILY)) assertEquals(Frequency.DAILY, habit.frequency)
assertTrue(isChecked(habit, 2016, 3, 18)) assertTrue(isChecked(habit, 2016, 3, 18))
assertTrue(isChecked(habit, 2016, 3, 19)) assertTrue(isChecked(habit, 2016, 3, 19))
assertFalse(isChecked(habit, 2016, 3, 20)) assertFalse(isChecked(habit, 2016, 3, 20))
@ -59,14 +47,13 @@ class ImportTest : JvmBaseUnitTest() {
} }
@Test @Test
@Throws(IOException::class)
fun testHabitBullCSV2() { fun testHabitBullCSV2() {
importFromFile("habitbull2.csv") importFromFile("habitbull2.csv")
assertThat(habitList.size(), equalTo(6)) assertEquals(6, habitList.size())
val habit = habitList.getByPosition(2) val habit = habitList.getByPosition(2)
assertThat(habit.name, equalTo("H3")) assertEquals("H3", habit.name)
assertThat(habit.description, equalTo("Habit 3")) assertEquals("Habit 3", habit.description)
assertThat(habit.frequency, equalTo(Frequency.DAILY)) assertEquals(Frequency.DAILY, habit.frequency)
assertTrue(isChecked(habit, 2019, 4, 11)) assertTrue(isChecked(habit, 2019, 4, 11))
assertTrue(isChecked(habit, 2019, 5, 7)) assertTrue(isChecked(habit, 2019, 5, 7))
assertFalse(isChecked(habit, 2019, 6, 14)) assertFalse(isChecked(habit, 2019, 6, 14))
@ -75,88 +62,83 @@ class ImportTest : JvmBaseUnitTest() {
} }
@Test @Test
@Throws(IOException::class)
fun testHabitBullCSV3() { fun testHabitBullCSV3() {
importFromFile("habitbull3.csv") importFromFile("habitbull3.csv")
assertThat(habitList.size(), equalTo(2)) assertEquals(2, habitList.size())
val habit = habitList.getByPosition(0) val habit = habitList.getByPosition(0)
assertThat(habit.name, equalTo("Pushups")) assertEquals("Pushups", habit.name)
assertThat(habit.type, equalTo(HabitType.NUMERICAL)) assertEquals(HabitType.NUMERICAL, habit.type)
assertThat(habit.description, equalTo("")) assertEquals("", habit.description)
assertThat(habit.frequency, equalTo(Frequency.DAILY)) assertEquals(Frequency.DAILY, habit.frequency)
assertThat(getValue(habit, 2021, 9, 1), equalTo(30000)) assertEquals(30000, getValue(habit, 2021, 9, 1))
assertThat(getValue(habit, 2022, 1, 8), equalTo(100000)) assertEquals(100000, getValue(habit, 2022, 1, 8))
val habit2 = habitList.getByPosition(1) val habit2 = habitList.getByPosition(1)
assertThat(habit2.name, equalTo("run")) assertEquals("run", habit2.name)
assertThat(habit2.type, equalTo(HabitType.YES_NO)) assertEquals(HabitType.YES_NO, habit2.type)
assertThat(habit2.description, equalTo("")) assertEquals("", habit2.description)
assertThat(habit2.frequency, equalTo(Frequency.DAILY)) assertEquals(Frequency.DAILY, habit2.frequency)
assertTrue(isChecked(habit2, 2022, 1, 3)) assertTrue(isChecked(habit2, 2022, 1, 3))
assertTrue(isChecked(habit2, 2022, 1, 18)) assertTrue(isChecked(habit2, 2022, 1, 18))
assertTrue(isChecked(habit2, 2022, 1, 19)) assertTrue(isChecked(habit2, 2022, 1, 19))
} }
@Test @Test
@Throws(IOException::class)
fun testHabitBullCSV4() { fun testHabitBullCSV4() {
importFromFile("habitbull4.csv") importFromFile("habitbull4.csv")
assertThat(habitList.size(), equalTo(1)) assertEquals(1, habitList.size())
val habit = habitList.getByPosition(0) val habit = habitList.getByPosition(0)
assertThat(habit.name, equalTo("Caffeine")) assertEquals("Caffeine", habit.name)
assertThat(habit.type, equalTo(HabitType.NUMERICAL)) assertEquals(HabitType.NUMERICAL, habit.type)
assertThat(habit.description, equalTo("")) assertEquals("", habit.description)
assertThat(habit.frequency, equalTo(Frequency.DAILY)) assertEquals(Frequency.DAILY, habit.frequency)
assertThat(getValue(habit, 2022, 11, 21), equalTo(80000)) assertEquals(80000, getValue(habit, 2022, 11, 21))
assertThat(getValue(habit, 2022, 11, 22), equalTo(80000)) assertEquals(80000, getValue(habit, 2022, 11, 22))
} }
@Test @Test
@Throws(IOException::class)
fun testLoopDB() { fun testLoopDB() {
importFromFile("loop.db") importFromFile("loop.db")
assertThat(habitList.size(), equalTo(9)) assertEquals(9, habitList.size())
val habit = habitList.getByPosition(0) val habit = habitList.getByPosition(0)
assertThat(habit.name, equalTo("Wake up early")) assertEquals("Wake up early", habit.name)
assertThat(habit.frequency, equalTo(Frequency.THREE_TIMES_PER_WEEK)) assertEquals(Frequency.THREE_TIMES_PER_WEEK, habit.frequency)
assertTrue(isChecked(habit, 2016, 3, 14)) assertTrue(isChecked(habit, 2016, 3, 14))
assertTrue(isChecked(habit, 2016, 3, 16)) assertTrue(isChecked(habit, 2016, 3, 16))
assertFalse(isChecked(habit, 2016, 3, 17)) assertFalse(isChecked(habit, 2016, 3, 17))
} }
@Test @Test
@Throws(IOException::class)
fun testRewireDB() { fun testRewireDB() {
importFromFile("rewire.db") importFromFile("rewire.db")
assertThat(habitList.size(), equalTo(3)) assertEquals(3, habitList.size())
var habit = habitList.getByPosition(1) var habit = habitList.getByPosition(1)
assertThat(habit.name, equalTo("Wake up early")) assertEquals("Wake up early", habit.name)
assertThat(habit.frequency, equalTo(Frequency.THREE_TIMES_PER_WEEK)) assertEquals(Frequency.THREE_TIMES_PER_WEEK, habit.frequency)
assertFalse(habit.hasReminder()) assertFalse(habit.hasReminder())
assertFalse(isChecked(habit, 2015, 12, 31)) assertFalse(isChecked(habit, 2015, 12, 31))
assertTrue(isChecked(habit, 2016, 1, 18)) assertTrue(isChecked(habit, 2016, 1, 18))
assertTrue(isChecked(habit, 2016, 1, 28)) assertTrue(isChecked(habit, 2016, 1, 28))
assertFalse(isChecked(habit, 2016, 3, 10)) assertFalse(isChecked(habit, 2016, 3, 10))
habit = habitList.getByPosition(2) habit = habitList.getByPosition(2)
assertThat(habit.name, equalTo("brush teeth")) assertEquals("brush teeth", habit.name)
assertThat(habit.frequency, equalTo(Frequency.THREE_TIMES_PER_WEEK)) assertEquals(Frequency.THREE_TIMES_PER_WEEK, habit.frequency)
assertThat(habit.hasReminder(), equalTo(true)) assertEquals(true, habit.hasReminder())
val reminder = habit.reminder val reminder = habit.reminder
assertThat(reminder!!.hour, equalTo(8)) assertEquals(8, reminder!!.hour)
assertThat(reminder.minute, equalTo(0)) assertEquals(0, reminder.minute)
val reminderDays = booleanArrayOf(false, true, true, true, true, true, false) val reminderDays = booleanArrayOf(false, true, true, true, true, true, false)
assertThat(reminder.days.toArray(), equalTo(reminderDays)) assertEquals(reminderDays.toList(), reminder.days.toArray().toList())
} }
@Test @Test
@Throws(IOException::class)
fun testTickmateDB() { fun testTickmateDB() {
importFromFile("tickmate.db") importFromFile("tickmate.db")
assertThat(habitList.size(), equalTo(3)) assertEquals(3, habitList.size())
val h = habitList.getByPosition(2) val h = habitList.getByPosition(2)
assertThat(h.name, equalTo("Vegan")) assertEquals("Vegan", h.name)
assertTrue(isChecked(h, 2016, 1, 24)) assertTrue(isChecked(h, 2016, 1, 24))
assertTrue(isChecked(h, 2016, 2, 5)) assertTrue(isChecked(h, 2016, 2, 5))
assertTrue(isChecked(h, 2016, 3, 18)) assertTrue(isChecked(h, 2016, 3, 18))
@ -175,13 +157,9 @@ class ImportTest : JvmBaseUnitTest() {
return h.originalEntries.get(LocalDate(year, month, day)).notes == notes return h.originalEntries.get(LocalDate(year, month, day)).notes == notes
} }
@Throws(IOException::class)
private fun importFromFile(assetFilename: String) = runBlocking { private fun importFromFile(assetFilename: String) = runBlocking {
val file = File.createTempFile("asset", "") val userFile = copyResourceToTempFile(assetFilename)
copyAssetToFile(assetFilename, file) assertTrue(userFile.exists())
assertTrue(file.exists())
assertTrue(file.canRead())
val userFile = JavaUserFile(file.toPath())
val importer = GenericImporter( val importer = GenericImporter(
LoopDBImporter( LoopDBImporter(
habitList, habitList,
@ -189,7 +167,7 @@ class ImportTest : JvmBaseUnitTest() {
databaseOpener, databaseOpener,
commandRunner, commandRunner,
StandardLogging(), StandardLogging(),
JavaFileOpener() fileOpener
), ),
RewireDBImporter(habitList, modelFactory, databaseOpener), RewireDBImporter(habitList, modelFactory, databaseOpener),
TickmateDBImporter(habitList, modelFactory, databaseOpener), TickmateDBImporter(habitList, modelFactory, databaseOpener),
@ -197,6 +175,6 @@ class ImportTest : JvmBaseUnitTest() {
) )
assertTrue(importer.canHandle(userFile)) assertTrue(importer.canHandle(userFile))
importer.importHabitsFromFile(userFile) importer.importHabitsFromFile(userFile)
file.delete() userFile.delete()
} }
} }

View File

@ -20,9 +20,7 @@ package org.isoron.uhabits.core.models.sqlite
import dev.mokkery.mock import dev.mokkery.mock
import dev.mokkery.verify import dev.mokkery.verify
import org.hamcrest.CoreMatchers.equalTo import org.isoron.uhabits.core.BaseUnitTest
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.core.JvmBaseUnitTest
import org.isoron.uhabits.core.database.HabitRepository import org.isoron.uhabits.core.database.HabitRepository
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitList
@ -31,20 +29,19 @@ import org.isoron.uhabits.core.models.ModelObservable
import org.isoron.uhabits.core.models.Reminder import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList import org.isoron.uhabits.core.models.WeekdayList
import org.isoron.uhabits.core.test.HabitFixtures import org.isoron.uhabits.core.test.HabitFixtures
import org.junit.After import kotlin.test.AfterTest
import org.junit.Assert.assertThrows import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
import java.util.ArrayList import kotlin.test.assertFailsWith
import kotlin.test.assertNull import kotlin.test.assertNull
class SQLiteHabitListTest : JvmBaseUnitTest() { class SQLiteHabitListTest : BaseUnitTest() {
private lateinit var repository: HabitRepository private lateinit var repository: HabitRepository
private var listener: ModelObservable.Listener = mock() private var listener: ModelObservable.Listener = mock()
private lateinit var habitsArray: ArrayList<Habit> private lateinit var habitsArray: ArrayList<Habit>
private lateinit var activeHabits: HabitList private lateinit var activeHabits: HabitList
private lateinit var reminderHabits: HabitList private lateinit var reminderHabits: HabitList
@Throws(Exception::class)
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
val db = buildMemoryDatabase() val db = buildMemoryDatabase()
@ -75,7 +72,7 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
habitList.observable.addListener(listener) habitList.observable.addListener(listener)
} }
@After @AfterTest
fun tearDown() { fun tearDown() {
habitList.observable.removeListener(listener) habitList.observable.removeListener(listener)
} }
@ -85,7 +82,7 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
val habit = modelFactory.buildHabit() val habit = modelFactory.buildHabit()
habitList.add(habit) habitList.add(habit)
verify { listener.onModelChange() } verify { listener.onModelChange() }
assertThrows(IllegalArgumentException::class.java) { assertFailsWith<IllegalArgumentException> {
habitList.add(habit) habitList.add(habit)
} }
} }
@ -96,10 +93,10 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
habit.name = "Hello world with id" habit.name = "Hello world with id"
habit.id = 12300L habit.id = 12300L
habitList.add(habit) habitList.add(habit)
assertThat(habit.id, equalTo(12300L)) assertEquals(12300L, habit.id)
val all = repository.findAll() val all = repository.findAll()
val record = all.find { it.id == 12300L } val record = all.find { it.id == 12300L }
assertThat(record!!.name, equalTo(habit.name)) assertEquals(habit.name, record!!.name)
} }
@Test @Test
@ -110,20 +107,20 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
habitList.add(habit) habitList.add(habit)
val all = repository.findAll() val all = repository.findAll()
val record = all.find { it.id == habit.id } val record = all.find { it.id == habit.id }
assertThat(record!!.name, equalTo(habit.name)) assertEquals(habit.name, record!!.name)
} }
@Test @Test
fun testSize() { fun testSize() {
assertThat(habitList.size(), equalTo(10)) assertEquals(10, habitList.size())
} }
@Test @Test
fun testGetById() { fun testGetById() {
val h1 = habitList.getById(1)!! val h1 = habitList.getById(1)!!
assertThat(h1.name, equalTo("habit 1")) assertEquals("habit 1", h1.name)
val h2 = habitList.getById(2)!! val h2 = habitList.getById(2)!!
assertThat(h2, equalTo(h2)) assertEquals(h2, h2)
} }
@Test @Test
@ -136,31 +133,30 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
@Test @Test
fun testGetByPosition() { fun testGetByPosition() {
val h = habitList.getByPosition(4) val h = habitList.getByPosition(4)
assertThat(h.name, equalTo("habit 5")) assertEquals("habit 5", h.name)
} }
@Test @Test
fun testIndexOf() { fun testIndexOf() {
val h1 = habitList.getByPosition(5) val h1 = habitList.getByPosition(5)
assertThat(habitList.indexOf(h1), equalTo(5)) assertEquals(5, habitList.indexOf(h1))
val h2 = modelFactory.buildHabit() val h2 = modelFactory.buildHabit()
assertThat(habitList.indexOf(h2), equalTo(-1)) assertEquals(-1, habitList.indexOf(h2))
h2.id = 1000L h2.id = 1000L
assertThat(habitList.indexOf(h2), equalTo(-1)) assertEquals(-1, habitList.indexOf(h2))
} }
@Test @Test
@Throws(Exception::class)
fun testRemove() { fun testRemove() {
val h = habitList.getById(2) val h = habitList.getById(2)
habitList.remove(h!!) habitList.remove(h!!)
assertThat(habitList.indexOf(h), equalTo(-1)) assertEquals(-1, habitList.indexOf(h))
val all = repository.findAll() val all = repository.findAll()
val rec2 = all.find { it.id == 2L } val rec2 = all.find { it.id == 2L }
assertNull(rec2) assertNull(rec2)
val rec3 = all.find { it.id == 3L }!! val rec3 = all.find { it.id == 3L }!!
assertThat(rec3.position, equalTo(1)) assertEquals(1, rec3.position)
} }
@Test @Test
@ -168,13 +164,13 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
habitList.primaryOrder = HabitList.Order.BY_NAME_DESC habitList.primaryOrder = HabitList.Order.BY_NAME_DESC
val h = habitList.getById(2) val h = habitList.getById(2)
habitList.remove(h!!) habitList.remove(h!!)
assertThat(habitList.indexOf(h), equalTo(-1)) assertEquals(-1, habitList.indexOf(h))
val all = repository.findAll() val all = repository.findAll()
val rec2 = all.find { it.id == 2L } val rec2 = all.find { it.id == 2L }
assertNull(rec2) assertNull(rec2)
val rec3 = all.find { it.id == 3L }!! val rec3 = all.find { it.id == 3L }!!
assertThat(rec3.position, equalTo(1)) assertEquals(1, rec3.position)
} }
@Test @Test
@ -184,8 +180,8 @@ class SQLiteHabitListTest : JvmBaseUnitTest() {
habitList.reorder(habit4, habit3) habitList.reorder(habit4, habit3)
val all = repository.findAll() val all = repository.findAll()
val record3 = all.find { it.id == 3L }!! val record3 = all.find { it.id == 3L }!!
assertThat(record3.position, equalTo(3)) assertEquals(3, record3.position)
val record4 = all.find { it.id == 4L }!! val record4 = all.find { it.id == 4L }!!
assertThat(record4.position, equalTo(2)) assertEquals(2, record4.position)
} }
} }

View File

@ -25,24 +25,22 @@ import dev.mokkery.every
import dev.mokkery.matcher.any import dev.mokkery.matcher.any
import dev.mokkery.mock import dev.mokkery.mock
import dev.mokkery.resetCalls import dev.mokkery.resetCalls
import dev.mokkery.spy
import dev.mokkery.verify import dev.mokkery.verify
import org.apache.commons.io.FileUtils import kotlinx.coroutines.runBlocking
import org.hamcrest.MatcherAssert.assertThat import org.isoron.platform.io.UserFile
import org.hamcrest.core.IsEqual.equalTo
import org.isoron.platform.io.JavaUserFile
import org.isoron.platform.time.getToday import org.isoron.platform.time.getToday
import org.isoron.uhabits.core.JvmBaseUnitTest import org.isoron.uhabits.core.BaseUnitTest
import org.isoron.uhabits.core.models.Entry import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.preferences.Preferences import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.ui.callbacks.NumberPickerCallback import org.isoron.uhabits.core.ui.callbacks.NumberPickerCallback
import org.junit.Before import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
import java.nio.file.Files
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class ListHabitsBehaviorTest : JvmBaseUnitTest() { class ListHabitsBehaviorTest : BaseUnitTest() {
private val dirFinder: ListHabitsBehavior.DirFinder = mock() private val dirFinder: ListHabitsBehavior.DirFinder = mock()
private val prefs: Preferences = mock() private val prefs: Preferences = mock()
@ -56,15 +54,13 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() {
private val bugReporter: ListHabitsBehavior.BugReporter = mock() private val bugReporter: ListHabitsBehavior.BugReporter = mock()
@Before
@Throws(Exception::class)
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
habit1 = fixtures.createShortHabit() habit1 = fixtures.createShortHabit()
habit2 = fixtures.createNumericalHabit() habit2 = fixtures.createNumericalHabit()
habitList.add(habit1) habitList.add(habit1)
habitList.add(habit2) habitList.add(habit2)
resetCalls(habitList) habitList = spy(habitList)
behavior = ListHabitsBehavior( behavior = ListHabitsBehavior(
habitList, habitList,
dirFinder, dirFinder,
@ -90,29 +86,26 @@ class ListHabitsBehaviorTest : JvmBaseUnitTest() {
screen.showNumberPopup(0.1, "", any()) screen.showNumberPopup(0.1, "", any())
} }
capturedPicker!!.onNumberPicked(100.0, "") capturedPicker!!.onNumberPicked(100.0, "")
assertThat(habit2.computedEntries.get(today).value, equalTo(100000)) assertEquals(100000, habit2.computedEntries.get(today).value)
} }
@Test @Test
@Throws(Exception::class)
fun testOnExportCSV() { fun testOnExportCSV() {
val outputDir = Files.createTempDirectory("CSV").toFile() val outputDir = createTempDir()
every { dirFinder.getCSVOutputDir() } returns JavaUserFile(outputDir.toPath()) every { dirFinder.getCSVOutputDir() } returns outputDir
behavior.onExportCSV() behavior.onExportCSV()
verify { screen.showSendFileScreen(any()) } verify { screen.showSendFileScreen(any()) }
assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1)) val files = runBlocking { outputDir.listFiles() }
FileUtils.deleteDirectory(outputDir) assertEquals(1, files!!.size)
} }
@Test @Test
@Throws(Exception::class)
fun testOnExportCSV_fail() { fun testOnExportCSV_fail() {
val outputDir = Files.createTempDirectory("CSV").toFile() val mockDir: UserFile = mock()
outputDir.setWritable(false) every { mockDir.resolve(any()) } throws RuntimeException("not writable")
every { dirFinder.getCSVOutputDir() } returns JavaUserFile(outputDir.toPath()) every { dirFinder.getCSVOutputDir() } returns mockDir
behavior.onExportCSV() behavior.onExportCSV()
verify { screen.showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT) } verify { screen.showMessage(ListHabitsBehavior.Message.COULD_NOT_EXPORT) }
assertTrue(outputDir.delete())
} }
@Test @Test

View File

@ -22,22 +22,18 @@ import dev.mokkery.answering.returns
import dev.mokkery.every import dev.mokkery.every
import dev.mokkery.mock import dev.mokkery.mock
import dev.mokkery.verify import dev.mokkery.verify
import org.apache.commons.io.FileUtils import kotlinx.coroutines.runBlocking
import org.hamcrest.CoreMatchers.equalTo import org.isoron.uhabits.core.BaseUnitTest
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.platform.io.JavaUserFile
import org.isoron.uhabits.core.JvmBaseUnitTest
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.junit.Test import kotlin.test.Test
import java.nio.file.Files import kotlin.test.assertEquals
class ShowHabitMenuPresenterTest : JvmBaseUnitTest() { class ShowHabitMenuPresenterTest : BaseUnitTest() {
private lateinit var system: ShowHabitMenuPresenter.System private lateinit var system: ShowHabitMenuPresenter.System
private lateinit var screen: ShowHabitMenuPresenter.Screen private lateinit var screen: ShowHabitMenuPresenter.Screen
private lateinit var habit: Habit private lateinit var habit: Habit
private lateinit var menu: ShowHabitMenuPresenter private lateinit var menu: ShowHabitMenuPresenter
@Throws(Exception::class)
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
system = mock() system = mock()
@ -60,12 +56,11 @@ class ShowHabitMenuPresenterTest : JvmBaseUnitTest() {
} }
@Test @Test
@Throws(Exception::class)
fun testOnExport() { fun testOnExport() {
val outputDir = Files.createTempDirectory("CSV").toFile() val outputDir = createTempDir()
every { system.getCSVOutputDir() } returns JavaUserFile(outputDir.toPath()) every { system.getCSVOutputDir() } returns outputDir
menu.onExportCSV() menu.onExportCSV()
assertThat(FileUtils.listFiles(outputDir, null, false).size, equalTo(1)) val files = runBlocking { outputDir.listFiles() }
FileUtils.deleteDirectory(outputDir) assertEquals(1, files!!.size)
} }
} }

View File

@ -0,0 +1,16 @@
package org.isoron.uhabits.core.utils
import kotlinx.coroutines.runBlocking
import org.isoron.uhabits.core.BaseUnitTest
import kotlin.test.Test
import kotlin.test.assertTrue
class FileExtensionsTest : BaseUnitTest() {
@Test
fun testIsSQLite3File() = runBlocking {
val userFile = copyResourceToTempFile("loop.db")
val isSqlite3File = isSQLite3File(userFile)
assertTrue(isSqlite3File)
}
}

View File

@ -103,6 +103,15 @@ class JavaUserFile(val path: Path) : UserFile {
override fun resolve(child: String): UserFile { override fun resolve(child: String): UserFile {
return JavaUserFile(path.resolve(child)) return JavaUserFile(path.resolve(child))
} }
override suspend fun listFiles(): List<UserFile>? {
val files = path.toFile().listFiles() ?: return null
return files.map { JavaUserFile(it.toPath()) }
}
override suspend fun mkdirs() {
path.toFile().mkdirs()
}
} }
@Suppress("NewApi") @Suppress("NewApi")

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2016-2025 Álinson Santos Xavier <git@axavier.org>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.platform.io
import java.io.ByteArrayInputStream
import java.util.zip.ZipInputStream
actual class ZipReader actual constructor(bytes: ByteArray) {
private val data = bytes
actual fun entries(): List<ZipEntry> {
val result = mutableListOf<ZipEntry>()
val zis = ZipInputStream(ByteArrayInputStream(data))
var entry = zis.nextEntry
while (entry != null) {
result.add(ZipEntry(entry.name, zis.readBytes().decodeToString()))
zis.closeEntry()
entry = zis.nextEntry
}
zis.close()
return result
}
}

View File

@ -20,7 +20,6 @@
package org.isoron.platform.io package org.isoron.platform.io
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
actual class ZipWriter { actual class ZipWriter {
@ -28,7 +27,7 @@ actual class ZipWriter {
private val zos = ZipOutputStream(baos) private val zos = ZipOutputStream(baos)
actual fun addEntry(name: String, content: String) { actual fun addEntry(name: String, content: String) {
zos.putNextEntry(ZipEntry(name)) zos.putNextEntry(java.util.zip.ZipEntry(name))
zos.write(content.toByteArray()) zos.write(content.toByteArray())
zos.closeEntry() zos.closeEntry()
} }

View File

@ -0,0 +1,4 @@
package org.isoron.platform.io
actual fun createTestFileOpener(): FileOpener = JavaFileOpener()
actual fun createTestDatabaseOpener(): DatabaseOpener = JavaDatabaseOpener()

View File

@ -20,7 +20,6 @@ package org.isoron.uhabits.core
import dev.mokkery.spy import dev.mokkery.spy
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.isoron.platform.io.JavaDatabaseOpener
import org.isoron.uhabits.core.models.memory.MemoryModelFactory import org.isoron.uhabits.core.models.memory.MemoryModelFactory
import org.isoron.uhabits.core.test.HabitFixtures import org.isoron.uhabits.core.test.HabitFixtures
import org.junit.Before import org.junit.Before
@ -35,8 +34,6 @@ import java.util.GregorianCalendar
import java.util.TimeZone import java.util.TimeZone
open class JvmBaseUnitTest : BaseUnitTest() { open class JvmBaseUnitTest : BaseUnitTest() {
protected var databaseOpener: org.isoron.platform.io.DatabaseOpener = JavaDatabaseOpener()
@Before @Before
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
@ -75,13 +72,4 @@ open class JvmBaseUnitTest : BaseUnitTest() {
if (inputStream != null) return inputStream if (inputStream != null) return inputStream
throw IllegalStateException("asset not found: $fullPath") throw IllegalStateException("asset not found: $fullPath")
} }
@Throws(IOException::class)
protected fun openDatabaseResource(path: String): org.isoron.platform.io.Database {
val original = openAsset(path)
val tmpDbFile = File.createTempFile("database", ".db")
tmpDbFile.deleteOnExit()
IOUtils.copy(original, FileOutputStream(tmpDbFile))
return databaseOpener.open(tmpDbFile.absolutePath)
}
} }

View File

@ -1,106 +0,0 @@
/*
* Copyright (C) 2016-2025 Álinson Santos Xavier <git@axavier.org>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.core.io
import kotlinx.coroutines.runBlocking
import org.isoron.uhabits.core.JvmBaseUnitTest
import org.isoron.uhabits.core.models.Habit
import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.util.*
import java.util.zip.ZipInputStream
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class HabitsCSVExporterTest : JvmBaseUnitTest() {
private lateinit var baseDir: File
@Before
@Throws(Exception::class)
override fun setUp() {
super.setUp()
habitList.add(fixtures.createShortHabit())
habitList.add(fixtures.createEmptyHabit())
baseDir = Files.createTempDirectory("csv").toFile()
baseDir.deleteOnExit()
}
@Test
@Throws(IOException::class)
fun testExportCSV() = runBlocking {
val selected: MutableList<Habit> = LinkedList()
for (h in habitList) selected.add(h)
val exporter = HabitsCSVExporter(habitList, selected)
val bytes = exporter.writeArchive()
assertTrue(bytes.isNotEmpty())
// Extract zip entries to baseDir for comparison
unzip(bytes)
val filesToCheck = arrayOf(
"001 Meditate/Checkmarks.csv",
"001 Meditate/Scores.csv",
"002 Wake up early/Checkmarks.csv",
"002 Wake up early/Scores.csv",
"Checkmarks.csv",
"Habits.csv",
"Scores.csv"
)
for (file in filesToCheck) {
assertPathExists(file)
assertFileAndReferenceAreEqual(file)
}
}
private fun unzip(bytes: ByteArray) {
val zis = ZipInputStream(ByteArrayInputStream(bytes))
var entry = zis.nextEntry
while (entry != null) {
val outFile = File(baseDir, entry.name)
outFile.parentFile?.mkdirs()
outFile.writeBytes(zis.readBytes())
zis.closeEntry()
entry = zis.nextEntry
}
zis.close()
}
private fun assertPathExists(s: String) {
val file = File(baseDir, s)
assertTrue(
String.format("File %s should exist", file.absolutePath)
) { file.exists() }
}
private fun assertFileAndReferenceAreEqual(s: String) {
val assetFilename = String.format("csv_export/%s", s)
val actualFile = File(baseDir, s)
val expectedFile = File.createTempFile("asset", "")
expectedFile.deleteOnExit()
copyAssetToFile(assetFilename, expectedFile)
val actualContents = actualFile.readText()
val expectedContents = expectedFile.readText()
assertEquals(expectedContents, actualContents, "content mismatch for $s")
}
}

View File

@ -1,20 +0,0 @@
package org.isoron.uhabits.core.utils
import kotlinx.coroutines.runBlocking
import org.isoron.platform.io.JavaUserFile
import org.isoron.uhabits.core.JvmBaseUnitTest
import org.junit.Test
import java.io.File
import kotlin.test.assertTrue
class FileExtensionsTest : JvmBaseUnitTest() {
@Test
fun testIsSQLite3File() = runBlocking {
val file = File.createTempFile("asset", "")
copyAssetToFile("loop.db", file)
val userFile = JavaUserFile(file.toPath())
val isSqlite3File = isSQLite3File(userFile)
assertTrue(isSqlite3File)
}
}