mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Move IosFiles implementation to Kotlin; setup tests for ios target
This commit is contained in:
@@ -91,5 +91,38 @@ kotlin {
|
|||||||
implementation kotlin('test-js')
|
implementation kotlin('test-js')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iosMain {
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.2.0-alpha-2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iosTest {
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.2.0-alpha-2'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task iosTestCopyResources(type: Copy) {
|
||||||
|
dependsOn 'linkTestIos'
|
||||||
|
from 'assets/test/'
|
||||||
|
from 'assets/main/'
|
||||||
|
into 'build/bin/ios/testDebugExecutable'
|
||||||
|
}
|
||||||
|
|
||||||
|
task iosTest {
|
||||||
|
dependsOn 'linkTestIos', 'iosTestCopyResources'
|
||||||
|
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||||
|
description = "Runs tests on iOS simulator"
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
def emulatorName = "iPhone 8 Plus"
|
||||||
|
def binary = kotlin.targets.ios.compilations.test.getBinary('EXECUTABLE', 'DEBUG')
|
||||||
|
exec {
|
||||||
|
commandLine 'xcrun', 'simctl', 'spawn', emulatorName, binary.absolutePath
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,3 +7,5 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enableFeaturePreview("GRADLE_METADATA")
|
||||||
@@ -26,34 +26,31 @@ import org.isoron.uhabits.components.*
|
|||||||
import org.isoron.uhabits.i18n.*
|
import org.isoron.uhabits.i18n.*
|
||||||
import org.isoron.uhabits.models.*
|
import org.isoron.uhabits.models.*
|
||||||
|
|
||||||
//class Backend(databaseName: String,
|
class Backend(databaseName: String,
|
||||||
// databaseOpener: DatabaseOpener,
|
databaseOpener: DatabaseOpener,
|
||||||
// fileOpener: FileOpener,
|
fileOpener: FileOpener,
|
||||||
// localeHelper: LocaleHelper,
|
localeHelper: LocaleHelper,
|
||||||
// val log: Log,
|
private val log: Log,
|
||||||
// val taskRunner: TaskRunner) {
|
private val taskRunner: TaskRunner) {
|
||||||
|
|
||||||
|
// private val database: Database
|
||||||
//
|
//
|
||||||
// val database: Database
|
// private val habitsRepository: HabitRepository
|
||||||
//
|
//
|
||||||
// val habitsRepository: HabitRepository
|
// private val checkmarkRepository: CheckmarkRepository
|
||||||
//
|
//
|
||||||
// val checkmarkRepository: CheckmarkRepository
|
// private val habits = mutableMapOf<Int, Habit>()
|
||||||
//
|
//
|
||||||
// val habits = mutableMapOf<Int, Habit>()
|
// private val checkmarks = mutableMapOf<Habit, CheckmarkList>()
|
||||||
//
|
//
|
||||||
// val checkmarks = mutableMapOf<Habit, CheckmarkList>()
|
// private val scores = mutableMapOf<Habit, ScoreList>()
|
||||||
//
|
|
||||||
// val scores = mutableMapOf<Habit, ScoreList>()
|
val mainScreenDataSource: MainScreenDataSource? = null
|
||||||
//
|
val strings = localeHelper.getStringsForCurrentLocale()
|
||||||
// val mainScreenDataSource: MainScreenDataSource
|
val preferences: Preferences? = null
|
||||||
//
|
var theme: Theme = LightTheme()
|
||||||
// val strings = localeHelper.getStringsForCurrentLocale()
|
|
||||||
//
|
init {
|
||||||
// val preferences: Preferences
|
|
||||||
//
|
|
||||||
// var theme: Theme = LightTheme()
|
|
||||||
//
|
|
||||||
// init {
|
|
||||||
// val dbFile = fileOpener.openUserFile(databaseName)
|
// val dbFile = fileOpener.openUserFile(databaseName)
|
||||||
// if (!dbFile.exists()) {
|
// if (!dbFile.exists()) {
|
||||||
// val templateFile = fileOpener.openResourceFile("databases/template.db")
|
// val templateFile = fileOpener.openResourceFile("databases/template.db")
|
||||||
@@ -78,9 +75,9 @@ import org.isoron.uhabits.models.*
|
|||||||
// checkmarks,
|
// checkmarks,
|
||||||
// scores,
|
// scores,
|
||||||
// taskRunner)
|
// taskRunner)
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// fun createHabit(habit: Habit) {
|
fun createHabit(habit: Habit) {
|
||||||
// val id = habitsRepository.nextId()
|
// val id = habitsRepository.nextId()
|
||||||
// habit.id = id
|
// habit.id = id
|
||||||
// habit.position = habits.size
|
// habit.position = habits.size
|
||||||
@@ -88,20 +85,20 @@ import org.isoron.uhabits.models.*
|
|||||||
// checkmarks[habit] = CheckmarkList(habit.frequency, habit.type)
|
// checkmarks[habit] = CheckmarkList(habit.frequency, habit.type)
|
||||||
// habitsRepository.insert(habit)
|
// habitsRepository.insert(habit)
|
||||||
// mainScreenDataSource.requestData()
|
// mainScreenDataSource.requestData()
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// fun deleteHabit(id: Int) {
|
fun deleteHabit(id: Int) {
|
||||||
// habits[id]?.let { habit ->
|
// habits[id]?.let { habit ->
|
||||||
// habitsRepository.delete(habit)
|
// habitsRepository.delete(habit)
|
||||||
// habits.remove(id)
|
// habits.remove(id)
|
||||||
// mainScreenDataSource.requestData()
|
// mainScreenDataSource.requestData()
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// fun updateHabit(modified: Habit) {
|
fun updateHabit(modified: Habit) {
|
||||||
// habits[modified.id]?.let { existing ->
|
// habits[modified.id]?.let { existing ->
|
||||||
// modified.position = existing.position
|
// modified.position = existing.position
|
||||||
// habitsRepository.update(modified)
|
// habitsRepository.update(modified)
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,16 @@ class FilesTest() : BaseTest() {
|
|||||||
suspend fun testLines() {
|
suspend fun testLines() {
|
||||||
val fileOpener = resolver.getFileOpener()
|
val fileOpener = resolver.getFileOpener()
|
||||||
|
|
||||||
assertFalse(fileOpener.openUserFile("non-existing.txt").exists())
|
assertFalse(fileOpener.openUserFile("non-existing-usr.txt").exists(),
|
||||||
assertFalse(fileOpener.openResourceFile("non-existing.txt").exists())
|
"non-existing-usr.txt shouldn't exist")
|
||||||
|
|
||||||
|
assertFalse(fileOpener.openResourceFile("non-existing-res.txt").exists(),
|
||||||
|
"non-existing-res.txt shouldn't exist")
|
||||||
|
|
||||||
val hello = fileOpener.openResourceFile("hello.txt")
|
val hello = fileOpener.openResourceFile("hello.txt")
|
||||||
|
assertTrue(hello.exists(), "hello.txt should exist")
|
||||||
var lines = hello.lines()
|
var lines = hello.lines()
|
||||||
|
assertEquals(2, lines.size)
|
||||||
assertEquals("Hello World!", lines[0])
|
assertEquals("Hello World!", lines[0])
|
||||||
assertEquals("This is a resource.", lines[1])
|
assertEquals("This is a resource.", lines[1])
|
||||||
|
|
||||||
@@ -40,13 +45,13 @@ class FilesTest() : BaseTest() {
|
|||||||
assertEquals("Hello World!", lines[0])
|
assertEquals("Hello World!", lines[0])
|
||||||
assertEquals("This is a resource.", lines[1])
|
assertEquals("This is a resource.", lines[1])
|
||||||
|
|
||||||
assertTrue(helloCopy.exists())
|
assertTrue(helloCopy.exists(), "helloCopy should exist")
|
||||||
helloCopy.delete()
|
helloCopy.delete()
|
||||||
assertFalse(helloCopy.exists())
|
assertFalse(helloCopy.exists(), "helloCopy shouldn't exist")
|
||||||
|
|
||||||
|
|
||||||
val migration = fileOpener.openResourceFile("migrations/012.sql")
|
val migration = fileOpener.openResourceFile("migrations/012.sql")
|
||||||
assertTrue(migration.exists())
|
assertTrue(migration.exists(), "migrations/012.sql should exist")
|
||||||
lines = migration.lines()
|
lines = migration.lines()
|
||||||
assertEquals("delete from Score", lines[0])
|
assertEquals("delete from Score", lines[0])
|
||||||
}
|
}
|
||||||
|
|||||||
59
core/src/iosMain/kotlin/org/isoron/platform/io/IosFiles.kt
Normal file
59
core/src/iosMain/kotlin/org/isoron/platform/io/IosFiles.kt
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2019 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UNCHECKED_CAST")
|
||||||
|
|
||||||
|
package org.isoron.platform.io
|
||||||
|
|
||||||
|
import platform.Foundation.*
|
||||||
|
|
||||||
|
class IosFileOpener : FileOpener {
|
||||||
|
override fun openResourceFile(path: String): ResourceFile {
|
||||||
|
val resPath = NSBundle.mainBundle.resourcePath!!
|
||||||
|
return IosFile("$resPath/$path")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun openUserFile(path: String): UserFile {
|
||||||
|
val manager = NSFileManager.defaultManager
|
||||||
|
val basePath = manager.URLsForDirectory(NSDocumentDirectory,
|
||||||
|
NSUserDomainMask) as List<NSURL>
|
||||||
|
val filePath = basePath.first().URLByAppendingPathComponent(path)!!.path!!
|
||||||
|
return IosFile(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IosFile(val path: String) : UserFile, ResourceFile {
|
||||||
|
override suspend fun delete() {
|
||||||
|
NSFileManager.defaultManager.removeItemAtPath(path, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun exists(): Boolean {
|
||||||
|
return NSFileManager.defaultManager.fileExistsAtPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun lines(): List<String> {
|
||||||
|
if (!exists()) throw Exception("File not found: $path")
|
||||||
|
val contents = NSString.stringWithContentsOfFile(path)
|
||||||
|
return contents.toString().lines()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun copyTo(dest: UserFile) {
|
||||||
|
NSFileManager.defaultManager.copyItemAtPath(path, (dest as IosFile).path, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,14 +17,34 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("UNCHECKED_CAST")
|
||||||
|
|
||||||
package org.isoron
|
package org.isoron
|
||||||
|
|
||||||
import org.isoron.platform.gui.*
|
import org.isoron.platform.gui.*
|
||||||
import org.isoron.platform.io.*
|
import org.isoron.platform.io.*
|
||||||
|
import platform.CoreGraphics.*
|
||||||
|
import platform.Foundation.*
|
||||||
|
import platform.UIKit.*
|
||||||
|
|
||||||
actual class DependencyResolver {
|
actual class DependencyResolver {
|
||||||
actual fun getFileOpener(): FileOpener = TODO()
|
actual suspend fun getFileOpener(): FileOpener {
|
||||||
actual fun getDatabase(): Database = TODO()
|
return IosFileOpener()
|
||||||
actual fun createCanvas(width: Int, height: Int): Canvas = TODO()
|
}
|
||||||
actual fun exportCanvas(canvas: Canvas, filename: String): Unit = TODO()
|
|
||||||
|
actual suspend fun getDatabase(): Database = TODO()
|
||||||
|
|
||||||
|
actual fun createCanvas(width: Int, height: Int): Canvas {
|
||||||
|
UIGraphicsBeginImageContext(CGSizeMake(width=500.0, height=600.0))
|
||||||
|
return IosCanvas()
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun exportCanvas(canvas: Canvas, filename: String): Unit {
|
||||||
|
val image = UIGraphicsGetImageFromCurrentImageContext()!!
|
||||||
|
val manager = NSFileManager.defaultManager
|
||||||
|
val paths = manager.URLsForDirectory(NSDocumentDirectory, NSUserDomainMask) as List<NSURL>
|
||||||
|
val filePath = paths.first().URLByAppendingPathComponent("IosCanvasTest.png")!!.path!!
|
||||||
|
val data = UIImagePNGRepresentation(image)!!
|
||||||
|
data.writeToFile(filePath, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
42
core/src/iosTest/kotlin/org/isoron/IosAsyncTests.kt
Normal file
42
core/src/iosTest/kotlin/org/isoron/IosAsyncTests.kt
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2019 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.isoron.platform.io.*
|
||||||
|
import org.isoron.uhabits.models.*
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
class IosAsyncTests {
|
||||||
|
@Test
|
||||||
|
fun testFiles() = runBlocking { FilesTest().testLines() }
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun testDatabase() = runBlocking { DatabaseTest().testUsage() }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun testCheckmarkRepository() = runBlocking { CheckmarkRepositoryTest().testCRUD() }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun testHabitRepository() = runBlocking { HabitRepositoryTest().testCRUD() }
|
||||||
|
//
|
||||||
|
// @Test
|
||||||
|
// fun testPreferencesRepository() = runBlocking { PreferencesRepositoryTest().testUsage() }
|
||||||
|
}
|
||||||
@@ -109,9 +109,9 @@ class MainScreenController: UITableViewController, MainScreenDataSourceListener
|
|||||||
init(withBackend backend:Backend) {
|
init(withBackend backend:Backend) {
|
||||||
self.backend = backend
|
self.backend = backend
|
||||||
self.strings = backend.strings
|
self.strings = backend.strings
|
||||||
self.dataSource = backend.mainScreenDataSource
|
self.dataSource = backend.mainScreenDataSource!
|
||||||
self.theme = backend.theme
|
self.theme = backend.theme
|
||||||
self.preferences = backend.preferences
|
self.preferences = backend.preferences!
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
self.dataSource.observable.addListener(listener: self)
|
self.dataSource.observable.addListener(listener: self)
|
||||||
self.dataSource.requestData()
|
self.dataSource.requestData()
|
||||||
|
|||||||
@@ -42,28 +42,6 @@ class ComponentView : UIView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IosCanvas : NSObject, Canvas {
|
class IosCanvas : NSObject, Canvas {
|
||||||
func fillArc(centerX: Double, centerY: Double, radius: Double, startAngle: Double, swipeAngle: Double) {
|
|
||||||
let center = CGPoint(x: CGFloat(centerX), y: CGFloat(centerY))
|
|
||||||
let a1 = startAngle / 180 * .pi * (-1)
|
|
||||||
let a2 = a1 - swipeAngle / 180 * .pi
|
|
||||||
self.ctx.beginPath()
|
|
||||||
self.ctx.move(to: center)
|
|
||||||
self.ctx.addArc(center: center,
|
|
||||||
radius: CGFloat(radius),
|
|
||||||
startAngle: CGFloat(a1),
|
|
||||||
endAngle: CGFloat(a2),
|
|
||||||
clockwise: swipeAngle >= 0)
|
|
||||||
self.ctx.closePath()
|
|
||||||
self.ctx.fillPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillCircle(centerX: Double, centerY: Double, radius: Double) {
|
|
||||||
self.ctx.fillEllipse(in: CGRect(x: CGFloat(centerX - radius),
|
|
||||||
y: CGFloat(centerY - radius),
|
|
||||||
width: CGFloat(radius * 2),
|
|
||||||
height: CGFloat(radius * 2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
var bounds: CGRect
|
var bounds: CGRect
|
||||||
var ctx: CGContext
|
var ctx: CGContext
|
||||||
|
|
||||||
@@ -157,4 +135,26 @@ class IosCanvas : NSObject, Canvas {
|
|||||||
func setTextAlign(align: TextAlign) {
|
func setTextAlign(align: TextAlign) {
|
||||||
self.textAlign = align
|
self.textAlign = align
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fillArc(centerX: Double, centerY: Double, radius: Double, startAngle: Double, swipeAngle: Double) {
|
||||||
|
let center = CGPoint(x: CGFloat(centerX), y: CGFloat(centerY))
|
||||||
|
let a1 = startAngle / 180 * .pi * (-1)
|
||||||
|
let a2 = a1 - swipeAngle / 180 * .pi
|
||||||
|
self.ctx.beginPath()
|
||||||
|
self.ctx.move(to: center)
|
||||||
|
self.ctx.addArc(center: center,
|
||||||
|
radius: CGFloat(radius),
|
||||||
|
startAngle: CGFloat(a1),
|
||||||
|
endAngle: CGFloat(a2),
|
||||||
|
clockwise: swipeAngle >= 0)
|
||||||
|
self.ctx.closePath()
|
||||||
|
self.ctx.fillPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillCircle(centerX: Double, centerY: Double, radius: Double) {
|
||||||
|
self.ctx.fillEllipse(in: CGRect(x: CGFloat(centerX - radius),
|
||||||
|
y: CGFloat(centerY - radius),
|
||||||
|
width: CGFloat(radius * 2),
|
||||||
|
height: CGFloat(radius * 2)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2019 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class IosResourceFile : NSObject, ResourceFile {
|
|
||||||
|
|
||||||
var path: String
|
|
||||||
var fileManager = FileManager.default
|
|
||||||
|
|
||||||
init(forPath path: String) {
|
|
||||||
self.path = path
|
|
||||||
}
|
|
||||||
|
|
||||||
func readLines() -> [String] {
|
|
||||||
do {
|
|
||||||
let contents = try String(contentsOfFile: self.path, encoding: .utf8)
|
|
||||||
return contents.components(separatedBy: CharacterSet.newlines)
|
|
||||||
} catch {
|
|
||||||
return ["ERROR"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func doCopyTo(dest: UserFile) {
|
|
||||||
try! fileManager.copyItem(atPath: self.path, toPath: (dest as! IosUserFile).path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IosUserFile : NSObject, UserFile {
|
|
||||||
|
|
||||||
var path: String
|
|
||||||
|
|
||||||
init(forPath path: String) {
|
|
||||||
self.path = path
|
|
||||||
}
|
|
||||||
|
|
||||||
func delete() {
|
|
||||||
do {
|
|
||||||
try FileManager.default.removeItem(atPath: path)
|
|
||||||
} catch {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exists() -> Bool {
|
|
||||||
return FileManager.default.fileExists(atPath: path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IosFileOpener : NSObject, FileOpener {
|
|
||||||
func openResourceFile(filename: String) -> ResourceFile {
|
|
||||||
let path = "\(Bundle.main.resourcePath!)/\(filename)"
|
|
||||||
return IosResourceFile(forPath: path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openUserFile(filename: String) -> UserFile {
|
|
||||||
do {
|
|
||||||
let root = try FileManager.default.url(for: .documentDirectory,
|
|
||||||
in: .userDomainMask,
|
|
||||||
appropriateFor: nil,
|
|
||||||
create: false).path
|
|
||||||
return IosUserFile(forPath: "\(root)/\(filename)")
|
|
||||||
} catch {
|
|
||||||
return IosUserFile(forPath: "invalid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,6 +64,5 @@ class IosCanvasTest : XCTestCase {
|
|||||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||||
let filePath = paths.first?.appendingPathComponent("IosCanvasTest.png")
|
let filePath = paths.first?.appendingPathComponent("IosCanvasTest.png")
|
||||||
try! image.pngData()!.write(to: filePath!, options: .atomic)
|
try! image.pngData()!.write(to: filePath!, options: .atomic)
|
||||||
UIGraphicsEndImageContext()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,52 +22,52 @@ import XCTest
|
|||||||
|
|
||||||
class IosDatabaseTest: XCTestCase {
|
class IosDatabaseTest: XCTestCase {
|
||||||
func testUsage() {
|
func testUsage() {
|
||||||
let databaseOpener = IosDatabaseOpener(withLog: StandardLog())
|
let databaseOpener = IosDatabaseOpener(withLog: StandardLog())
|
||||||
let fileOpener = IosFileOpener()
|
let fileOpener = IosFileOpener()
|
||||||
|
|
||||||
let dbFile = fileOpener.openUserFile(filename: "test.sqlite3")
|
let dbFile = fileOpener.openUserFile(path: "test.sqlite3")
|
||||||
if dbFile.exists() {
|
if dbFile.exists() {
|
||||||
|
dbFile.delete()
|
||||||
|
}
|
||||||
|
let db = databaseOpener.open(file: dbFile)
|
||||||
|
|
||||||
|
var stmt = db.prepareStatement(sql: "drop table if exists demo")
|
||||||
|
stmt.step()
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
stmt = db.prepareStatement(sql: "begin")
|
||||||
|
stmt.step()
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
stmt = db.prepareStatement(sql: "create table if not exists demo(key int, value text)")
|
||||||
|
stmt.step()
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
stmt = db.prepareStatement(sql: "insert into demo(key, value) values (?, ?)")
|
||||||
|
stmt.bindInt(index: 0, value: 42)
|
||||||
|
stmt.bindText(index: 1, value: "Hello World")
|
||||||
|
stmt.step()
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
stmt = db.prepareStatement(sql: "select * from demo where key > ?")
|
||||||
|
stmt.bindInt(index: 0, value: 10)
|
||||||
|
var result = stmt.step()
|
||||||
|
XCTAssertEqual(result, StepResult.row)
|
||||||
|
XCTAssertEqual(stmt.getInt(index: 0), 42)
|
||||||
|
XCTAssertEqual(stmt.getText(index: 1), "Hello World")
|
||||||
|
result = stmt.step()
|
||||||
|
XCTAssertEqual(result, StepResult.done)
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
stmt = db.prepareStatement(sql: "drop table demo")
|
||||||
|
stmt.step()
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
stmt = db.prepareStatement(sql: "commit")
|
||||||
|
stmt.step()
|
||||||
|
stmt.finalize()
|
||||||
|
|
||||||
|
db.close()
|
||||||
dbFile.delete()
|
dbFile.delete()
|
||||||
}
|
|
||||||
let db = databaseOpener.open(file: dbFile)
|
|
||||||
|
|
||||||
var stmt = db.prepareStatement(sql: "drop table if exists demo")
|
|
||||||
stmt.step()
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
stmt = db.prepareStatement(sql: "begin")
|
|
||||||
stmt.step()
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
stmt = db.prepareStatement(sql: "create table if not exists demo(key int, value text)")
|
|
||||||
stmt.step()
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
stmt = db.prepareStatement(sql: "insert into demo(key, value) values (?, ?)")
|
|
||||||
stmt.bindInt(index: 0, value: 42)
|
|
||||||
stmt.bindText(index: 1, value: "Hello World")
|
|
||||||
stmt.step()
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
stmt = db.prepareStatement(sql: "select * from demo where key > ?")
|
|
||||||
stmt.bindInt(index: 0, value: 10)
|
|
||||||
var result = stmt.step()
|
|
||||||
XCTAssertEqual(result, StepResult.row)
|
|
||||||
XCTAssertEqual(stmt.getInt(index: 0), 42)
|
|
||||||
XCTAssertEqual(stmt.getText(index: 1), "Hello World")
|
|
||||||
result = stmt.step()
|
|
||||||
XCTAssertEqual(result, StepResult.done)
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
stmt = db.prepareStatement(sql: "drop table demo")
|
|
||||||
stmt.step()
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
stmt = db.prepareStatement(sql: "commit")
|
|
||||||
stmt.step()
|
|
||||||
stmt.finalize()
|
|
||||||
|
|
||||||
db.close()
|
|
||||||
dbFile.delete()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016-2019 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import uhabits
|
|
||||||
|
|
||||||
class IosFilesTest: XCTestCase {
|
|
||||||
func testResourceFiles() {
|
|
||||||
let fileOpener = IosFileOpener()
|
|
||||||
let file = fileOpener.openResourceFile(filename: "migrations/010.sql")
|
|
||||||
let lines = file.readLines()
|
|
||||||
XCTAssertEqual(lines[0], "delete from Score")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUserFiles() throws {
|
|
||||||
let fm = FileManager.default
|
|
||||||
let root = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).path
|
|
||||||
let path = "\(root)/test.txt"
|
|
||||||
print(path)
|
|
||||||
fm.createFile(atPath: path, contents: "Hello world\nThis is line 2".data(using: .utf8), attributes: nil)
|
|
||||||
|
|
||||||
let fileOpener = IosFileOpener()
|
|
||||||
let file = fileOpener.openUserFile(filename: "test.txt")
|
|
||||||
XCTAssertTrue(file.exists())
|
|
||||||
|
|
||||||
file.delete()
|
|
||||||
XCTAssertFalse(file.exists())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,10 +12,8 @@
|
|||||||
00A5B42822009F590024E00C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A5B42722009F590024E00C /* AppDelegate.swift */; };
|
00A5B42822009F590024E00C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A5B42722009F590024E00C /* AppDelegate.swift */; };
|
||||||
00A5B42A22009F590024E00C /* MainScreenController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A5B42922009F590024E00C /* MainScreenController.swift */; };
|
00A5B42A22009F590024E00C /* MainScreenController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A5B42922009F590024E00C /* MainScreenController.swift */; };
|
||||||
00A5B42F22009F5A0024E00C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00A5B42E22009F5A0024E00C /* Assets.xcassets */; };
|
00A5B42F22009F5A0024E00C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00A5B42E22009F5A0024E00C /* Assets.xcassets */; };
|
||||||
00C0C6A52246537A003D8AF0 /* IosFilesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C0C6A122465365003D8AF0 /* IosFilesTest.swift */; };
|
|
||||||
00C0C6A62246537E003D8AF0 /* IosDatabaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C0C6A222465365003D8AF0 /* IosDatabaseTest.swift */; };
|
00C0C6A62246537E003D8AF0 /* IosDatabaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C0C6A222465365003D8AF0 /* IosDatabaseTest.swift */; };
|
||||||
00C0C6A8224654A2003D8AF0 /* IosDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C0C6A7224654A2003D8AF0 /* IosDatabase.swift */; };
|
00C0C6A8224654A2003D8AF0 /* IosDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C0C6A7224654A2003D8AF0 /* IosDatabase.swift */; };
|
||||||
00C0C6AA224654F4003D8AF0 /* IosFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C0C6A9224654F4003D8AF0 /* IosFiles.swift */; };
|
|
||||||
00C0C6BD22465F65003D8AF0 /* fonts in Resources */ = {isa = PBXBuildFile; fileRef = 00C0C6BA22465F65003D8AF0 /* fonts */; };
|
00C0C6BD22465F65003D8AF0 /* fonts in Resources */ = {isa = PBXBuildFile; fileRef = 00C0C6BA22465F65003D8AF0 /* fonts */; };
|
||||||
00C0C6BE22465F65003D8AF0 /* databases in Resources */ = {isa = PBXBuildFile; fileRef = 00C0C6BB22465F65003D8AF0 /* databases */; };
|
00C0C6BE22465F65003D8AF0 /* databases in Resources */ = {isa = PBXBuildFile; fileRef = 00C0C6BB22465F65003D8AF0 /* databases */; };
|
||||||
00C0C6BF22465F65003D8AF0 /* migrations in Resources */ = {isa = PBXBuildFile; fileRef = 00C0C6BC22465F65003D8AF0 /* migrations */; };
|
00C0C6BF22465F65003D8AF0 /* migrations in Resources */ = {isa = PBXBuildFile; fileRef = 00C0C6BC22465F65003D8AF0 /* migrations */; };
|
||||||
@@ -66,10 +64,8 @@
|
|||||||
00A5B43322009F5A0024E00C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
00A5B43322009F5A0024E00C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
00A5B43822009F5A0024E00C /* uhabitsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = uhabitsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
00A5B43822009F5A0024E00C /* uhabitsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = uhabitsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
00A5B43E22009F5A0024E00C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
00A5B43E22009F5A0024E00C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
00C0C6A122465365003D8AF0 /* IosFilesTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IosFilesTest.swift; sourceTree = "<group>"; };
|
|
||||||
00C0C6A222465365003D8AF0 /* IosDatabaseTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IosDatabaseTest.swift; sourceTree = "<group>"; };
|
00C0C6A222465365003D8AF0 /* IosDatabaseTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IosDatabaseTest.swift; sourceTree = "<group>"; };
|
||||||
00C0C6A7224654A2003D8AF0 /* IosDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IosDatabase.swift; sourceTree = "<group>"; };
|
00C0C6A7224654A2003D8AF0 /* IosDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IosDatabase.swift; sourceTree = "<group>"; };
|
||||||
00C0C6A9224654F4003D8AF0 /* IosFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IosFiles.swift; sourceTree = "<group>"; };
|
|
||||||
00C0C6AE224655D8003D8AF0 /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = "<group>"; };
|
00C0C6AE224655D8003D8AF0 /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = "<group>"; };
|
||||||
00C0C6BA22465F65003D8AF0 /* fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = fonts; sourceTree = "<group>"; };
|
00C0C6BA22465F65003D8AF0 /* fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = fonts; sourceTree = "<group>"; };
|
||||||
00C0C6BB22465F65003D8AF0 /* databases */ = {isa = PBXFileReference; lastKnownFileType = folder; path = databases; sourceTree = "<group>"; };
|
00C0C6BB22465F65003D8AF0 /* databases */ = {isa = PBXFileReference; lastKnownFileType = folder; path = databases; sourceTree = "<group>"; };
|
||||||
@@ -185,7 +181,6 @@
|
|||||||
00C0C6A7224654A2003D8AF0 /* IosDatabase.swift */,
|
00C0C6A7224654A2003D8AF0 /* IosDatabase.swift */,
|
||||||
00C0C6DA2247E6B0003D8AF0 /* IosDates.swift */,
|
00C0C6DA2247E6B0003D8AF0 /* IosDates.swift */,
|
||||||
00C0C6CD2246EFB3003D8AF0 /* IosExtensions.swift */,
|
00C0C6CD2246EFB3003D8AF0 /* IosExtensions.swift */,
|
||||||
00C0C6A9224654F4003D8AF0 /* IosFiles.swift */,
|
|
||||||
006EFE4D2252EA2B008464E0 /* IosLocale.swift */,
|
006EFE4D2252EA2B008464E0 /* IosLocale.swift */,
|
||||||
);
|
);
|
||||||
path = Platform;
|
path = Platform;
|
||||||
@@ -197,7 +192,6 @@
|
|||||||
00C0C6D82247DC13003D8AF0 /* IosCanvasTest.swift */,
|
00C0C6D82247DC13003D8AF0 /* IosCanvasTest.swift */,
|
||||||
00C0C6A222465365003D8AF0 /* IosDatabaseTest.swift */,
|
00C0C6A222465365003D8AF0 /* IosDatabaseTest.swift */,
|
||||||
00C0C6DC2247E6C4003D8AF0 /* IosDatesTest.swift */,
|
00C0C6DC2247E6C4003D8AF0 /* IosDatesTest.swift */,
|
||||||
00C0C6A122465365003D8AF0 /* IosFilesTest.swift */,
|
|
||||||
);
|
);
|
||||||
path = Platform;
|
path = Platform;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -328,7 +322,6 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
00C0C6AA224654F4003D8AF0 /* IosFiles.swift in Sources */,
|
|
||||||
00C0C6D122470705003D8AF0 /* IosCanvas.swift in Sources */,
|
00C0C6D122470705003D8AF0 /* IosCanvas.swift in Sources */,
|
||||||
00C0C6CE2246EFB3003D8AF0 /* IosExtensions.swift in Sources */,
|
00C0C6CE2246EFB3003D8AF0 /* IosExtensions.swift in Sources */,
|
||||||
00C0C6E0224A3602003D8AF0 /* DetailScreenController.swift in Sources */,
|
00C0C6E0224A3602003D8AF0 /* DetailScreenController.swift in Sources */,
|
||||||
@@ -347,7 +340,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
00C0C6DD2247E6C4003D8AF0 /* IosDatesTest.swift in Sources */,
|
00C0C6DD2247E6C4003D8AF0 /* IosDatesTest.swift in Sources */,
|
||||||
00C0C6A52246537A003D8AF0 /* IosFilesTest.swift in Sources */,
|
|
||||||
00C0C6D92247DC13003D8AF0 /* IosCanvasTest.swift in Sources */,
|
00C0C6D92247DC13003D8AF0 /* IosCanvasTest.swift in Sources */,
|
||||||
00C0C6A62246537E003D8AF0 /* IosDatabaseTest.swift in Sources */,
|
00C0C6A62246537E003D8AF0 /* IosDatabaseTest.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user