Add unit test code for DB1

This commit is contained in:
2024-11-08 23:24:03 +09:00
parent 9646dbe556
commit 7750faf8d0
7 changed files with 311 additions and 162 deletions

View File

@@ -0,0 +1,44 @@
//
// KissTestCase.swift
// KissMe
//
// Created by ened-book-m1 on 11/8/24.
//
public protocol KissTestCase {
// Method called before each test
func setUp()
// Method called after each test
func tearDown()
// Method to run all tests
func runTests()
}
extension KissTestCase {
public func setUp() {}
public func tearDown() {}
public func runTests() {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let name = child.label, name.hasPrefix("test"),
let test = child.value as? () -> Void {
setUp()
print("\(name) begin")
test()
print("\(name) end")
tearDown()
}
}
}
}
public func assertEqual<T: Equatable>(_ a: T, _ b: T, message: String = "") {
if a != b {
print("Assertion failed: \(a) is not equal to \(b). \(message)")
} else {
//print("Test passed.")
}
}

View File

@@ -10,47 +10,6 @@ import KissMe
import KissMeme
extension KissConsole {
}
func test_build_min_db() {
// guard let enumerator = FileManager.subPathFiles("data") else {
// return
// }
//let db = try KissDB(directory: url)
//test_check_name_parsed()
//test_date_time()
//build_min_db_from_candle_csv()
test_select_min_db()
}
private func test_field_type() {
let v1: Int64 = 0
let v2: Int64 = 1
let v3: Int64 = 65536
let v4: Int64 = -2
print("\(v1.fieldType)")
print("\(v2.fieldType)")
print("\(v3.fieldType)")
print("\(v4.fieldType)")
print("done")
}
private func test_date_time() {
let kissDate = Date.date(yyyyMMdd: "20200101", HHmmss: "000000")
let timestamp = UInt64(kissDate!.timeIntervalSince1970)
print("timestamp: \(timestamp)")
print("kissDate: \(kissDate!.timeIntervalSince2020)")
print("today: \(UInt32(Date().timeIntervalSince2020))")
let value: UInt64 = 1234567890
let d = Data(value: value)
print(d.hexString)
}
enum CandleDataFieldType: UInt8 {
case uint8 = 1 // 8 bits unsigned integer
case uint16 = 2 // 16 bits unsigned integer
@@ -60,6 +19,20 @@ enum CandleDataFieldType: UInt8 {
case float = 11 // 4 byte float point
}
extension CandleDataFieldType: CustomStringConvertible {
var description: String {
switch self {
case .uint8: return "uint8"
case .uint16: return "uint16"
case .uint32: return "uint32"
case .uint64: return "uint64"
case .double: return "double"
case .float: return "float"
}
}
}
extension Int64 {
var fieldType: CandleDataFieldType {
let unsignedValue = UInt64(bitPattern: self)
@@ -89,6 +62,7 @@ extension Int64 {
}
}
extension Domestic.Candle: @retroactive Equatable {
public static func == (lhs: Domestic.Candle, rhs: Domestic.Candle) -> Bool {
return
@@ -103,6 +77,7 @@ extension Domestic.Candle: @retroactive Equatable {
}
}
struct CandleData {
let key: Data
let data: Data
@@ -142,7 +117,7 @@ struct CandleData {
let values = [accumulatedTradingAmount, currentStockPrice, stockOpenningPrice, highestStockPrice, lowestStockPrice, conclusionVolume]
var typeFields = [UInt8]()
var typeFields = [UInt8]()
var valuesData = Data()
for value in values {
let valueData: Data
@@ -165,7 +140,7 @@ struct CandleData {
data.append(contentsOf: typeFields)
data.append(valuesData)
self.data = data
print("data: \(data.count)")
//print("data: \(data.count)")
}
var candle: Domestic.Candle {
@@ -177,19 +152,36 @@ struct CandleData {
let typeFields = [UInt8](data[0 ..< 6])
var values = [stockBusinessDate, stockConclusionTime]
print("candle data: \(data.count)")
//print("candle data: \(data.count)")
var start = 6
for field in typeFields {
let value: String
switch CandleDataFieldType(rawValue: field)! {
case .uint8: value = String(data.subdata(in: start ..< start+1).value_UInt8); start += 1
case .uint16: value = String(data.subdata(in: start ..< start+2).value_UInt16); start += 2
case .uint32: value = String(data.subdata(in: start ..< start+4).value_UInt32); start += 4
case .uint64: value = String(data.subdata(in: start ..< start+8).value_UInt64); start += 8
case .float: value = String(data.subdata(in: start ..< start+4).value_Float); start += 4
case .double: value = String(data.subdata(in: start ..< start+8).value_Double); start += 8
case .uint8:
value = String(data.subdata(in: start ..< start+1).value_UInt8)
start += 1
case .uint16:
value = String(data.subdata(in: start ..< start+2).value_UInt16)
start += 2
case .uint32:
value = String(data.subdata(in: start ..< start+4).value_UInt32)
start += 4
case .uint64:
value = String(data.subdata(in: start ..< start+8).value_UInt64)
start += 8
case .float:
value = String(data.subdata(in: start ..< start+4).value_Float)
start += 4
case .double:
value = String(data.subdata(in: start ..< start+8).value_Double)
start += 8
}
values.append(value)
}
@@ -197,117 +189,8 @@ struct CandleData {
}
}
private func build_min_db(_ productNo: String, _ candle_csvs: [URL]) {
let dataPath = URL.currentDirectory().appending(path: "data")
for csvUrl in candle_csvs {
let candleMinName = CandleMinuteFileName()
if let (_, yyyyMMdd) = candleMinName.matchedUrl(csvUrl.path), let year = Int(yyyyMMdd.prefix(4)) {
let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1")
//try? FileManager.default.removeItem(at: directory)
try? FileManager.default.createDirectory(at: yearDbPath, withIntermediateDirectories: true)
do {
let candles = try [Domestic.Candle].readCsv(fromFile: csvUrl)
let db = try KissDB(directory: yearDbPath)
try db.begin()
for candle in candles {
let candleData = try CandleData(candle: candle)
let item = KissDB.DataItem(key: candleData.key, value: candleData.data)
try db.insert(item: item)
if candleData.candle != candle {
assertionFailure("invalid candle data")
}
}
try db.commit()
} catch {
print("\(error)")
return
}
}
}
}
private func build_min_db_from_candle_csv() {
guard let enumerator = FileManager.subPathFiles("data") else {
return
}
var lastProductNo: String?
let candleMinName = CandleMinuteFileName()
var allCandles = [String: [URL]]()
for case let fileUrl as URL in enumerator {
guard let (productNo, yyyyMMdd) = candleMinName.matchedUrl(fileUrl.path) else {
continue
}
// Select only one product no
if lastProductNo == nil {
lastProductNo = productNo
}
else {
if lastProductNo! != productNo {
break
}
}
if allCandles.keys.contains(productNo) {
allCandles[productNo]!.append(fileUrl)
}
else {
allCandles[productNo] = [fileUrl]
}
print("product: \(productNo) \(yyyyMMdd)")
}
print("total \(allCandles.count)")
if let productCandles = allCandles.first {
build_min_db(productCandles.key, productCandles.value)
}
}
private func test_select_min_db() {
let yearDbPath = URL(filePath: "/Users/ened/Kiss/KissMe/bin/data/047040/min/candle-2023.db1")
do {
let startTime = KissDB.appTime
let db = try KissDB(directory: yearDbPath)
try db.begin()
try db.select(into: { (dataItem: KissDB.DataItem) -> Bool in
//let candleData = CandleData(key: dataItem.key, data: dataItem.value)
//print("\(candleData.candleDate) : \(candleData.candle.accumulatedTradingAmount)")
return true
})
try db.rollback()
let endTime = KissDB.appTime
print("DB count: \(db.count) insertAll elapsed: \(endTime - startTime)")
} catch {
print("\(error)")
}
}
private func test_check_name_parsed() {
let candleMinName = CandleMinuteFileName()
let url = "/Users/ened/Kiss/KissMe/bin/data/000020/min/candle-20230705.csv"
guard let (productNo, yyyyMMdd) = candleMinName.matchedUrl(url) else {
return
}
print(productNo, yyyyMMdd)
}
class CandleMinuteFileName {
let regex: NSRegularExpression
init() {
@@ -333,3 +216,66 @@ class CandleMinuteFileName {
return nil
}
}
extension KissConsole {
func collectCandleMinuteFiles() -> [String: [URL]] {
guard let enumerator = FileManager.subPathFiles("data") else {
return [:]
}
let candleMinName = CandleMinuteFileName()
var allCandles = [String: [URL]]()
for case let fileUrl as URL in enumerator {
guard let (productNo, _) = candleMinName.matchedUrl(fileUrl.path) else {
continue
}
if allCandles.keys.contains(productNo) {
allCandles[productNo]!.append(fileUrl)
}
else {
allCandles[productNo] = [fileUrl]
}
}
return allCandles
}
func buildCandleMinuteDB(productNo: String, csvFiles: [URL], removeOldDB: Bool = false) {
let dataPath = URL.currentDirectory().appending(path: "data")
for csvFile in csvFiles {
let candleMinName = CandleMinuteFileName()
if let (_, yyyyMMdd) = candleMinName.matchedUrl(csvFile.path), let year = Int(yyyyMMdd.prefix(4)) {
let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1")
if removeOldDB {
try? FileManager.default.removeItem(at: yearDbPath)
}
try? FileManager.default.createDirectory(at: yearDbPath, withIntermediateDirectories: true)
do {
let candles = try [Domestic.Candle].readCsv(fromFile: csvFile)
let db = try KissDB(directory: yearDbPath)
try db.begin()
for candle in candles {
let candleData = try CandleData(candle: candle)
let item = KissDB.DataItem(key: candleData.key, value: candleData.data)
try db.insertData(item: item)
if candleData.candle != candle {
assertionFailure("invalid candle data")
}
}
try db.commit()
} catch {
print("\(error)")
return
}
}
}
}
}

View File

@@ -0,0 +1,129 @@
//
// DB1_CandleData_Tests.swift
// KissMeConsole
//
// Created by ened-book-m1 on 11/8/24.
//
import Foundation
import KissMe
import KissMeme
struct DB1_CandleData_Tests: KissTestCase {
let test_1_FieldType = {
let v1: Int64 = 0
let v2: Int64 = 1
let v3: Int64 = 65536
let v4: Int64 = -2
assertEqual(v1.fieldType, .uint8)
assertEqual(v2.fieldType, .uint8)
assertEqual(v3.fieldType, .uint32)
assertEqual(v4.fieldType, .uint64)
}
let test_2_DateTimeSince2020 = {
let kissDate = Date.date(yyyyMMdd: "20200101", HHmmss: "000000")
let timestamp = UInt64(kissDate!.timeIntervalSince1970)
assertEqual(kissDate!.timeIntervalSince2020, 0)
let today = Date()
let todayTimestampSince2020 = UInt64(today.timeIntervalSince2020)
let diffTimeStamp = UInt64(today.timeIntervalSince1970) - timestamp
assertEqual(todayTimestampSince2020, diffTimeStamp)
}
let test_3_ParseCandleCsvFileName = {
let candleMinName = CandleMinuteFileName()
let url = "/Users/ened/Kiss/KissMe/bin/data/000020/min/candle-20230705.csv"
guard let (productNo, yyyyMMdd) = candleMinName.matchedUrl(url) else {
return
}
assertEqual(productNo, "000020")
assertEqual(yyyyMMdd, "20230705")
}
let test_4_BuildSamsungDB = {
let productNo = Self.samsungProductNo
let year = Self.year
let month = Self.month
let csvFiles = Self.collectCsv(productNo: productNo, yyyy: year, MM: month)
let dataPath = URL.currentDirectory().appending(path: "data")
for csvFile in csvFiles {
let candleMinName = CandleMinuteFileName()
if let (_, yyyyMMdd) = candleMinName.matchedUrl(csvFile.path), let year = Int(yyyyMMdd.prefix(4)) {
let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1")
}
let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1")
try? FileManager.default.removeItem(at: yearDbPath)
try? FileManager.default.createDirectory(at: yearDbPath, withIntermediateDirectories: true)
do {
let candles = try [Domestic.Candle].readCsv(fromFile: csvFile)
let db = try KissDB(directory: yearDbPath)
try db.begin()
for candle in candles {
let candleData = try CandleData(candle: candle)
let item = KissDB.DataItem(key: candleData.key, value: candleData.data)
try db.insertData(item: item)
assertEqual(candleData.candle, candle)
}
try db.commit()
} catch {
print("\(error)")
}
}
}
let test_5_CountSamsungDB: () -> Void = {
let productNo = Self.samsungProductNo
let year = Self.year
let yyyyMM = String(year) + Self.month
let dataPath = URL.currentDirectory().appending(path: "data")
let yearDbPath = dataPath.appending(path: "\(productNo)/min/candle-\(year).db1")
do {
let db = try KissDB(directory: yearDbPath)
try db.begin()
try db.selectData(into: { (dataItem: KissDB.DataItem) -> Bool in
let candleData = CandleData(key: dataItem.key, data: dataItem.value)
assertEqual(String(candleData.candleDate.prefix(6)), yyyyMM)
return true
})
try db.rollback()
} catch {
print("\(error)")
}
}
static let samsungProductNo = "005930"
static let year = "2023"
static let month = "06"
static func collectCsv(productNo: String, yyyy: String, MM: String) -> [URL] {
guard let enumerator = FileManager.subPathFiles("data/\(productNo)") else {
return []
}
let candleMinName = CandleMinuteFileName()
var csvFiles = [URL]()
for case let fileUrl as URL in enumerator {
guard let (fileProductNo, yyyyMMdd) = candleMinName.matchedUrl(fileUrl.path) else {
continue
}
if fileProductNo == productNo, yyyyMMdd.prefix(4) == yyyy, yyyyMMdd.dropFirst(4).prefix(2) == MM {
csvFiles.append(fileUrl)
}
}
return csvFiles
}
}

View File

@@ -7,7 +7,7 @@
import Foundation
KissConsole().run()
//KissConsole().run()
import KissMe
@@ -17,4 +17,10 @@ import KissMe
//test_websocket_dump_data()
//test_auction()
//test_build_min_db()
func runTests() {
let tests = DB1_CandleData_Tests()
tests.runTests()
}
runTests()

View File

@@ -36,6 +36,7 @@
341F5F112A1685E700962D48 /* ShopRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F102A1685E700962D48 /* ShopRequest.swift */; };
341F5F142A16CD7A00962D48 /* DomesticShopProduct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F132A16CD7A00962D48 /* DomesticShopProduct.swift */; };
3435A7F72A35D82000D604F1 /* DomesticShortSelling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F62A35D82000D604F1 /* DomesticShortSelling.swift */; };
346C25DA2CDE1D97003EF8D7 /* KissTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346C25D92CDE1D91003EF8D7 /* KissTestCase.swift */; };
34942F562CCA9AD200F85B79 /* DomesticStock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34942F552CCA9AD200F85B79 /* DomesticStock.swift */; };
34942F5A2CCA9B2700F85B79 /* DomesticFutures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34942F592CCA9B2700F85B79 /* DomesticFutures.swift */; };
349B05172C25B7C600378D55 /* OverseasStockResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349B05162C25B7C600378D55 /* OverseasStockResult.swift */; };
@@ -183,6 +184,7 @@
341F5F102A1685E700962D48 /* ShopRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopRequest.swift; sourceTree = "<group>"; };
341F5F132A16CD7A00962D48 /* DomesticShopProduct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticShopProduct.swift; sourceTree = "<group>"; };
3435A7F62A35D82000D604F1 /* DomesticShortSelling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticShortSelling.swift; sourceTree = "<group>"; };
346C25D92CDE1D91003EF8D7 /* KissTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissTestCase.swift; sourceTree = "<group>"; };
34942F552CCA9AD200F85B79 /* DomesticStock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStock.swift; sourceTree = "<group>"; };
34942F592CCA9B2700F85B79 /* DomesticFutures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticFutures.swift; sourceTree = "<group>"; };
349B05162C25B7C600378D55 /* OverseasStockResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverseasStockResult.swift; sourceTree = "<group>"; };
@@ -329,6 +331,7 @@
341F5EAD2A0A80EC00962D48 /* KissMe */ = {
isa = PBXGroup;
children = (
346C25D82CDE1D7A003EF8D7 /* Test */,
34F1900D2A426C1C0068C697 /* Context */,
34F1900A2A41981A0068C697 /* Index */,
341F5EF32A0F88AC00962D48 /* Common */,
@@ -416,6 +419,14 @@
path = Shop;
sourceTree = "<group>";
};
346C25D82CDE1D7A003EF8D7 /* Test */ = {
isa = PBXGroup;
children = (
346C25D92CDE1D91003EF8D7 /* KissTestCase.swift */,
);
path = Test;
sourceTree = "<group>";
};
34942F582CCA9B1000F85B79 /* Futures */ = {
isa = PBXGroup;
children = (
@@ -827,6 +838,7 @@
files = (
341F5EFB2A10909D00962D48 /* LoginResult.swift in Sources */,
34BC447D2A86635A0052D8EB /* Domestic.ContractPriceWebSocket.swift in Sources */,
346C25DA2CDE1D97003EF8D7 /* KissTestCase.swift in Sources */,
34C1BA882A5D9A4A00423D64 /* DomesticDartDisclosureInterests.swift in Sources */,
340A4DC42A4E4345005A1FBA /* ArrayDecodable.swift in Sources */,
34C1BA532A5A683D00423D64 /* DomesticDartBusinessReport.swift in Sources */,

View File

@@ -12,6 +12,7 @@
341F5F092A1463A100962D48 /* KissConsole.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F082A1463A100962D48 /* KissConsole.swift */; };
3435A7F22A35A8A900D604F1 /* KissConsole+Investor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */; };
3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F32A35B4D000D604F1 /* KissConsole+Price.swift */; };
346C25DD2CDE5652003EF8D7 /* DB1_CandleData_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 346C25DC2CDE5652003EF8D7 /* DB1_CandleData_Tests.swift */; };
348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348168482A2F92AC00A50BD3 /* KissContext.swift */; };
349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349327F62A20E3E300097063 /* Foundation+Extensions.swift */; };
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349843202A242AC900E85B08 /* KissConsole+CSV.swift */; };
@@ -59,6 +60,7 @@
341F5F082A1463A100962D48 /* KissConsole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissConsole.swift; sourceTree = "<group>"; };
3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Investor.swift"; sourceTree = "<group>"; };
3435A7F32A35B4D000D604F1 /* KissConsole+Price.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Price.swift"; sourceTree = "<group>"; };
346C25DC2CDE5652003EF8D7 /* DB1_CandleData_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DB1_CandleData_Tests.swift; sourceTree = "<group>"; };
348168482A2F92AC00A50BD3 /* KissContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissContext.swift; sourceTree = "<group>"; };
349327F62A20E3E300097063 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = "<group>"; };
3498431E2A24287600E85B08 /* KissMeConsoleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KissMeConsoleTests.swift; sourceTree = "<group>"; };
@@ -106,6 +108,7 @@
341F5ED22A0A8B9000962D48 /* KissMeConsole */ = {
isa = PBXGroup;
children = (
346C25DB2CDE2A7F003EF8D7 /* Tests */,
341F5ED32A0A8B9000962D48 /* main.swift */,
341F5F042A13B82F00962D48 /* test.swift */,
34DA3EA32A9A176B00BB3439 /* test_websocket.swift */,
@@ -134,6 +137,14 @@
name = Frameworks;
sourceTree = "<group>";
};
346C25DB2CDE2A7F003EF8D7 /* Tests */ = {
isa = PBXGroup;
children = (
346C25DC2CDE5652003EF8D7 /* DB1_CandleData_Tests.swift */,
);
path = Tests;
sourceTree = "<group>";
};
3498431D2A24284600E85B08 /* KissMeConsoleTests */ = {
isa = PBXGroup;
children = (
@@ -211,6 +222,7 @@
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */,
348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */,
34DB3C452AA6071D00B6763E /* KissConsole+WebSocket.swift in Sources */,
346C25DD2CDE5652003EF8D7 /* DB1_CandleData_Tests.swift in Sources */,
3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */,
34DA3EA42A9A176B00BB3439 /* test_websocket.swift in Sources */,
34F190132A4441F00068C697 /* KissConsole+Test.swift in Sources */,