Add tests for FFI functions

Improve API
This commit is contained in:
Emily Toop 2018-04-12 11:23:11 +01:00
parent 10e3d52902
commit 0dfb712ef7
9 changed files with 1001 additions and 121 deletions

View file

@ -17,13 +17,13 @@
7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AA2077C38E009D0651 /* RustObject.swift */; };
7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AB2077C38E009D0651 /* OptionalRustObject.swift */; };
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AC2077C38E009D0651 /* TupleResult.swift */; };
7BDB96B62077C38E009D0651 /* TxReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AD2077C38E009D0651 /* TxReport.swift */; };
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AE2077C38E009D0651 /* TypedValue.swift */; };
7BDB96C02077CD7A009D0651 /* libmentat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BDB96BF2077CD7A009D0651 /* libmentat.a */; };
7BDB96C22077CD98009D0651 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BDB96C12077CD98009D0651 /* libresolv.tbd */; };
7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96C52077D346009D0651 /* Date+Int64.swift */; };
7BDB96C9207B735A009D0651 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 7BDB96C8207B735A009D0651 /* fixtures */; };
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96CB207B7684009D0651 /* Errors.swift */; };
7BEB7D25207BE3BF000369AD /* libtoodle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BEB7D21207BDDEF000369AD /* libtoodle.a */; };
7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB7D2B207D03DA000369AD /* TxReport.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -50,7 +50,6 @@
7BDB96AA2077C38E009D0651 /* RustObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RustObject.swift; sourceTree = "<group>"; };
7BDB96AB2077C38E009D0651 /* OptionalRustObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionalRustObject.swift; sourceTree = "<group>"; };
7BDB96AC2077C38E009D0651 /* TupleResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TupleResult.swift; sourceTree = "<group>"; };
7BDB96AD2077C38E009D0651 /* TxReport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TxReport.swift; sourceTree = "<group>"; };
7BDB96AE2077C38E009D0651 /* TypedValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypedValue.swift; sourceTree = "<group>"; };
7BDB96BF2077CD7A009D0651 /* libmentat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmentat.a; path = ../../../target/universal/release/libmentat.a; sourceTree = "<group>"; };
7BDB96C12077CD98009D0651 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
@ -58,6 +57,9 @@
7BDB96C52077D346009D0651 /* Date+Int64.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Int64.swift"; sourceTree = "<group>"; };
7BDB96C8207B735A009D0651 /* fixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = fixtures; path = ../../../../fixtures; sourceTree = "<group>"; };
7BDB96CB207B7684009D0651 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
7BEB7D21207BDDEF000369AD /* libtoodle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtoodle.a; path = ../../../target/universal/release/libtoodle.a; sourceTree = "<group>"; };
7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmentat_ffi.a; path = ../../../target/universal/release/libmentat_ffi.a; sourceTree = "<group>"; };
7BEB7D2B207D03DA000369AD /* TxReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxReport.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -66,7 +68,7 @@
buildActionMask = 2147483647;
files = (
7BDB96C22077CD98009D0651 /* libresolv.tbd in Frameworks */,
7BDB96C02077CD7A009D0651 /* libmentat.a in Frameworks */,
7BEB7D25207BE3BF000369AD /* libtoodle.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -106,11 +108,11 @@
7BDB96CA207B7672009D0651 /* Errors */,
7BDB96C42077D346009D0651 /* Extensions */,
7BDB96BA2077C42B009D0651 /* Core */,
7BDB96A42077C301009D0651 /* Query */,
7BDB96B92077C403009D0651 /* Rust */,
7BDB96A82077C38E009D0651 /* store.h */,
7BDB96A72077C38D009D0651 /* Mentat.swift */,
7BDB96A42077C301009D0651 /* Query */,
7BDB96B82077C3B2009D0651 /* Transact */,
7BEB7D26207BE5BB000369AD /* Transact */,
7BDB968D2077C299009D0651 /* Mentat.h */,
7BDB968E2077C299009D0651 /* Info.plist */,
7BDB96C32077D090009D0651 /* module.map */,
@ -138,14 +140,6 @@
path = Query;
sourceTree = "<group>";
};
7BDB96B82077C3B2009D0651 /* Transact */ = {
isa = PBXGroup;
children = (
7BDB96AD2077C38E009D0651 /* TxReport.swift */,
);
path = Transact;
sourceTree = "<group>";
};
7BDB96B92077C403009D0651 /* Rust */ = {
isa = PBXGroup;
children = (
@ -166,6 +160,8 @@
7BDB96BE2077CD7A009D0651 /* Frameworks */ = {
isa = PBXGroup;
children = (
7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */,
7BEB7D21207BDDEF000369AD /* libtoodle.a */,
7BDB96C12077CD98009D0651 /* libresolv.tbd */,
7BDB96BF2077CD7A009D0651 /* libmentat.a */,
);
@ -188,6 +184,14 @@
path = Errors;
sourceTree = "<group>";
};
7BEB7D26207BE5BB000369AD /* Transact */ = {
isa = PBXGroup;
children = (
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
);
path = Transact;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -300,8 +304,8 @@
buildActionMask = 2147483647;
files = (
7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */,
7BDB96B62077C38E009D0651 /* TxReport.swift in Sources */,
7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */,
7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */,
7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */,
7BDB96B22077C38E009D0651 /* RelResult.swift in Sources */,
7BDB96AF2077C38E009D0651 /* Query.swift in Sources */,

View file

@ -7,6 +7,10 @@ import Mentatlib
class TypedValue: OptionalRustObject {
var valueType: ValueType {
return typed_value_value_type(self.raw!)
}
func asLong() -> Int64 {
defer {
self.raw = nil
@ -32,7 +36,8 @@ class TypedValue: OptionalRustObject {
defer {
self.raw = nil
}
return typed_value_as_boolean(self.raw!) == 0 ? false : true
let v = typed_value_as_boolean(self.raw!)
return v > 0
}
func asDouble() -> Double {

View file

@ -8,7 +8,7 @@ import Mentatlib
protocol Observing {
// define functions for store observation
func transactionDidOccur(key: String, reports: [TxReport])
func transactionDidOccur(key: String, reports: [TransactionChange])
}
protocol Observable {
@ -27,16 +27,15 @@ class Mentat: RustObject {
self.init(raw: store_open(storeURI))
}
func transact(transaction: String) throws -> Bool {
func transact(transaction: String) throws -> TxReport {
let result = store_transact(self.raw, transaction).pointee
guard let _ = result.ok else {
guard let success = result.ok else {
if let error = result.err {
throw MentatError(message: String(cString: error))
}
throw MentatError(message: "Unspecified Error")
}
print("Successfull")
return true
return TxReport(raw: success)
}
func entidForAttribute(attribute: String) -> Int64 {
@ -70,6 +69,78 @@ class Mentat: RustObject {
return TypedValue(raw: success)
}
func set(date value: Date, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_timestamp_for_attribute_on_entid(self.intoRaw(), entid, attribute, value.toMicroseconds())
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func set(long value: Int64, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_long_for_attribute_on_entid(self.intoRaw(), entid, attribute, value)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func set(reference value: Int64, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_entid_for_attribute_on_entid(self.intoRaw(), entid, attribute, value)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func setKeywordReference(value: String, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_kw_ref_for_attribute_on_entid(self.intoRaw(), entid, attribute, value)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func set(boolean value: Bool, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_boolean_for_attribute_on_entid(self.intoRaw(), entid, attribute, value ? 1 : 0)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func set(double value: Double, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_double_for_attribute_on_entid(self.intoRaw(), entid, attribute, value)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func set(string value: String, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_string_for_attribute_on_entid(self.intoRaw(), entid, attribute, value)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
func set(uuid value: UUID, forAttribute attribute: String, onEntity entid: Int64) throws {
let result = store_set_uuid_for_attribute_on_entid(self.intoRaw(), entid, attribute, value.uuidString)
guard let err = result.pointee.err else {
return
}
throw QueryError.executionFailed(message: String(cString: err))
}
override func cleanup(pointer: OpaquePointer) {
store_destroy(pointer)
}
@ -99,20 +170,19 @@ extension Mentat: Observable {
}
}
private func transactionObserverCallback(key: UnsafePointer<CChar>, reports: UnsafePointer<TxReportList>) {
private func transactionObserverCallback(key: UnsafePointer<CChar>, reports: UnsafePointer<TxChangeList>) {
// needs to be done in the same thread as the calling thread otherwise the TxReportList might be released before
// we can reference it.
let key = String(cString: key)
guard let observer = Mentat.observers[key] else { return }
let len = Int(reports.pointee.len)
var txReports = [TxReport]()
for i in 0..<len {
let raw = tx_report_list_entry_at(reports, i)
let report = TxReport(raw: raw!)
txReports.append(report)
}
// let len = Int(reports.pointee.len)
// var txReports = [TxReport]()
// for i in 0..<len {
// guard let report = tx_report_list_entry_at(reports, i)?.pointee else { continue }
// txReports.append(TxReport(raw: report))
// }
DispatchQueue.global(qos: .background).async {
observer.transactionDidOccur(key: key, reports: txReports)
observer.transactionDidOccur(key: key, reports: [TransactionChange]())
}
}

View file

@ -5,84 +5,81 @@
import Foundation
import Mentatlib
enum QueryResult<T> {
case error(Error)
case success(T)
}
class Query: OptionalRustObject {
func bind(varName: String, toInt value: Int32) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_int(r, varName, value)
}
func bind(varName: String, toLong value: Int64) throws {
func bind(varName: String, toLong value: Int64) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_long(r, varName, value)
return self
}
func bind(varName: String, toReference value: Int64) throws {
func bind(varName: String, toReference value: Int64) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_ref(r, varName, value)
return self
}
func bind(varName: String, toReference value: String) throws {
func bind(varName: String, toReference value: String) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_ref_kw(r, varName, value)
return self
}
func bind(varName: String, toKeyword value: String) throws {
func bind(varName: String, toKeyword value: String) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_kw(r, varName, value)
return self
}
func bind(varName: String, toBoolean value: Bool) throws {
func bind(varName: String, toBoolean value: Bool) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_boolean(r, varName, value ? 1 : 0)
return self
}
func bind(varName: String, toDouble value: Double) throws {
func bind(varName: String, toDouble value: Double) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_double(r, varName, value)
return self
}
func bind(varName: String, toDate value: Date) throws {
func bind(varName: String, toDate value: Date) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_timestamp(r, varName, value.toMicroseconds())
return self
}
func bind(varName: String, toString value: String) throws {
func bind(varName: String, toString value: String) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_string(r, varName, value)
return self
}
func bind(varName: String, toUuid value: UUID) throws {
func bind(varName: String, toUuid value: UUID) throws -> Query {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
query_builder_bind_uuid(r, varName, value.uuidString)
return self
}
func executeMap(map: @escaping (QueryResult<TupleResult>) -> Void) throws {
func executeMap(map: @escaping (TupleResult?, QueryError?) -> Void) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
@ -93,7 +90,7 @@ class Query: OptionalRustObject {
if let err = result.pointee.err {
let message = String(cString: err)
map(QueryResult.error(QueryError.executionFailed(message: message)))
map(nil, QueryError.executionFailed(message: message))
return
}
guard let rowsPtr = result.pointee.ok else {
@ -101,12 +98,12 @@ class Query: OptionalRustObject {
}
let rows = RelResult(raw: rowsPtr)
for row in rows {
map(QueryResult.success(row))
map(row, nil)
}
}
}
func execute(callback: @escaping (QueryResult<RelResult?>) -> Void) throws {
func execute(callback: @escaping (RelResult?, QueryError?) -> Void) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
@ -117,18 +114,18 @@ class Query: OptionalRustObject {
if let err = result.pointee.err {
let message = String(cString: err)
callback(QueryResult.error(QueryError.executionFailed(message: message)))
callback(nil, QueryError.executionFailed(message: message))
return
}
guard let results = result.pointee.ok else {
callback(QueryResult.success(nil))
callback(nil, nil)
return
}
callback(QueryResult.success(RelResult(raw: results)))
callback(RelResult(raw: results), nil)
}
}
func executeScalar(callback: @escaping (QueryResult<TypedValue?>) -> Void) throws {
func executeScalar(callback: @escaping (TypedValue?, QueryError?) -> Void) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
@ -139,17 +136,17 @@ class Query: OptionalRustObject {
if let err = result.pointee.err {
let message = String(cString: err)
callback(QueryResult.error(QueryError.executionFailed(message: message)))
callback(nil, QueryError.executionFailed(message: message))
}
guard let results = result.pointee.ok else {
callback(QueryResult.success(nil))
callback(nil, nil)
return
}
callback(QueryResult.success(TypedValue(raw: OpaquePointer(results))))
callback(TypedValue(raw: OpaquePointer(results)), nil)
}
}
func executeColl(callback: @escaping (QueryResult<ColResult?>) -> Void) throws {
func executeColl(callback: @escaping (ColResult?, QueryError?) -> Void) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
@ -160,17 +157,17 @@ class Query: OptionalRustObject {
if let err = result.pointee.err {
let message = String(cString: err)
callback(QueryResult.error(QueryError.executionFailed(message: message)))
callback(nil, QueryError.executionFailed(message: message))
}
guard let results = result.pointee.ok else {
callback(QueryResult.success(nil))
callback(nil, nil)
return
}
callback(QueryResult.success(ColResult(raw: results)))
callback(ColResult(raw: results), nil)
}
}
func executeCollMap(map: @escaping (QueryResult<TypedValue>) -> Void) throws {
func executeCollMap(map: @escaping (TypedValue?, QueryError?) -> Void) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
@ -181,7 +178,7 @@ class Query: OptionalRustObject {
if let err = result.pointee.err {
let message = String(cString: err)
map(QueryResult.error(QueryError.executionFailed(message: message)))
map(nil, QueryError.executionFailed(message: message))
return
}
guard let cols = result.pointee.ok else {
@ -189,12 +186,12 @@ class Query: OptionalRustObject {
}
let rowList = ColResult(raw: cols)
for row in rowList {
map(QueryResult.success(row))
map(row, nil)
}
}
}
func executeTuple(callback: @escaping (QueryResult<TupleResult?>) -> Void) throws {
func executeTuple(callback: @escaping (TupleResult?, QueryError?) -> Void) throws {
guard let r = self.raw else {
throw QueryError.builderConsumed
}
@ -205,13 +202,13 @@ class Query: OptionalRustObject {
if let err = result.pointee.err {
let message = String(cString: err)
callback(QueryResult.error(QueryError.executionFailed(message: message)))
callback(nil, QueryError.executionFailed(message: message))
}
guard let results = result.pointee.ok else {
callback(QueryResult.success(nil))
callback(nil, nil)
return
}
callback(QueryResult.success(TupleResult(raw: OpaquePointer(results))))
callback(TupleResult(raw: OpaquePointer(results)), nil)
}
}

View file

@ -7,40 +7,40 @@ import Mentatlib
class TupleResult: OptionalRustObject {
func get(index: Int32) -> TypedValue {
return TypedValue(raw: value_at_index(self.raw!, index))
func get(index: Int) -> TypedValue {
return TypedValue(raw: value_at_index(self.raw!, Int32(index)))
}
func asLong(index: Int32) -> Int64 {
return value_at_index_as_long(self.raw!, index)
func asLong(index: Int) -> Int64 {
return value_at_index_as_long(self.raw!, Int32(index))
}
func asEntid(index: Int32) -> Int64 {
return value_at_index_as_entid(self.raw!, index)
func asEntid(index: Int) -> Int64 {
return value_at_index_as_entid(self.raw!, Int32(index))
}
func asKeyword(index: Int32) -> String {
return String(cString: value_at_index_as_kw(self.raw!, index))
func asKeyword(index: Int) -> String {
return String(cString: value_at_index_as_kw(self.raw!, Int32(index)))
}
func asBool(index: Int32) -> Bool {
return value_at_index_as_boolean(self.raw!, index) == 0 ? false : true
func asBool(index: Int) -> Bool {
return value_at_index_as_boolean(self.raw!, Int32(index)) == 0 ? false : true
}
func asDouble(index: Int32) -> Double {
return value_at_index_as_double(self.raw!, index)
func asDouble(index: Int) -> Double {
return value_at_index_as_double(self.raw!, Int32(index))
}
func asDate(index: Int32) -> Date {
return Date(timeIntervalSince1970: TimeInterval(value_at_index_as_timestamp(self.raw!, index)))
func asDate(index: Int) -> Date {
return Date(timeIntervalSince1970: TimeInterval(value_at_index_as_timestamp(self.raw!, Int32(index))))
}
func asString(index: Int32) -> String {
return String(cString: value_at_index_as_string(self.raw!, index))
func asString(index: Int) -> String {
return String(cString: value_at_index_as_string(self.raw!, Int32(index)))
}
func asUUID(index: Int32) -> UUID? {
return UUID(uuidString: String(cString: value_at_index_as_uuid(self.raw!, index)))
func asUUID(index: Int) -> UUID? {
return UUID(uuidString: String(cString: value_at_index_as_uuid(self.raw!, Int32(index))))
}
override func cleanup(pointer: OpaquePointer) {
@ -73,8 +73,10 @@ class ColResultIterator: OptionalRustObject, IteratorProtocol {
extension ColResult: Sequence {
func makeIterator() -> ColResultIterator {
defer {
self.raw = nil
}
guard let raw = self.raw else {
print("list pointer destroyed")
return ColResultIterator(iter: nil)
}
let rowIter = values_iter(raw)

View file

@ -9,7 +9,7 @@ protocol Destroyable {
func cleanup(pointer: OpaquePointer)
}
class RustObject: Destroyable {
public class RustObject: Destroyable {
var raw: OpaquePointer
lazy var uniqueId: ObjectIdentifier = {

View file

@ -1,22 +1,30 @@
//
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Mentatlib
class TxReport {
var raw: UnsafePointer<ExternTxReport>
class TxReport: RustObject {
required init(raw: UnsafePointer<ExternTxReport>) {
self.raw = raw
public var txId: Int64 {
return tx_report_get_entid(self.raw)
}
func intoRaw() -> UnsafePointer<ExternTxReport> {
return self.raw
public var txInstant: Date {
return Date(timeIntervalSince1970: TimeInterval(tx_report_get_tx_instant(self.raw)))
}
deinit {
destroy(UnsafeMutableRawPointer(mutating: raw))
public func entidForTmpId(tmpId: String) -> Int64? {
guard let entidPtr = tx_report_entity_for_temp_id(self.raw, tmpId) else {
return nil
}
return entidPtr.pointee
}
override func cleanup(pointer: OpaquePointer) {
tx_report_destroy(pointer)
}
}

View file

@ -5,13 +5,20 @@
#ifndef store_h
#define store_h
#include <stdint.h>
#include <Foundation/NSObjCRuntime.h>
struct ExternTxReport {
struct TransactionChange {
int64_t txid;
int64_t*_Nonnull* _Nonnull changes;
uint64_t len;
};
struct TxChangeList {
struct TransactionChange*_Nonnull* _Nonnull reports;
uint64_t len;
};
typedef struct TxChangeList TxChangeList;
struct Result {
void* _Nullable ok;
char* _Nullable err;
@ -23,20 +30,25 @@ struct Option {
};
typedef struct Option Option;
struct Store;
struct TxReportList {
struct ExternTxReport*_Nonnull* _Nonnull reports;
uint64_t len;
typedef NS_ENUM(NSInteger, ValueType) {
ValueTypeRef = 1,
ValueTypeBoolean,
ValueTypeInstant,
ValueTypeLong,
ValueTypeDouble,
ValueTypeString,
ValueTypeKeyword,
ValueTypeUuid
};
typedef struct TxReportList TxReportList;
struct Query;
struct TypedValue;
struct QueryResultRow;
struct QueryResultRows;
struct QueryRowsIterator;
struct QueryRowIterator;
struct Store;
struct TxReport;
struct TypedValue;
// Store
struct Store*_Nonnull store_open(const char*_Nonnull uri);
@ -44,6 +56,7 @@ struct Store*_Nonnull store_open(const char*_Nonnull uri);
void destroy(void* _Nullable obj);
void query_builder_destroy(struct Query* _Nullable obj);
void store_destroy(struct Store* _Nonnull obj);
void tx_report_destroy(struct TxReport* _Nonnull obj);
void typed_value_destroy(struct TypedValue* _Nullable obj);
void typed_value_list_destroy(struct QueryResultRow* _Nullable obj);
void typed_value_list_iter_destroy(struct QueryRowIterator* _Nullable obj);
@ -52,15 +65,18 @@ void typed_value_result_set_iter_destroy(struct QueryRowsIterator* _Nullable obj
// transact
struct Result*_Nonnull store_transact(struct Store*_Nonnull store, const char* _Nonnull transaction);
const int64_t* _Nullable tx_report_entity_for_temp_id(const struct TxReport* _Nonnull report, const char* _Nonnull tempid);
int64_t tx_report_get_entid(const struct TxReport* _Nonnull report);
int64_t tx_report_get_tx_instant(const struct TxReport* _Nonnull report);
// Sync
struct Result*_Nonnull store_sync(struct Store*_Nonnull store, const char* _Nonnull user_uuid, const char* _Nonnull server_uri);
// Observers
void store_register_observer(struct Store*_Nonnull store, const char* _Nonnull key, const int64_t* _Nonnull attributes, const int64_t len, void (*_Nonnull callback_fn)(const char* _Nonnull key, const struct TxReportList* _Nonnull reports));
void store_register_observer(struct Store*_Nonnull store, const char* _Nonnull key, const int64_t* _Nonnull attributes, const int64_t len, void (*_Nonnull callback_fn)(const char* _Nonnull key, const struct TxChangeList* _Nonnull reports));
void store_unregister_observer(struct Store*_Nonnull store, const char* _Nonnull key);
int64_t store_entid_for_attribute(struct Store*_Nonnull store, const char*_Nonnull attr);
const struct int64_t changelist_entry_at(const struct ExternTxReport* _Nonnull report, size_t index);
int64_t changelist_entry_at(const struct TransactionChange* _Nonnull report, size_t index);
// Query
struct Query*_Nonnull store_query(struct Store*_Nonnull store, const char* _Nonnull query);
@ -93,6 +109,7 @@ double typed_value_as_double(struct TypedValue*_Nonnull value);
int64_t typed_value_as_timestamp(struct TypedValue*_Nonnull value);
const char* _Nonnull typed_value_as_string(struct TypedValue*_Nonnull value);
const char* _Nonnull typed_value_as_uuid(struct TypedValue*_Nonnull value);
enum ValueType typed_value_value_type(struct TypedValue*_Nonnull value);
struct QueryResultRow* _Nullable row_at_index(struct QueryResultRows* _Nonnull rows, const int32_t index);
struct QueryRowsIterator* _Nonnull rows_iter(struct QueryResultRows* _Nonnull rows);
@ -128,7 +145,7 @@ struct Result*_Nonnull store_set_timestamp_for_attribute_on_entid(struct Store*_
struct Result*_Nonnull store_set_string_for_attribute_on_entid(struct Store*_Nonnull store, const int64_t entid, const char* _Nonnull attribute, const char* _Nonnull value);
struct Result*_Nonnull store_set_uuid_for_attribute_on_entid(struct Store*_Nonnull store, const int64_t entid, const char* _Nonnull attribute, const char* _Nonnull value);
// TxReports
const struct ExternTxReport* _Nullable tx_report_list_entry_at(const struct TxReportList* _Nonnull list, size_t index);
// Transaction change lists
const struct TransactionChange* _Nullable tx_change_list_entry_at(const struct TxChangeList* _Nonnull list, size_t index);
#endif /* store_h */

View file

@ -3,11 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import XCTest
@testable import Mentat
class MentatTests: XCTestCase {
var schema: String?
var edn: String?
var store: Mentat?
override func setUp() {
super.setUp()
@ -35,8 +38,8 @@ class MentatTests: XCTestCase {
func readSchema() throws -> String {
guard let schema = self.schema else {
let bundle = Bundle(for: type(of: self))
guard let schemaPath = bundle.path(forResource: "cities", ofType: "schema") else { return "" }
let schema = try String(contentsOf: URL(fileURLWithPath: schemaPath))
let schemaUrl = bundle.url(forResource: "cities", withExtension: "schema", subdirectory: "fixtures")!
let schema = try String(contentsOf: schemaUrl)
self.schema = schema
return schema
}
@ -44,16 +47,790 @@ class MentatTests: XCTestCase {
return schema
}
func testTransactVocabulary() {
do {
func readEdn() throws -> String {
guard let edn = self.edn else {
let bundle = Bundle(for: type(of: self))
let ednUrl = bundle.url(forResource: "all_seattle", withExtension: "edn", subdirectory: "fixtures")!
let edn = try String(contentsOf: ednUrl)
self.edn = edn
return edn
}
return edn
}
func transactSchema(mentat: Mentat) throws -> TxReport {
let vocab = try readSchema()
let report = try mentat.transact(transaction: vocab)
return report
}
func transactEdn(mentat: Mentat) throws -> TxReport {
let edn = try readEdn()
let report = try mentat.transact(transaction: edn)
return report
}
func newStore() -> Mentat {
guard let mentat = self.store else {
let mentat = Mentat()
let success = try mentat.transact(transaction: vocab)
assert( success )
let _ = try! self.transactSchema(mentat: mentat)
let _ = try! self.transactEdn(mentat: mentat)
self.store = mentat
return mentat
}
return mentat
}
func populateWithTypesSchema(mentat: Mentat) -> TxReport? {
do {
let schema = """
[
[:db/add "b" :db/ident :foo/boolean]
[:db/add "b" :db/valueType :db.type/boolean]
[:db/add "b" :db/cardinality :db.cardinality/one]
[:db/add "l" :db/ident :foo/long]
[:db/add "l" :db/valueType :db.type/long]
[:db/add "l" :db/cardinality :db.cardinality/one]
[:db/add "r" :db/ident :foo/ref]
[:db/add "r" :db/valueType :db.type/ref]
[:db/add "r" :db/cardinality :db.cardinality/one]
[:db/add "i" :db/ident :foo/instant]
[:db/add "i" :db/valueType :db.type/instant]
[:db/add "i" :db/cardinality :db.cardinality/one]
[:db/add "d" :db/ident :foo/double]
[:db/add "d" :db/valueType :db.type/double]
[:db/add "d" :db/cardinality :db.cardinality/one]
[:db/add "s" :db/ident :foo/string]
[:db/add "s" :db/valueType :db.type/string]
[:db/add "s" :db/cardinality :db.cardinality/one]
[:db/add "k" :db/ident :foo/keyword]
[:db/add "k" :db/valueType :db.type/keyword]
[:db/add "k" :db/cardinality :db.cardinality/one]
[:db/add "u" :db/ident :foo/uuid]
[:db/add "u" :db/valueType :db.type/uuid]
[:db/add "u" :db/cardinality :db.cardinality/one]
]
"""
let report = try mentat.transact(transaction: schema)
let stringEntid = report.entidForTmpId(tmpId: "s")!
let data = """
[
[:db/add "a" :foo/boolean true]
[:db/add "a" :foo/long 25]
[:db/add "a" :foo/instant #inst "2017-01-01T11:00:00.000Z"]
[:db/add "a" :foo/double 11.23]
[:db/add "a" :foo/string "The higher we soar the smaller we appear to those who cannot fly."]
[:db/add "a" :foo/keyword :foo/string]
[:db/add "a" :foo/uuid #uuid "550e8400-e29b-41d4-a716-446655440000"]
[:db/add "b" :foo/boolean false]
[:db/add "b" :foo/ref \(stringEntid)]
[:db/add "b" :foo/long 50]
[:db/add "b" :foo/instant #inst "2018-01-01T11:00:00.000Z"]
[:db/add "b" :foo/double 22.46]
[:db/add "b" :foo/string "Silence is worse; all truths that are kept silent become poisonous."]
[:db/add "b" :foo/uuid #uuid "4cb3f828-752d-497a-90c9-b1fd516d5644"]
]
"""
return try mentat.transact(transaction: data)
} catch {
assertionFailure(error.localizedDescription)
}
return nil
}
func test1TransactVocabulary() {
do {
let mentat = Mentat()
let report = try transactSchema(mentat: mentat)
XCTAssertNotNil(report)
assert(report.txId > 0)
} catch {
assertionFailure(error.localizedDescription)
}
}
// TODO: Add more tests once we are able to add vocabulary and transact entities
func test2TransactEntities() {
do {
let mentat = Mentat()
let _ = try self.transactSchema(mentat: mentat)
let report = try self.transactEdn(mentat: mentat)
XCTAssertNotNil(report)
assert(report.txId > 0)
let entid = report.entidForTmpId(tmpId: "a17592186045438")
assert(entid == 65566)
} catch {
assertionFailure(error.localizedDescription)
}
}
func testQueryScalar() {
let mentat = newStore()
let query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query).bind(varName: "?name", toString: "Wallingford").executeScalar(callback: { (scalarResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let result = scalarResult?.asString() else {
return assertionFailure("No String value received")
}
assert(result == "KOMO Communities - Wallingford")
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testQueryColl() {
let mentat = newStore()
let query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query).executeColl(callback: { (collResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let rows = collResult else {
return assertionFailure("No results received")
}
// we are expecting 3 results
for i in 0..<3 {
let _ = rows.asDate(index: i)
assert(true)
}
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testQueryCollResultIterator() {
let mentat = newStore()
let query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query).executeColl(callback: { (collResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let rows = collResult else {
return assertionFailure("No results received")
}
rows.forEach({ (value) in
assert(value.valueType.rawValue == 2)
})
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testQueryTuple() {
let mentat = newStore()
let query = """
[:find [?name ?cat]
:where
[?c :community/name ?name]
[?c :community/type :community.type/website]
[(fulltext $ :community/category "food") [[?c ?cat]]]]
"""
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query).executeTuple(callback: { (tupleResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let tuple = tupleResult else {
return assertionFailure("expecting a result")
}
let name = tuple.asString(index: 0)
let category = tuple.asString(index: 1)
assert(name == "Community Harvest of Southwest Seattle")
assert(category == "sustainable food")
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testQueryRel() {
let mentat = newStore()
let query = """
[:find ?name ?cat
:where
[?c :community/name ?name]
[?c :community/type :community.type/website]
[(fulltext $ :community/category "food") [[?c ?cat]]]]
"""
let expect = expectation(description: "Query is executed")
let expectedResults = [("InBallard", "food"),
("Seattle Chinatown Guide", "food"),
("Community Harvest of Southwest Seattle", "sustainable food"),
("University District Food Bank", "food bank")]
try! mentat.query(query: query).execute(callback: { (relResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let rows = relResult else {
return assertionFailure("No results received")
}
for (i, row) in rows.enumerated() {
let (name, category) = expectedResults[i]
assert( row.asString(index: 0) == name)
assert(row.asString(index: 1) == category)
}
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testQueryRelResultIterator() {
let mentat = newStore()
let query = """
[:find ?name ?cat
:where
[?c :community/name ?name]
[?c :community/type :community.type/website]
[(fulltext $ :community/category "food") [[?c ?cat]]]]
"""
let expect = expectation(description: "Query is executed")
let expectedResults = [("InBallard", "food"),
("Seattle Chinatown Guide", "food"),
("Community Harvest of Southwest Seattle", "sustainable food"),
("University District Food Bank", "food bank")]
try! mentat.query(query: query).execute(callback: { (relResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let rows = relResult else {
return assertionFailure("No results received")
}
var i = 0
rows.forEach({ (row) in
let (name, category) = expectedResults[i]
i += 1
assert(row.asString(index: 0) == name)
assert(row.asString(index: 1) == category)
})
assert(i == 4)
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindLong() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")
let query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?bool", toBoolean: true)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindRef() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let stringEntid = mentat.entidForAttribute(attribute: ":foo/string")
let bEntid = report.entidForTmpId(tmpId: "b")
let query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?ref", toReference: stringEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == bEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindKwRef() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let bEntid = report.entidForTmpId(tmpId: "b")
let query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?ref", toReference: ":foo/string")
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == bEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindKw() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")
let query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?kw", toKeyword: ":foo/string")
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindDate() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")
let query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?now", toDate: Date())
.executeTuple { (row, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(row)
assert(row?.asEntid(index: 0) == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindString() {
let mentat = newStore()
let query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?name", toString: "Wallingford")
.executeScalar(callback: { (scalarResult, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
guard let result = scalarResult?.asString() else {
return assertionFailure("No String value received")
}
assert(result == "KOMO Communities - Wallingford")
expect.fulfill()
})
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindUuid() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")
let query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]"
let uuid = UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?uuid", toUuid: uuid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindBoolean() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")
let query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?bool", toBoolean: true)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testBindDouble() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")
let query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?double", toDouble: 11.23)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsLong() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asLong() == 25)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsRef() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?e . :where [?e :foo/long 25]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asEntid() == aEntid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsKw() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asKeyword() == ":foo/string")
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsBoolean() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asBool() == true)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsDouble() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asDouble() == 11.23)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsDate() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]"
let expect = expectation(description: "Query is executed")
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
let expectedDate = formatter.date(from: "2017-01-01T11:00:00+00:00")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asDate() == expectedDate)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsString() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]"
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asString() == "The higher we soar the smaller we appear to those who cannot fly.")
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testTypedValueAsUuid() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]"
let expectedUuid = UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!
let expect = expectation(description: "Query is executed")
try! mentat.query(query: query)
.bind(varName: "?e", toReference: aEntid)
.executeScalar { (value, error) in
assert(error == nil, "Unexpected error: \(String(describing: error))")
XCTAssertNotNil(value)
assert(value?.asUUID() == expectedUuid)
expect.fulfill()
}
waitForExpectations(timeout: 1) { error in
if let error = error {
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
}
}
}
func testValueForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let value = mentat.value(forAttribute: ":foo/long", ofEntity: aEntid)
XCTAssertNotNil(value)
assert(value?.asLong() == 25)
}
func testEntidForAttribute() {
let mentat = Mentat()
let _ = self.populateWithTypesSchema(mentat: mentat)!
let entid = mentat.entidForAttribute(attribute: ":foo/long")
assert(entid == 65540)
}
func testSetLongForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let attr = ":foo/long"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(pre)
assert(pre?.asLong() == 25)
XCTAssertNoThrow(try mentat.set(long: 100, forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
assert(post?.asLong() == 100)
}
func testSetBooleanForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let attr = ":foo/boolean"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(pre)
assert(pre?.asBool() == true)
XCTAssertNoThrow(try mentat.set(boolean: false, forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
let p = post?.asBool()
assert(p == false)
}
func testSetRefForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let bEntid = report.entidForTmpId(tmpId: "b")!
let attr = ":foo/ref"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNil(pre)
XCTAssertNoThrow(try mentat.set(reference: bEntid, forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
assert(post?.asEntid() == bEntid)
}
func testSetRefKwForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let attr = ":foo/ref"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNil(pre)
XCTAssertNoThrow(try mentat.setKeywordReference(value: ":foo/long", forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
assert(post?.asEntid() == 65540)
}
func testSetDateForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let attr = ":foo/instant"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
let previousDate = formatter.date(from: "2017-01-01T11:00:00+00:00")
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(pre)
assert(pre?.asDate() == previousDate)
let now = Date()
XCTAssertNoThrow(try mentat.set(date: now, forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
let p = post?.asDate()
assert(formatter.string(from: p!) == formatter.string(from: now))
}
func testSetDoubleForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let attr = ":foo/double"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(pre)
assert(pre?.asDouble() == 11.23)
XCTAssertNoThrow(try mentat.set(double: 22.0, forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
assert(post?.asDouble() == 22.0)
}
func testSetStringForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let attr = ":foo/string"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(pre)
assert(pre?.asString() == "The higher we soar the smaller we appear to those who cannot fly.")
XCTAssertNoThrow(try mentat.set(string: "Become who you are!", forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
assert(post?.asString() == "Become who you are!")
}
func testSetUuidForAttributeOfEntity() {
let mentat = Mentat()
let report = self.populateWithTypesSchema(mentat: mentat)!
let aEntid = report.entidForTmpId(tmpId: "a")!
let previousUuid = UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!
let attr = ":foo/uuid"
let pre = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(pre)
assert(pre?.asUUID() == previousUuid)
let newUuid = UUID()
XCTAssertNoThrow(try mentat.set(uuid: newUuid, forAttribute: attr, onEntity: aEntid))
let post = mentat.value(forAttribute: attr, ofEntity: aEntid)
XCTAssertNotNil(post)
assert(post?.asUUID() == newUuid)
}
}