swift-cgo: Implement scan/delete/batch/permission/get/put
Various refactors, formatting, and bug fixes plus:
Implements:
Collection.Scan
Collection.DeleteRange
Collection.get/put using user-supplied byte arrays*
Database.watch using user-supplied byte arrays*
Database.setPermission/getPermission
Service.setPermission/getPermission
Collection.getPermission/setPermission
Batch.runInBatch
Database.beginBatch / BatchDatabase.abort/commit
Unit tests:
Scan
Delete
Get/put
Batch operations
Missing unit tests:
None for permissions
* Removes the JSON support for arbitrary byte arrays as a stop
gap until VOM is implemented in Swift.
MultiPart: 2/2
Change-Id: I1cef38dbf3aa444d1a7fb6f679ccfa022cbd9206
diff --git a/SyncbaseCore/Source/Batch.swift b/SyncbaseCore/Source/Batch.swift
index f3bf94b..febd0f8 100644
--- a/SyncbaseCore/Source/Batch.swift
+++ b/SyncbaseCore/Source/Batch.swift
@@ -4,23 +4,76 @@
import Foundation
-class BatchHandle {
-}
-
public enum Batch {
- public typealias BatchCompletionHandler = SyncbaseError? -> Void
- public typealias Operation = Void -> BatchCompletionHandler
+ public typealias BatchCompletionHandler = ErrorType? -> Void
+ public typealias Operation = BatchDatabase throws -> Void
/**
Runs the given batch operation, managing retries and BatchDatabase's commit() and abort()s.
- - Parameter db: database on which the batch operation is to be performed
- - Parameter opts: batch configuration
- - Parameter op: batch operation
+ This is run in a background thread and calls back on main.
+
+ - Parameter retries: number of retries attempted before giving up. defaults to 3
+ - Parameter db: database on which the batch operation is to be performed
+ - Parameter opts: batch configuration
+ - Parameter op: batch operation
- Parameter completionHandler: future result called when runInBatch finishes
*/
- public static func runInBatch(db: Database, opts: BatchOptions, op: Operation, completionHandler: BatchCompletionHandler) {
- preconditionFailure("stub")
+ public static func runInBatch(retries: Int = 3,
+ db: Database,
+ opts: BatchOptions?,
+ op: Operation,
+ completionHandler: BatchCompletionHandler) {
+ RunInBackgroundQueue {
+ for _ in 0...retries {
+ // TODO(sadovsky): Commit() can fail for a number of reasons, e.g. RPC
+ // failure or ErrConcurrentTransaction. Depending on the cause of failure,
+ // it may be desirable to retry the Commit() and/or to call Abort().
+ var err: ErrorType? = nil
+ do {
+ try attemptBatch(db, opts: opts, op: op)
+ err = nil
+ } catch SyncbaseError.ConcurrentBatch {
+ continue
+ } catch let e {
+ log.warning("Unable to complete batch operation: \(e)")
+ err = e
+ }
+ RunInMainQueue { completionHandler(err) }
+ return
+ }
+ // We never were able to do it without error
+ RunInMainQueue { completionHandler(SyncbaseError.ConcurrentBatch) }
+ }
+ }
+
+ private static func attemptBatch(db: Database, opts: BatchOptions?, op: Operation) throws {
+ let batchDb = try db.beginBatch(opts)
+ // Use defer for abort to make sure it gets called in case op throws.
+ var commitCalled = false
+ defer {
+ if !commitCalled {
+ do {
+ try batchDb.abort()
+ } catch let e {
+ log.warning("Unable abort the non-comitted batch: \(e)")
+ }
+ }
+ }
+ // Attempt operation
+ try op(batchDb)
+ // A readonly batch should be Aborted; Commit would fail.
+ if opts?.readOnly ?? false {
+ return
+ }
+ // Commit is about to be called, do not call Abort.
+ commitCalled = true
+ do {
+ try batchDb.commit()
+ } catch SyncbaseError.UnknownBatch {
+ // Occurs if op called batchDb.abort() or batchDb.commit() -- ignore the unknown batch
+ // at this point.
+ }
}
}
@@ -37,14 +90,34 @@
public let readOnly: Bool = false
}
-public protocol BatchDatabase: DatabaseHandle {
+public class BatchDatabase: Database {
/// Commit persists the pending changes to the database.
/// If the batch is readonly, Commit() will fail with ErrReadOnlyBatch; Abort()
/// should be used instead.
- func commit() throws
+ public func commit() throws {
+ guard let cHandle = try batchHandle?.toCgoString() else {
+ throw SyncbaseError.UnknownBatch
+ }
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_DbCommit(
+ try encodedDatabaseName.toCgoString(),
+ cHandle,
+ errPtr)
+ }
+ }
/// Abort notifies the server that any pending changes can be discarded.
/// It is not strictly required, but it may allow the server to release locks
/// or other resources sooner than if it was not called.
- func abort() throws
+ public func abort() throws {
+ guard let cHandle = try batchHandle?.toCgoString() else {
+ throw SyncbaseError.UnknownBatch
+ }
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_DbAbort(
+ try encodedDatabaseName.toCgoString(),
+ cHandle,
+ errPtr)
+ }
+ }
}
diff --git a/SyncbaseCore/Source/Collection.swift b/SyncbaseCore/Source/Collection.swift
index 8ce81d1..269c34a 100644
--- a/SyncbaseCore/Source/Collection.swift
+++ b/SyncbaseCore/Source/Collection.swift
@@ -27,15 +27,10 @@
let batchHandle: String?
let encodedCollectionName: String
- init?(databaseId: Identifier, collectionId: Identifier, batchHandle: String?) {
+ init(databaseId: Identifier, collectionId: Identifier, batchHandle: String?) throws {
self.collectionId = collectionId
self.batchHandle = batchHandle
- do {
- self.encodedCollectionName = try Collection.encodedName(databaseId, collectionId: collectionId)
- } catch {
- // UTF8 encoding error.
- return nil
- }
+ self.encodedCollectionName = try Collection.encodedName(databaseId, collectionId: collectionId)
}
/// Exists returns true only if this Collection exists. Insufficient
@@ -58,13 +53,10 @@
/// TODO(sadovsky): Specify what happens if perms is nil.
public func create(permissions: Permissions?) throws {
try VError.maybeThrow { errPtr in
- guard let cPermissions = v23_syncbase_Permissions(permissions) else {
- throw SyncbaseError.PermissionsSerializationError(permissions: permissions)
- }
v23_syncbase_CollectionCreate(
try encodedCollectionName.toCgoString(),
try cBatchHandle(),
- cPermissions,
+ try v23_syncbase_Permissions(permissions),
errPtr)
}
}
@@ -86,12 +78,27 @@
/// receive the rows in this Collection. It only determines which clients
/// are allowed to retrieve the value using a Syncbase RPC.
public func getPermissions() throws -> Permissions {
- preconditionFailure("stub")
+ var permissions = v23_syncbase_Permissions()
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_CollectionGetPermissions(
+ try encodedCollectionName.toCgoString(),
+ try cBatchHandle(),
+ &permissions,
+ errPtr)
+ }
+ // TODO(zinman): Verify that permissions defaulting to zero-value is correct.
+ return try permissions.toPermissions() ?? Permissions()
}
/// SetPermissions replaces the current Permissions for the Collection.
public func setPermissions(permissions: Permissions) throws {
- preconditionFailure("stub")
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_CollectionSetPermissions(
+ try encodedCollectionName.toCgoString(),
+ try cBatchHandle(),
+ try v23_syncbase_Permissions(permissions),
+ errPtr)
+ }
}
/**
@@ -113,37 +120,50 @@
let isRed: Bool = try collection.get("isRed")
```
*/
- public func get<T: SyncbaseJsonConvertible>(key: String, inout value: T?) throws {
+ public func get<T: SyncbaseConvertible>(key: String, inout value: T?) throws {
// TODO(zinman): We should probably kill this variant unless it provides .dynamicType benefits
// with VOM support and custom class serialization/deserialization.
value = try get(key)
}
/// Get loads the value stored under the given key.
- public func get<T: SyncbaseJsonConvertible>(key: String) throws -> T? {
- guard let jsonData = try getRawBytes(key) else {
+ public func get<T: SyncbaseConvertible>(key: String) throws -> T? {
+ guard let data = try getRawBytes(key) else {
return nil
}
- let value: T = try T.fromSyncbaseJson(jsonData)
+ let value: T = try T.deserializeFromSyncbase(data)
return value
}
func getRawBytes(key: String) throws -> NSData? {
var cBytes = v23_syncbase_Bytes()
- try VError.maybeThrow { errPtr in
- v23_syncbase_RowGet(try encodedRowName(key),
- try cBatchHandle(),
- &cBytes,
- errPtr)
+ do {
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_RowGet(
+ try encodedRowName(key),
+ try cBatchHandle(),
+ &cBytes,
+ errPtr)
+ }
+ } catch let e as VError {
+ if e.id == "v.io/v23/verror.NoExist" {
+ return nil
+ }
+ throw e
}
- return cBytes.toNSData()
+ // If we got here then we know that row exists, otherwise we would have gotten the NoExist
+ // exception above. However, that row might also just be empty data. Because
+ // cBytes.toNSData can't distinguish between the struct's zero-values and a nil array,
+ // we must explicitly default to an empty NSData here since we know that it is not nil.
+ return cBytes.toNSData() ?? NSData()
}
/// Put writes the given value to this Collection under the given key.
- public func put(key: String, value: SyncbaseJsonConvertible) throws {
- let (data, _) = try value.toSyncbaseJson()
+ public func put(key: String, value: SyncbaseConvertible) throws {
+ let data = try value.serializeToSyncbase()
try VError.maybeThrow { errPtr in
- v23_syncbase_RowPut(try encodedRowName(key),
+ v23_syncbase_RowPut(
+ try encodedRowName(key),
try cBatchHandle(),
v23_syncbase_Bytes(data),
errPtr)
@@ -153,7 +173,8 @@
/// Delete deletes the row for the given key.
public func delete(key: String) throws {
try VError.maybeThrow { errPtr in
- v23_syncbase_RowDelete(try encodedRowName(key),
+ v23_syncbase_RowDelete(
+ try encodedRowName(key),
try cBatchHandle(),
errPtr)
}
@@ -166,7 +187,20 @@
/// deletions?
/// See helpers Prefix(), Range(), SingleRow().
public func deleteRange(r: RowRange) throws {
- preconditionFailure("stub")
+ try VError.maybeThrow { errPtr in
+ let cStartStr = try r.start.toCgoString()
+ let cLimitStr = try r.limit.toCgoString()
+ let cStartBytes = v23_syncbase_Bytes(
+ p: unsafeBitCast(cStartStr.p, UnsafeMutablePointer<UInt8>.self), n: cStartStr.n)
+ let cLimitBytes = v23_syncbase_Bytes(
+ p: unsafeBitCast(cLimitStr.p, UnsafeMutablePointer<UInt8>.self), n: cLimitStr.n)
+ v23_syncbase_CollectionDeleteRange(
+ try encodedCollectionName.toCgoString(),
+ try cBatchHandle(),
+ cStartBytes,
+ cLimitBytes,
+ errPtr)
+ }
}
/// Scan returns all rows in the given half-open range [start, limit). If limit
@@ -176,8 +210,128 @@
/// time of the RPC (or at the time of BeginBatch, if in a batch), and will not
/// reflect subsequent writes to keys not yet reached by the stream.
/// See helpers Prefix(), Range(), SingleRow().
- public func scan(r: RowRange) -> ScanStream {
- preconditionFailure("stub")
+ private static let scanQueue = dispatch_queue_create("ScanQueue", DISPATCH_QUEUE_CONCURRENT)
+ public func scan(r: RowRange) throws -> ScanStream {
+ // Scan works by having Go call Swift as encounters each row. This is a bit of a mismatch
+ // for the Swift GeneratorType which is pull instead of push-based. We create a push-pull
+ // adapter by blocking on either side using condition variables until both are ready for the
+ // next data handoff. Adding to the complexity is that Go has separate callbacks for when it has
+ // data or is done, yet the GeneratorType uses a single fetch function that handles both
+ // conditions (the data is nil when it's done). Thus we end up with 2 different callbacks from
+ // Go with similar condition-variable logic.
+ let condition = NSCondition()
+ var data: (String, NSData)? = nil
+ var doneErr: ErrorType? = nil
+ var updateAvailable = false
+
+ // The anonymous function that gets called from the Swift. It blocks until there's an update
+ // available from Go.
+ let fetchNext = { () -> ((String, GetValueFromScanStream)?, ErrorType?) in
+ condition.lock()
+ while !updateAvailable {
+ condition.wait()
+ }
+ // Grab the data from this update and reset for the next update.
+ let fetchedData = data
+ data = nil
+ updateAvailable = false
+ // Signal that we've fetched the data to Go.
+ condition.signal()
+ condition.unlock()
+ // Default the ret to nil (valid for isDone).
+ var ret: (String, GetValueFromScanStream)? = nil
+ if let d = fetchedData {
+ // Create the closured function that deserializes the data from Syncbase on demand.
+ ret = (d.0, { () throws -> SyncbaseConvertible in
+ // Let T be inferred by being explicit about NSData (the only conversion possible until
+ // we have VOM support).
+ let data: NSData = try NSData.deserializeFromSyncbase(d.1)
+ return data
+ })
+ }
+ return (ret, doneErr)
+ }
+
+ // The callback from Go when there's a new Row (key-value) scanned.
+ let onKV = { (key: String, valueBytes: NSData) in
+ condition.lock()
+ // Wait until any existing update has been received by the fetch so we don't just blow
+ // past it.
+ while updateAvailable {
+ condition.wait()
+ }
+ // Set the new data.
+ data = (key, valueBytes)
+ updateAvailable = true
+ // Wake up any blocked fetch.
+ condition.signal()
+ condition.unlock()
+ }
+
+ let onDone = { (err: ErrorType?) in
+ condition.lock()
+ // Wait until any existing update has been received by the fetch so we don't just blow
+ // past it.
+ while updateAvailable {
+ condition.wait()
+ }
+ // Marks the end of data by clearing it and saving any associated error from Syncbase.
+ data = nil
+ doneErr = err
+ updateAvailable = true
+ // Wake up any blocked fetch.
+ condition.signal()
+ condition.unlock()
+ }
+
+ try VError.maybeThrow { errPtr in
+ let cStartStr = try r.start.toCgoString()
+ let cLimitStr = try r.limit.toCgoString()
+ let cStartBytes = v23_syncbase_Bytes(
+ p: unsafeBitCast(cStartStr.p, UnsafeMutablePointer<UInt8>.self), n: cStartStr.n)
+ let cLimitBytes = v23_syncbase_Bytes(
+ p: unsafeBitCast(cLimitStr.p, UnsafeMutablePointer<UInt8>.self), n: cLimitStr.n)
+ let callbacks = v23_syncbase_CollectionScanCallbacks(
+ hOnKeyValue: Collection.onScanKVClosures.ref(onKV),
+ hOnDone: Collection.onScanDoneClosures.ref(onDone),
+ onKeyValue: { Collection.onScanKV(AsyncId($0), kv: $1) },
+ onDone: { Collection.onScanDone(AsyncId($0), doneHandle: AsyncId($1), err: $2) })
+ v23_syncbase_CollectionScan(
+ try encodedCollectionName.toCgoString(),
+ try cBatchHandle(),
+ cStartBytes,
+ cLimitBytes,
+ callbacks,
+ errPtr)
+ }
+
+ return AnonymousStream(fetchNextFunction: fetchNext, cancelFunction: { })
+ }
+
+ // Reference maps between closured functions and handles passed back/forth with Go.
+ private static var onScanKVClosures = RefMap < (String, NSData) -> Void > ()
+ private static var onScanDoneClosures = RefMap < ErrorType? -> Void > ()
+
+ // Callback handlers that convert the Cgo bridge types to native Swift types and pass them to
+ // the closured functions reference by the passed handle.
+ private static func onScanKV(handle: AsyncId, kv: v23_syncbase_KeyValue) {
+ guard let key = kv.key.toString(),
+ valueBytes = kv.value.toNSData(),
+ callback = onScanKVClosures.get(handle) else {
+ log.warning("Could not fully unpact scan kv callback; dropping")
+ return
+ }
+ callback(key, valueBytes)
+ }
+
+ private static func onScanDone(kvHandle: AsyncId, doneHandle: AsyncId, err: v23_syncbase_VError) {
+ let e = err.toVError()
+ onScanKVClosures.unref(kvHandle)
+ guard let callback = onScanDoneClosures.unref(doneHandle) else {
+ log.warning("Could not find closure for onDone handle; dropping")
+ return
+ }
+ callback(e)
}
// MARK: Internal helpers
@@ -206,7 +360,7 @@
}
/// Returns the decoded value, or throws an error if the value could not be decoded.
-public typealias GetValueFromScanStream = () throws -> SyncbaseJsonConvertible
+public typealias GetValueFromScanStream = () throws -> SyncbaseConvertible
/// Stream resulting from a scan on a scollection for a given row range.
public typealias ScanStream = AnonymousStream<(String, GetValueFromScanStream)>
diff --git a/SyncbaseCore/Source/Database.swift b/SyncbaseCore/Source/Database.swift
index effac29..6101a02 100644
--- a/SyncbaseCore/Source/Database.swift
+++ b/SyncbaseCore/Source/Database.swift
@@ -32,25 +32,20 @@
let batchHandle: String?
let encodedDatabaseName: String
- init?(databaseId: Identifier, batchHandle: String?) {
+ init(databaseId: Identifier, batchHandle: String?) throws {
self.databaseId = databaseId
self.batchHandle = batchHandle
- do {
- self.encodedDatabaseName = try databaseId.encodeId().toString()!
- } catch {
- // UTF8 encoding error.
- return nil
- }
+ self.encodedDatabaseName = try databaseId.encodeId().toString()!
}
/// Create creates this Database.
/// TODO(sadovsky): Specify what happens if perms is nil.
public func create(permissions: Permissions?) throws {
try VError.maybeThrow { errPtr in
- guard let cPermissions = v23_syncbase_Permissions(permissions) else {
- throw SyncbaseError.PermissionsSerializationError(permissions: permissions)
- }
- v23_syncbase_DbCreate(try encodedDatabaseName.toCgoString(), cPermissions, errPtr)
+ v23_syncbase_DbCreate(
+ try encodedDatabaseName.toCgoString(),
+ try v23_syncbase_Permissions(permissions),
+ errPtr)
}
}
@@ -96,7 +91,18 @@
TODO(sadovsky): Use varargs for options.
*/
public func beginBatch(options: BatchOptions?) throws -> BatchDatabase {
- preconditionFailure("stub")
+ var cHandle = v23_syncbase_String()
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_DbBeginBatch(
+ try encodedDatabaseName.toCgoString(),
+ try v23_syncbase_BatchOptions(options),
+ &cHandle,
+ errPtr)
+ }
+ guard let handle = cHandle.toString() else {
+ throw SyncbaseError.InvalidUTF8(invalidUtf8: "\(cHandle)")
+ }
+ return try BatchDatabase(databaseId: databaseId, batchHandle: handle)
}
/// Watch allows a client to watch for updates to the database. For each watch
@@ -161,19 +167,17 @@
}
public func collection(collectionId: Identifier) throws -> Collection {
- guard let collection = Collection(
+ return try Collection(
databaseId: databaseId,
collectionId: collectionId,
- batchHandle: batchHandle) else {
- throw SyncbaseError.InvalidUTF8(invalidUtf8: "\(collectionId)")
- }
- return collection
+ batchHandle: batchHandle)
}
public func listCollections() throws -> [Identifier] {
return try VError.maybeThrow { errPtr in
var ids = v23_syncbase_Ids()
- v23_syncbase_DbListCollections(try encodedDatabaseName.toCgoString(),
+ v23_syncbase_DbListCollections(
+ try encodedDatabaseName.toCgoString(),
try batchHandle?.toCgoString() ?? v23_syncbase_String(),
&ids,
errPtr)
@@ -182,16 +186,41 @@
}
public func getResumeMarker() throws -> ResumeMarker {
- preconditionFailure("stub")
+ var cMarker = v23_syncbase_Bytes()
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_DbGetResumeMarker(
+ try encodedDatabaseName.toCgoString(),
+ try batchHandle?.toCgoString() ?? v23_syncbase_String(),
+ &cMarker,
+ errPtr)
+ }
+ return ResumeMarker(data: cMarker.toNSData() ?? NSData())
}
}
extension Database: AccessController {
- public func setPermissions(perms: Permissions, version: PermissionsVersion) throws {
- preconditionFailure("stub")
+ public func getPermissions() throws -> (Permissions, PermissionsVersion) {
+ var cPermissions = v23_syncbase_Permissions()
+ var cVersion = v23_syncbase_String()
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_DbGetPermissions(
+ try encodedDatabaseName.toCgoString(),
+ &cPermissions,
+ &cVersion,
+ errPtr)
+ }
+ // TODO(zinman): Verify that permissions defaulting to zero-value is correct for Permissions.
+ // We force cast of cVersion because we know it can be UTF8 converted.
+ return (try cPermissions.toPermissions() ?? Permissions(), cVersion.toString()!)
}
- public func getPermissions() throws -> (Permissions, PermissionsVersion) {
- preconditionFailure("stub")
+ public func setPermissions(permissions: Permissions, version: PermissionsVersion) throws {
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_DbSetPermissions(
+ try encodedDatabaseName.toCgoString(),
+ try v23_syncbase_Permissions(permissions),
+ try version.toCgoString(),
+ errPtr)
+ }
}
}
diff --git a/SyncbaseCore/Source/Errors.swift b/SyncbaseCore/Source/Errors.swift
index 5d75ba9..755cfd1 100644
--- a/SyncbaseCore/Source/Errors.swift
+++ b/SyncbaseCore/Source/Errors.swift
@@ -14,29 +14,50 @@
case BlobNotCommitted
case SyncgroupJoinFailed
case BadExecStreamHeader
+ case InvalidPermissionsChange
case InvalidName(name: String)
case CorruptDatabase(path: String)
case InvalidOperation(reason: String)
- case PermissionsSerializationError(permissions: Permissions?)
case InvalidUTF8(invalidUtf8: String)
+ case CastError(obj: Any)
+
+ init?(_ err: VError) {
+ // TODO(zinman): Make VError better by having the proper arguments transmitted across
+ // so we don't have to use err.msg to repeat our messages.
+ switch err.id {
+ case "v.io/v23/services/syncbase.NotInDevMode": self = SyncbaseError.NotInDevMode
+ case "v.io/v23/services/syncbase.InvalidName": self = SyncbaseError.InvalidName(name: err.msg)
+ case "v.io/v23/services/syncbase.CorruptDatabase": self = SyncbaseError.CorruptDatabase(path: err.msg)
+ case "v.io/v23/services/syncbase.UnknownBatch": self = SyncbaseError.UnknownBatch
+ case "v.io/v23/services/syncbase.NotBoundToBatch": self = SyncbaseError.NotBoundToBatch
+ case "v.io/v23/services/syncbase.ReadOnlyBatch": self = SyncbaseError.ReadOnlyBatch
+ case "v.io/v23/services/syncbase.ConcurrentBatch": self = SyncbaseError.ConcurrentBatch
+ case "v.io/v23/services/syncbase.BlobNotCommitted": self = SyncbaseError.BlobNotCommitted
+ case "v.io/v23/services/syncbase.SyncgroupJoinFailed": self = SyncbaseError.SyncgroupJoinFailed
+ case "v.io/v23/services/syncbase.BadExecStreamHeader": self = SyncbaseError.BadExecStreamHeader
+ case "v.io/v23/services/syncbase.InvalidPermissionsChange": self = SyncbaseError.InvalidPermissionsChange
+ default: return nil
+ }
+ }
public var description: String {
switch (self) {
- case .NotAuthorized: return "no valid blessings; create new blessings using oauth"
- case .NotInDevMode: return "not running with --dev=true"
- case .UnknownBatch: return "unknown batch, perhaps the server restarted"
- case .NotBoundToBatch: return "not bound to batch"
- case .ReadOnlyBatch: return "batch is read-only"
- case .ConcurrentBatch: return "concurrent batch"
- case .BlobNotCommitted: return "blob is not yet committed"
- case .SyncgroupJoinFailed: return "syncgroup join failed"
+ case .NotAuthorized: return "No valid blessings; create new blessings using oauth"
+ case .NotInDevMode: return "Not running with --dev=true"
+ case .UnknownBatch: return "Unknown batch, perhaps the server restarted"
+ case .NotBoundToBatch: return "Not bound to batch"
+ case .ReadOnlyBatch: return "Batch is read-only"
+ case .ConcurrentBatch: return "Concurrent batch"
+ case .BlobNotCommitted: return "Blob is not yet committed"
+ case .SyncgroupJoinFailed: return "Syncgroup join failed"
case .BadExecStreamHeader: return "Exec stream header improperly formatted"
- case .InvalidName(let name): return "invalid name: \(name)"
+ case .InvalidPermissionsChange: return "The sequence of permission changes is invalid"
+ case .InvalidName(let name): return "Invalid name: \(name)"
case .CorruptDatabase(let path):
- return "database corrupt, moved to path \(path); client must create a new database"
- case .InvalidOperation(let reason): return "invalid operation: \(reason)"
- case .PermissionsSerializationError(let permissions): return "unable to serialize permissions: \(permissions)"
- case .InvalidUTF8(let invalidUtf8): return "unable to convert to utf8: \(invalidUtf8)"
+ return "Database corrupt, moved to path \(path); client must create a new database"
+ case .InvalidOperation(let reason): return "Invalid operation: \(reason)"
+ case .InvalidUTF8(let invalidUtf8): return "Unable to convert to utf8: \(invalidUtf8)"
+ case .CastError(let obj): return "Unable to convert to cast: \(obj)"
}
}
-}
\ No newline at end of file
+}
diff --git a/SyncbaseCore/Source/Locking.swift b/SyncbaseCore/Source/Locking.swift
new file mode 100644
index 0000000..c73dd61
--- /dev/null
+++ b/SyncbaseCore/Source/Locking.swift
@@ -0,0 +1,17 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+import Foundation
+
+protocol Lockable {
+ func lock(block:()->())
+}
+
+extension Lockable where Self : AnyObject {
+ func lock(block:()->()) {
+ objc_sync_enter(self)
+ block()
+ objc_sync_exit(self)
+ }
+}
\ No newline at end of file
diff --git a/SyncbaseCore/Source/Marshal.swift b/SyncbaseCore/Source/Marshal.swift
index 618b012..f5b67c0 100644
--- a/SyncbaseCore/Source/Marshal.swift
+++ b/SyncbaseCore/Source/Marshal.swift
@@ -47,183 +47,22 @@
}
}
-public enum JsonDataType: Int {
- case Bool = 0,
- Int, Int8, Int16, Int32, Int64,
- UInt, UInt8, UInt16, UInt32, UInt64,
- Float, Double,
- String,
- Array, Dictionary,
- RawJson
+public protocol SyncbaseConvertible {
+ func serializeToSyncbase() throws -> NSData
+ static func deserializeFromSyncbase<T: SyncbaseConvertible>(data: NSData) throws -> T
}
-public protocol SyncbaseJsonConvertible {
- func toSyncbaseJson() throws -> (NSData, JsonDataType)
- static func fromSyncbaseJson<T: SyncbaseJsonConvertible>(data: NSData) throws -> T
-}
-
-extension SyncbaseJsonConvertible {
- public static func fromSyncbaseJson<T: SyncbaseJsonConvertible>(data: NSData) throws -> T {
- let obj = try NSJSONSerialization.deserialize(data)
- var target: T? = nil
- if NSNumber.isNSNumber(obj) {
- let nsnumber = obj as! NSNumber
- if !nsnumber.isTargetCastable(&target) {
- throw JsonErrors.CastError(value: nsnumber, target: target)
- }
- return nsnumber as! T
- }
- if let cast = obj as? T {
+extension SyncbaseConvertible {
+ public static func deserializeFromSyncbase<T: SyncbaseConvertible>(data: NSData) throws -> T {
+ if let cast = data as? T {
return cast
}
- throw JsonErrors.CastError(value: obj, target: target)
+ throw SyncbaseError.CastError(obj: data)
}
}
-extension Bool: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Bool)
- }
-}
-
-// Wish we could put an extension on a protocol like IntegerLiterableConvertable or IntegerType
-// but we can't as of Swift 2.2, so we enumerate all the possiblities
-
-extension Int: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Int)
- }
-}
-
-extension Int8: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(Int(self)), JsonDataType.Int8)
- }
-}
-
-extension Int16: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(Int(self)), JsonDataType.Int16)
- }
-}
-
-extension Int32: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(Int(self)), JsonDataType.Int32)
- }
-}
-
-extension Int64: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(NSNumber(longLong: self)), JsonDataType.Int64)
- }
-}
-
-extension UInt: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.UInt)
- }
-}
-
-extension UInt8: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(UInt(self)), JsonDataType.UInt8)
- }
-}
-
-extension UInt16: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(UInt(self)), JsonDataType.UInt16)
- }
-}
-
-extension UInt32: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(UInt(self)), JsonDataType.UInt32)
- }
-}
-
-extension UInt64: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(NSNumber(unsignedLongLong: self)), JsonDataType.UInt64)
- }
-}
-
-extension Float: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Float)
- }
-}
-
-extension Double: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Double)
- }
-}
-
-extension String: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.String)
- }
-}
-
-// Wish we could do this:
-// extension Array : SyncbaseJsonConvertible where Element: AnyObject {
-// but it's not valid Swift 2.2
-
-extension Array where Element: AnyObject {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try! NSJSONSerialization.dataWithJSONObject(self, options: []), JsonDataType.Array)
- }
-}
-
-extension NSArray: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try! NSJSONSerialization.dataWithJSONObject(self, options: []), JsonDataType.Array)
- }
-}
-
-extension NSDictionary: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (try! NSJSONSerialization.dataWithJSONObject(self, options: []), JsonDataType.Dictionary)
- }
-}
-
-// Annoyingly we can't directly cast an Array that we have no type information on into an AnyObject
-// or similarly the same for Dictionary. So instead we're forced to copy all the elements and test
-// all individual types inside.
-extension Array: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- let copy: [AnyObject] = try self.map { elem in
- guard let jsonable = elem as? AnyObject else {
- throw JsonErrors.ArrayContainsInvalidTypes
- }
- return jsonable
- }
- let data = try NSJSONSerialization.serialize(copy)
- return (data, JsonDataType.Array)
- }
-}
-
-extension Dictionary: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- let copy = NSMutableDictionary()
- try self.forEach { (key, value) in
- guard let strKey = key as? String else {
- throw JsonErrors.DictionaryContainsInvalidTypes
- }
- guard let jsonable = value as? AnyObject else {
- throw JsonErrors.DictionaryContainsInvalidTypes
- }
- copy[strKey] = jsonable
- }
- let data = try NSJSONSerialization.serialize(copy)
- return (data, JsonDataType.Dictionary)
- }
-}
-
-extension NSData: SyncbaseJsonConvertible {
- public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
- return (self, JsonDataType.RawJson)
+extension NSData: SyncbaseConvertible {
+ public func serializeToSyncbase() throws -> NSData {
+ return self
}
}
diff --git a/SyncbaseCore/Source/Permissions.swift b/SyncbaseCore/Source/Permissions.swift
index beb1e34..1553bb7 100644
--- a/SyncbaseCore/Source/Permissions.swift
+++ b/SyncbaseCore/Source/Permissions.swift
@@ -55,14 +55,23 @@
func toJsonable() -> [String: AnyObject] {
return ["In": allowed, "NotIn": notAllowed]
}
+
+ static func fromJsonable(jsonable: [String: AnyObject]) -> AccessList? {
+ guard let castIn = jsonable["In"] as? [String],
+ castNotIn = jsonable["NotIn"] as? [String] else {
+ return nil
+ }
+ return AccessList(
+ allowed: castIn as [BlessingPattern],
+ notAllowed: castNotIn as [BlessingPattern])
+ }
}
/// AccessController provides access control for various syncbase objects.
public protocol AccessController {
+ /// getPermissions returns the current Permissions for an object.
+ func getPermissions() throws -> (Permissions, PermissionsVersion)
+
/// setPermissions replaces the current Permissions for an object.
func setPermissions(perms: Permissions, version: PermissionsVersion) throws
-
- /// GetPermissions returns the current Permissions for an object.
- /// For detailed documentation, see Object.GetPermissions.
- func getPermissions() throws -> (Permissions, PermissionsVersion)
}
diff --git a/SyncbaseCore/Source/Refs.swift b/SyncbaseCore/Source/Refs.swift
new file mode 100644
index 0000000..585fddb
--- /dev/null
+++ b/SyncbaseCore/Source/Refs.swift
@@ -0,0 +1,35 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/// RefMap holds onto objs that get referenced by AsyncId between Go and Swift.
+/// The larger motivation is closures that get passed as function pointers have to be 'context-free'
+/// so we can't closure on a reference to a given future. By allowing the end user to hold onto
+/// this in various places (strongly typed to the appropriate T) then we can pass a handle
+/// back and forth safely.
+typealias AsyncId = Int32
+
+class RefMap<T>: Lockable {
+ private (set) var lastId: AsyncId = 0
+ private var refs = [AsyncId: T]()
+
+ /// Stores an object and returns the associated asyncId. If the object is already in the map
+ /// it will be stored twice -- ref does not actually perform reference counting.
+ func ref(obj: T) -> AsyncId {
+ let asyncId = OSAtomicIncrement32(&lastId)
+ lock { self.refs[asyncId] = obj }
+ return asyncId
+ }
+
+ /// Gets the associated value for a given asyncId.
+ func get(asyncId: AsyncId) -> T? {
+ return refs[asyncId]
+ }
+
+ /// Get and deletes any associated asyncId, returning the associated value.
+ func unref(asyncId: AsyncId) -> T? {
+ guard let p = refs[asyncId] else { return nil }
+ lock { self.refs[asyncId] = nil }
+ return p
+ }
+}
\ No newline at end of file
diff --git a/SyncbaseCore/Source/RowRange.swift b/SyncbaseCore/Source/RowRange.swift
index 39dd257..209a6da 100644
--- a/SyncbaseCore/Source/RowRange.swift
+++ b/SyncbaseCore/Source/RowRange.swift
@@ -12,15 +12,27 @@
var limit: String { get }
}
-/// StandardRowRange represents all rows with keys in [start, limit).
+/// RowRangeAll represents all rows.
+public struct RowRangeAll: RowRange {
+ public var start: String {
+ return ""
+ }
+
+ public var limit: String {
+ return ""
+ }
+}
+
+/// RowRangeStandard represents all rows with keys in [start, limit).
/// If limit is "", all rows with keys >= start are included.
-public struct StandardRowRange: RowRange {
+public struct RowRangeStandard: RowRange {
public let start: String
/// If limit is "", all rows with keys >= start are included.
public let limit: String
}
-public struct SingleRow: RowRange {
+/// RowRangeSingleRow represents a single row with an explicit key.
+public struct RowRangeSingleRow: RowRange {
public let row: String
public var start: String {
@@ -32,8 +44,8 @@
}
}
-/// PrefixRange represents all rows with keys that have some prefix.
-public struct PrefixRange: RowRange {
+/// RowRangePrefix represents all rows with keys that have some prefix.
+public struct RowRangePrefix: RowRange {
public let prefix: String
/// Returns the start of the row range for the given prefix.
diff --git a/SyncbaseCore/Source/Stream.swift b/SyncbaseCore/Source/Stream.swift
index 544b3b0..ebf71cb 100644
--- a/SyncbaseCore/Source/Stream.swift
+++ b/SyncbaseCore/Source/Stream.swift
@@ -5,10 +5,10 @@
import Foundation
/// Base protocol for iterating through elements of unknown length.
-public protocol Stream: GeneratorType {
+public protocol Stream: SequenceType, GeneratorType {
/// Err returns a non-nil error iff the stream encountered any errors. Err does
/// not block.
- func err() -> SyncbaseError?
+ func err() -> ErrorType?
/// Cancel notifies the stream provider that it can stop producing elements.
/// The client must call Cancel if it does not iterate through all elements
@@ -19,14 +19,14 @@
}
/// Typed-stream backed by anonymous callbacks
-public struct AnonymousStream<T>: Stream {
+public class AnonymousStream<T>: Stream {
public typealias Element = T
- public typealias FetchNextFunction = Void -> (T?, SyncbaseError?)
+ public typealias FetchNextFunction = Void -> (T?, ErrorType?)
let fetchNextFunction: FetchNextFunction
public typealias CancelFunction = Void -> Void
let cancelFunction: CancelFunction
- private var lastErr: SyncbaseError?
- private var isCancelled: Bool = false
+ private var lastErr: ErrorType?
+ private var isDone: Bool = false
init(fetchNextFunction: FetchNextFunction, cancelFunction: CancelFunction) {
self.fetchNextFunction = fetchNextFunction
self.cancelFunction = cancelFunction
@@ -40,8 +40,8 @@
/// has returned `nil`. Specific implementations of this protocol
/// are encouraged to respond to violations of this requirement by
/// calling `preconditionFailure("...")`.
- public mutating func next() -> T? {
- guard !isCancelled else {
+ public func next() -> T? {
+ guard !isDone else {
return nil
}
let (result, err) = fetchNextFunction()
@@ -49,12 +49,13 @@
return ret
}
lastErr = err
+ isDone = true
return nil
}
/// Err returns a non-nil error iff the stream encountered any errors. Err does
/// not block.
- public func err() -> SyncbaseError? {
+ public func err() -> ErrorType? {
return lastErr
}
@@ -63,10 +64,10 @@
/// (i.e. until Advance returns false). Cancel is idempotent and can be called
/// concurrently with a goroutine that is iterating via Advance.
/// Cancel causes Advance to subsequently return false. Cancel does not block.
- public mutating func cancel() {
- if !isCancelled {
+ public func cancel() {
+ if !isDone {
cancelFunction()
- isCancelled = true
+ isDone = true
}
}
}
diff --git a/SyncbaseCore/Source/Syncbase.swift b/SyncbaseCore/Source/Syncbase.swift
index 877a994..9ffe8bd 100644
--- a/SyncbaseCore/Source/Syncbase.swift
+++ b/SyncbaseCore/Source/Syncbase.swift
@@ -15,7 +15,7 @@
/// Private constructor -- because this class is a singleton it should only be called once
/// and by the static instance method.
private init() {
- v23_syncbase_Init()
+ v23_syncbase_Init(v23_syncbase_Bool(false))
}
/// Create a database using the relative name and user's blessings.
@@ -25,20 +25,17 @@
/// DatabaseForId returns the Database with the given app blessing and name (from the Id struct).
public func database(databaseId: Identifier) throws -> Database {
- guard let db = Database(databaseId: databaseId, batchHandle: nil) else {
- throw SyncbaseError.InvalidUTF8(invalidUtf8: "\(databaseId)")
- }
- return db
+ return try Database(databaseId: databaseId, batchHandle: nil)
}
/// ListDatabases returns a list of all Database ids that the caller is allowed to see.
/// The list is sorted by blessing, then by name.
public func listDatabases() throws -> [Identifier] {
var ids = v23_syncbase_Ids()
- return try VError.maybeThrow({ err in
+ return try VError.maybeThrow { err in
v23_syncbase_ServiceListDatabases(&ids, err)
return ids.toIdentifiers()
- })
+ }
}
/// Must return true before any Syncbase operation can work. Authorize using GoogleCredentials
@@ -81,14 +78,26 @@
}
extension Syncbase: AccessController {
- /// setPermissions replaces the current Permissions for an object.
- public func setPermissions(perms: Permissions, version: PermissionsVersion) throws {
- preconditionFailure("Implement me")
+ public func getPermissions() throws -> (Permissions, PermissionsVersion) {
+ var cPermissions = v23_syncbase_Permissions()
+ var cVersion = v23_syncbase_String()
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_ServiceGetPermissions(
+ &cPermissions,
+ &cVersion,
+ errPtr)
+ }
+ // TODO(zinman): Verify that permissions defaulting to zero-value is correct for Permissions.
+ // We force cast of cVersion because we know it can be UTF8 converted.
+ return (try cPermissions.toPermissions() ?? Permissions(), cVersion.toString()!)
}
- /// getPermissions returns the current Permissions for an object.
- /// For detailed documentation, see Object.GetPermissions.
- public func getPermissions() throws -> (Permissions, PermissionsVersion) {
- preconditionFailure("Implement me")
+ public func setPermissions(permissions: Permissions, version: PermissionsVersion) throws {
+ try VError.maybeThrow { errPtr in
+ v23_syncbase_ServiceSetPermissions(
+ try v23_syncbase_Permissions(permissions),
+ try version.toCgoString(),
+ errPtr)
+ }
}
}
diff --git a/SyncbaseCore/Source/Watch.swift b/SyncbaseCore/Source/Watch.swift
index 2d5cb03..80b1c9c 100644
--- a/SyncbaseCore/Source/Watch.swift
+++ b/SyncbaseCore/Source/Watch.swift
@@ -5,7 +5,7 @@
import Foundation
public struct ResumeMarker {
- let data: [UInt8]
+ let data: NSData
}
public struct WatchChange {
diff --git a/SyncbaseCore/Source/util/NSNumber.swift b/SyncbaseCore/Source/util/NSNumber.swift
deleted file mode 100644
index 7a1ec26..0000000
--- a/SyncbaseCore/Source/util/NSNumber.swift
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-import Foundation
-import CoreGraphics
-
-extension NSNumber {
- /// Returns true if value can be cast to NSNumber or NSNumber?
- static func isNSNumber<V>(value: V) -> Bool {
- // Can't do this:
- // if value is NSNumber {
- // because it will always succeed through type coercion, even if value is an Int.
- // Instead we use the dynamic (runtime) type
- let type = value.dynamicType
- return type is NSNumber.Type || type is NSNumber?.Type
- }
-
- /// Returns true if target can be set/cast from this NSNumber without precision-loss or type
- /// conversion. For example ```NSNumber.init(bool: true) as Float``` will return a Swift Float of
- /// value 1, where as this function would return false as the types are unrelated -- only
- /// casting as Bool would return true. This function checks for 32/64-bit correctness with types.
- ///
- /// Caveat: Currently this function makes no attempt to determine signed/unsigned correctness of
- /// the underlying data, although this is sometimes knowable with NSNumber.objCType.
- func isTargetCastable<T>(inout target: T?) -> Bool {
- // Allow matching types of this size, and bigger. Signed and unsigned of same size are not allowed.
- // Swift: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
- // C types: https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/Major64-BitChanges/Major64-BitChanges.html
- switch CFNumberGetType(self as CFNumberRef) {
- // Obj-C bool is stored as Char, but if it's a @(YES) or @(NO) they are all a shared instance.
- case .CharType where (unsafeAddressOf(self) == unsafeAddressOf(kCFBooleanFalse) ||
- unsafeAddressOf(self) == unsafeAddressOf(kCFBooleanTrue)):
- // We know we have a bool... make sure it's compatible
- let type = target.dynamicType
- guard type == Bool?.self || type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- // Handle fixed lengths on 32/64 bit
- case .SInt8Type, .CharType:
- let type = target.dynamicType
- guard type == Int?.self || type == UInt?.self ||
- type == Int8?.self || type == Int16?.self || type == Int32?.self || type == Int64?.self ||
- type == UInt8?.self || type == UInt16?.self || type == UInt32?.self || type == UInt64?.self ||
- type == CChar?.self || type == CShort?.self || type == CInt?.self || type == CLong?.self || type == CLongLong?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- case .SInt16Type, .ShortType:
- let type = target.dynamicType
- guard type == Int?.self || type == UInt?.self ||
- type == Int16?.self || type == Int32?.self || type == Int64?.self ||
- type == UInt16?.self || type == UInt32?.self || type == UInt64?.self ||
- type == CShort?.self || type == CInt?.self || type == CLong?.self || type == CLongLong?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- case .SInt32Type, .IntType:
- let type = target.dynamicType
- guard type == Int?.self || type == UInt?.self ||
- type == Int32?.self || type == Int64?.self ||
- type == UInt32?.self || type == UInt64?.self ||
- type == CInt?.self || type == CLong?.self || type == CLongLong?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- case .SInt64Type, .LongLongType:
- let type = target.dynamicType
- guard (type == Int?.self && sizeof(Int) == sizeof(CLongLong)) ||
- (type == UInt?.self && sizeof(UInt) == sizeof(CLongLong)) ||
- type == Int64?.self || type == UInt64?.self ||
- type == CLongLong?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- case .Float32Type, .FloatType:
- let type = target.dynamicType
- guard type == Float?.self ||
- type == Double?.self ||
- type == Float32?.self ||
- type == Float64?.self ||
- type == CFloat?.self || type == CDouble?.self || type == CGFloat?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- case .Float64Type, .DoubleType: /* 64-bit IEEE 754 */
- let type = target.dynamicType
- guard type == Double?.self ||
- type == Float64?.self ||
- type == CDouble?.self || type == CGFloat?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
-
- // Handle 32/64-bit types
- case .LongType, .NSIntegerType:
- let type = target.dynamicType
- guard type == Int?.self || type == UInt?.self ||
- (type == Int32?.self && sizeof(Int32) == sizeof(NSInteger)) ||
- (type == UInt32?.self && sizeof(UInt32) == sizeof(NSInteger)) ||
- type == Int64?.self || type == UInt64?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self ||
- type == CLong?.self || type == CLongLong?.self else {
- return false
- }
- case .CGFloatType:
- let type = target.dynamicType
- guard (type == Float?.self && sizeof(CGFloat) == sizeof(Float)) ||
- type == Double?.self ||
- (type == Float32?.self && sizeof(CGFloat) == sizeof(Float32)) ||
- type == Float64?.self ||
- type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
- return false
- }
- // Misc
- case .CFIndexType:
- guard target.dynamicType == CFIndex?.self else {
- return false
- }
- }
- return true
- }
-}
diff --git a/SyncbaseCore/Source/util/Types.swift b/SyncbaseCore/Source/util/Types.swift
index 67e5e11..eea44c3 100644
--- a/SyncbaseCore/Source/util/Types.swift
+++ b/SyncbaseCore/Source/util/Types.swift
@@ -7,6 +7,18 @@
import Foundation
+extension v23_syncbase_BatchOptions {
+ init (_ opts: BatchOptions?) throws {
+ guard let o = opts else {
+ self.hint = v23_syncbase_String()
+ self.readOnly = false
+ return
+ }
+ self.hint = try o.hint?.toCgoString() ?? v23_syncbase_String()
+ self.readOnly = o.readOnly
+ }
+}
+
extension v23_syncbase_Bool {
init(_ bool: Bool) {
switch bool {
@@ -23,6 +35,97 @@
}
}
+extension v23_syncbase_Bytes {
+ init(_ data: NSData) {
+ let p = malloc(data.length)
+ if p == nil {
+ fatalError("Couldn't allocate \(data.length) bytes")
+ }
+ let n = data.length
+ data.getBytes(p, length: n)
+ self.p = UnsafeMutablePointer<UInt8>(p)
+ self.n = Int32(n)
+ }
+
+ // Return value takes ownership of the memory associated with this object.
+ func toNSData() -> NSData? {
+ if p == nil {
+ return nil
+ }
+ return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(p), length: Int(n), freeWhenDone: true)
+ }
+}
+
+extension v23_syncbase_Id {
+ init?(_ id: Identifier) {
+ do {
+ self.name = try id.name.toCgoString()
+ self.blessing = try id.blessing.toCgoString()
+ } catch (let e) {
+ log.warning("Unable to UTF8-encode id: \(e)")
+ return nil
+ }
+ }
+
+ func toIdentifier() -> Identifier? {
+ guard let name = name.toString(),
+ blessing = blessing.toString() else {
+ return nil
+ }
+ return Identifier(name: name, blessing: blessing)
+ }
+}
+
+extension v23_syncbase_Ids {
+ func toIdentifiers() -> [Identifier] {
+ var ids: [Identifier] = []
+ for i in 0 ..< n {
+ let idStruct = p.advancedBy(Int(i)).memory
+ if let id = idStruct.toIdentifier() {
+ ids.append(id)
+ }
+ }
+ if p != nil {
+ free(p)
+ }
+ return ids
+ }
+}
+
+extension v23_syncbase_Permissions {
+ init(_ permissions: Permissions?) throws {
+ guard let p = permissions where !p.isEmpty else {
+ // Zero-value constructor.
+ self.json = v23_syncbase_Bytes()
+ return
+ }
+ var m = [String: AnyObject]()
+ for (key, value) in p {
+ m[key as String] = (value as AccessList).toJsonable()
+ }
+ let serialized = try NSJSONSerialization.serialize(m)
+ let bytes = v23_syncbase_Bytes(serialized)
+ self.json = bytes
+ }
+
+ func toPermissions() throws -> Permissions? {
+ guard let data = self.json.toNSData(),
+ map = try NSJSONSerialization.deserialize(data) as? NSDictionary else {
+ return nil
+ }
+ var p = Permissions()
+ for (k, v) in map {
+ guard let key = k as? String,
+ jsonAcessList = v as? [String: AnyObject],
+ accessList = AccessList.fromJsonable(jsonAcessList) else {
+ throw SyncbaseError.CastError(obj: v)
+ }
+ p[key] = accessList
+ }
+ return p
+ }
+}
+
extension v23_syncbase_String {
init?(_ string: String) {
// TODO: If possible, make one copy instead of two, e.g. using s.getCString.
@@ -111,27 +214,6 @@
}
}
-extension v23_syncbase_Bytes {
- init(_ data: NSData) {
- let p = malloc(data.length)
- if p == nil {
- fatalError("Couldn't allocate \(data.length) bytes")
- }
- let n = data.length
- data.getBytes(p, length: n)
- self.p = UnsafeMutablePointer<UInt8>(p)
- self.n = Int32(n)
- }
-
- // Return value takes ownership of the memory associated with this object.
- func toNSData() -> NSData? {
- if p == nil {
- return nil
- }
- return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(p), length: Int(n), freeWhenDone: true)
- }
-}
-
// Note, we don't define init?(VError) since we never pass Swift VError objects to Go.
extension v23_syncbase_VError {
// Return value takes ownership of the memory associated with this object.
@@ -142,7 +224,7 @@
// Take ownership of all memory before checking optionals.
let vId = id.toString(), vMsg = msg.toString(), vStack = stack.toString()
// TODO: Stop requiring id, msg, and stack to be valid UTF8?
- return VError(id: vId!, actionCode: actionCode, msg: vMsg!, stack: vStack!)
+ return VError(id: vId!, actionCode: actionCode, msg: vMsg ?? "", stack: vStack!)
}
}
@@ -156,66 +238,13 @@
var e = v23_syncbase_VError()
let res = try f(&e)
if let err = e.toVError() {
+ // We might be able to convert this VError into a SyncbaseError depending on the ID.
+ if let syncbaseError = SyncbaseError(err) {
+ throw syncbaseError
+ }
throw err
}
return res
}
}
-extension v23_syncbase_Ids {
- func toIdentifiers() -> [Identifier] {
- var ids: [Identifier] = []
- for i in 0 ..< n {
- let idStruct = p.advancedBy(Int(i)).memory
- if let id = idStruct.toIdentifier() {
- ids.append(id)
- }
- }
- if p != nil {
- free(p)
- }
- return ids
- }
-}
-
-extension v23_syncbase_Id {
- init?(_ id: Identifier) {
- do {
- self.name = try id.name.toCgoString()
- self.blessing = try id.blessing.toCgoString()
- } catch (let e) {
- log.warning("Unable to UTF8-encode id: \(e)")
- return nil
- }
- }
-
- func toIdentifier() -> Identifier? {
- guard let name = name.toString(),
- let blessing = blessing.toString() else {
- return nil
- }
- return Identifier(name: name, blessing: blessing)
- }
-}
-
-extension v23_syncbase_Permissions {
- init?(_ permissions: Permissions?) {
- guard let p = permissions where !p.isEmpty else {
- // Zero-value constructor.
- self.json = v23_syncbase_Bytes()
- return
- }
- var m = [String: AnyObject]()
- for (key, value) in p {
- m[key as String] = (value as AccessList).toJsonable()
- }
- do {
- let serialized = try NSJSONSerialization.serialize(m)
- let bytes = v23_syncbase_Bytes(serialized)
- self.json = bytes
- } catch {
- log.warning("Unable to serialize permissions: \(permissions)")
- return nil
- }
- }
-}
diff --git a/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj b/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
index 0026a4f..7407901 100644
--- a/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
+++ b/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
@@ -25,14 +25,11 @@
30AD2E401CDD508700A28A0C /* Syncgroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */; };
30AD2E441CDD508700A28A0C /* Vom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2E1CDD508700A28A0C /* Vom.swift */; };
30AD2E451CDD508700A28A0C /* Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2F1CDD508700A28A0C /* Watch.swift */; };
- 30AD2E4D1CDD508D00A28A0C /* JSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E491CDD508D00A28A0C /* JSONTests.swift */; };
- 30AD2E4E1CDD508D00A28A0C /* NSNumberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E4A1CDD508D00A28A0C /* NSNumberTests.swift */; };
30AD2E801CDD569200A28A0C /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E791CDD569200A28A0C /* Logging.swift */; };
30AD2E811CDD569200A28A0C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E7A1CDD569200A28A0C /* Strings.swift */; };
30AD2E821CDD569200A28A0C /* Threads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E7B1CDD569200A28A0C /* Threads.swift */; };
30AD2E891CDD56B700A28A0C /* Exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30AD2E861CDD56B700A28A0C /* Exceptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
30AD2E8A1CDD56B700A28A0C /* Exceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E871CDD56B700A28A0C /* Exceptions.m */; };
- 30AD2E8B1CDD56B700A28A0C /* NSNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E881CDD56B700A28A0C /* NSNumber.swift */; };
30AD2E911CDD593000A28A0C /* Principal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E901CDD593000A28A0C /* Principal.swift */; };
30AD2E931CDD60A600A28A0C /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E921CDD60A600A28A0C /* Types.swift */; };
30BCD5D01CEFA32900C76DA2 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30BCD5CF1CEFA32900C76DA2 /* CoreBluetooth.framework */; };
@@ -40,6 +37,8 @@
30D45FCA1CE14A8A004A59FA /* go_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D45FC51CE14A8A004A59FA /* go_types.h */; settings = {ATTRIBUTES = (Public, ); }; };
30D45FCD1CE14A8A004A59FA /* sbcore_arm64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 30D45FC81CE14A8A004A59FA /* sbcore_arm64.a */; };
930DFCE21CEE46DE00738DB8 /* OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930DFCE11CEE46DE00738DB8 /* OAuth.swift */; };
+ 930DFCEB1CEED8BD00738DB8 /* Refs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930DFCEA1CEED8BD00738DB8 /* Refs.swift */; };
+ 930DFCF51CEED96B00738DB8 /* Locking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930DFCF41CEED96B00738DB8 /* Locking.swift */; };
9351A4941CE46DB9009CC4F4 /* sbcore_amd64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9351A4931CE46DB9009CC4F4 /* sbcore_amd64.a */; };
93D3AD5C1CE4392A00A80CDA /* libleveldb_amd64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D3AD581CE4392A00A80CDA /* libleveldb_amd64.a */; };
93D3AD5D1CE4392A00A80CDA /* libleveldb_arm64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D3AD591CE4392A00A80CDA /* libleveldb_arm64.a */; };
@@ -79,14 +78,11 @@
30AD2E2E1CDD508700A28A0C /* Vom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vom.swift; sourceTree = "<group>"; };
30AD2E2F1CDD508700A28A0C /* Watch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Watch.swift; sourceTree = "<group>"; };
30AD2E481CDD508D00A28A0C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
- 30AD2E491CDD508D00A28A0C /* JSONTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONTests.swift; sourceTree = "<group>"; };
- 30AD2E4A1CDD508D00A28A0C /* NSNumberTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSNumberTests.swift; sourceTree = "<group>"; };
30AD2E791CDD569200A28A0C /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Logging.swift; path = util/Logging.swift; sourceTree = "<group>"; };
30AD2E7A1CDD569200A28A0C /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Strings.swift; path = util/Strings.swift; sourceTree = "<group>"; };
30AD2E7B1CDD569200A28A0C /* Threads.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Threads.swift; path = util/Threads.swift; sourceTree = "<group>"; };
30AD2E861CDD56B700A28A0C /* Exceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exceptions.h; path = util/Exceptions.h; sourceTree = "<group>"; };
30AD2E871CDD56B700A28A0C /* Exceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Exceptions.m; path = util/Exceptions.m; sourceTree = "<group>"; };
- 30AD2E881CDD56B700A28A0C /* NSNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSNumber.swift; path = util/NSNumber.swift; sourceTree = "<group>"; };
30AD2E901CDD593000A28A0C /* Principal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Principal.swift; sourceTree = "<group>"; };
30AD2E921CDD60A600A28A0C /* Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Types.swift; path = util/Types.swift; sourceTree = "<group>"; };
30BCD5CF1CEFA32900C76DA2 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; };
@@ -94,6 +90,8 @@
30D45FC51CE14A8A004A59FA /* go_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = go_types.h; sourceTree = "<group>"; };
30D45FC81CE14A8A004A59FA /* sbcore_arm64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = sbcore_arm64.a; sourceTree = "<group>"; };
930DFCE11CEE46DE00738DB8 /* OAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth.swift; sourceTree = "<group>"; };
+ 930DFCEA1CEED8BD00738DB8 /* Refs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Refs.swift; sourceTree = "<group>"; };
+ 930DFCF41CEED96B00738DB8 /* Locking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locking.swift; sourceTree = "<group>"; };
9351A4931CE46DB9009CC4F4 /* sbcore_amd64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = sbcore_amd64.a; sourceTree = "<group>"; };
93D3AD581CE4392A00A80CDA /* libleveldb_amd64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libleveldb_amd64.a; sourceTree = "<group>"; };
93D3AD591CE4392A00A80CDA /* libleveldb_arm64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libleveldb_arm64.a; sourceTree = "<group>"; };
@@ -150,13 +148,14 @@
30AD2E191CDD508700A28A0C /* Source */ = {
isa = PBXGroup;
children = (
+ 30AD2E281CDD508700A28A0C /* SyncbaseCore.h */,
+ 30AD2E211CDD508700A28A0C /* Info.plist */,
30AD2E1B1CDD508700A28A0C /* Batch.swift */,
30AD2E1C1CDD508700A28A0C /* Blob.swift */,
30AD2E1D1CDD508700A28A0C /* Collection.swift */,
30AD2E1E1CDD508700A28A0C /* Database.swift */,
30AD2E1F1CDD508700A28A0C /* Errors.swift */,
30AD2E201CDD508700A28A0C /* Identifier.swift */,
- 30AD2E211CDD508700A28A0C /* Info.plist */,
30AD2E221CDD508700A28A0C /* Marshal.swift */,
930DFCE11CEE46DE00738DB8 /* OAuth.swift */,
30AD2E231CDD508700A28A0C /* Permissions.swift */,
@@ -165,11 +164,10 @@
30AD2E261CDD508700A28A0C /* Service.swift */,
30AD2E271CDD508700A28A0C /* Stream.swift */,
30AD2E291CDD508700A28A0C /* Syncbase.swift */,
- 30AD2E281CDD508700A28A0C /* SyncbaseCore.h */,
30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */,
- 30AD2E771CDD567D00A28A0C /* Util */,
30AD2E2E1CDD508700A28A0C /* Vom.swift */,
30AD2E2F1CDD508700A28A0C /* Watch.swift */,
+ 30AD2E771CDD567D00A28A0C /* Util */,
);
path = Source;
sourceTree = "<group>";
@@ -178,8 +176,6 @@
isa = PBXGroup;
children = (
30AD2E481CDD508D00A28A0C /* Info.plist */,
- 30AD2E491CDD508D00A28A0C /* JSONTests.swift */,
- 30AD2E4A1CDD508D00A28A0C /* NSNumberTests.swift */,
30A1F52D1CE68465008FC205 /* BasicDatabaseTests.swift */,
);
path = Tests;
@@ -205,8 +201,9 @@
children = (
30AD2E861CDD56B700A28A0C /* Exceptions.h */,
30AD2E871CDD56B700A28A0C /* Exceptions.m */,
- 30AD2E881CDD56B700A28A0C /* NSNumber.swift */,
+ 930DFCF41CEED96B00738DB8 /* Locking.swift */,
30AD2E791CDD569200A28A0C /* Logging.swift */,
+ 930DFCEA1CEED8BD00738DB8 /* Refs.swift */,
30AD2E7A1CDD569200A28A0C /* Strings.swift */,
30AD2E7B1CDD569200A28A0C /* Threads.swift */,
30AD2E921CDD60A600A28A0C /* Types.swift */,
@@ -342,9 +339,10 @@
30AD2E3F1CDD508700A28A0C /* Syncbase.swift in Sources */,
30AD2E401CDD508700A28A0C /* Syncgroup.swift in Sources */,
30AD2E811CDD569200A28A0C /* Strings.swift in Sources */,
- 30AD2E8B1CDD56B700A28A0C /* NSNumber.swift in Sources */,
+ 930DFCF51CEED96B00738DB8 /* Locking.swift in Sources */,
30AD2E451CDD508700A28A0C /* Watch.swift in Sources */,
30AD2E821CDD569200A28A0C /* Threads.swift in Sources */,
+ 930DFCEB1CEED8BD00738DB8 /* Refs.swift in Sources */,
30AD2E801CDD569200A28A0C /* Logging.swift in Sources */,
30AD2E351CDD508700A28A0C /* Errors.swift in Sources */,
930DFCE21CEE46DE00738DB8 /* OAuth.swift in Sources */,
@@ -364,8 +362,6 @@
buildActionMask = 2147483647;
files = (
30A1F52E1CE68465008FC205 /* BasicDatabaseTests.swift in Sources */,
- 30AD2E4D1CDD508D00A28A0C /* JSONTests.swift in Sources */,
- 30AD2E4E1CDD508D00A28A0C /* NSNumberTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/SyncbaseCore/Tests/BasicDatabaseTests.swift b/SyncbaseCore/Tests/BasicDatabaseTests.swift
index 095c9fb..87a5626 100644
--- a/SyncbaseCore/Tests/BasicDatabaseTests.swift
+++ b/SyncbaseCore/Tests/BasicDatabaseTests.swift
@@ -11,19 +11,38 @@
// MARK: Basic test helpers
func withTestDb(runBlock: Database throws -> Void) {
- do {
- let db = try Syncbase.instance.database("test1")
- print("Got db \(db)")
- XCTAssertFalse(try db.exists(), "Database shouldn't exist before being created")
- print("Creating db \(db)")
- try db.create(nil)
- XCTAssertTrue(try db.exists(), "Database should exist after being created")
-
+ withTestDbAsync { (db, cleanup) in
+ defer { cleanup() }
try runBlock(db)
+ }
+ }
- print("Destroying db \(db)")
- try db.destroy()
- XCTAssertFalse(try db.exists(), "Database shouldn't exist after being destroyed")
+ func withTestDbAsync(runBlock: (db: Database, cleanup: Void -> Void) throws -> Void) {
+ do {
+ // Randomize the name to prevent conflicts between tests
+ let dbName = "test\(NSUUID().UUIDString)".stringByReplacingOccurrencesOfString("-", withString: "")
+ let db = try Syncbase.instance.database(dbName)
+ let cleanup = {
+ do {
+ print("Destroying db \(db)")
+ try db.destroy()
+ XCTAssertFalse(try db.exists(), "Database shouldn't exist after being destroyed")
+ } catch let e {
+ log.warning("Unable to delete db: \(e)")
+ }
+ }
+ do {
+ print("Got db \(db)")
+ XCTAssertFalse(try db.exists(), "Database shouldn't exist before being created")
+ print("Creating db \(db)")
+ try db.create(nil)
+ XCTAssertTrue(try db.exists(), "Database should exist after being created")
+ // Always delete the db at the end to prevent conflicts between tests
+ try runBlock(db: db, cleanup: cleanup)
+ } catch let e {
+ XCTFail("Got unexpected exception: \(e)")
+ cleanup()
+ }
} catch (let e) {
XCTFail("Got unexpected exception: \(e)")
}
@@ -50,8 +69,10 @@
func testListDatabases() {
withTestDb { db in
let databases = try Syncbase.instance.listDatabases()
- XCTAssertEqual(databases.count, 1)
- XCTAssertTrue(databases.first! == db.databaseId)
+ XCTAssertFalse(databases.isEmpty)
+ if !databases.isEmpty {
+ XCTAssertTrue(databases.first! == db.databaseId)
+ }
}
}
@@ -67,11 +88,11 @@
}
}
- // MARK: Test getting/putting data
+ // MARK: Test getting/putting/deleting data
- class func testGetPutRow<T: SyncbaseJsonConvertible where T: Equatable>(collection: Collection, key: String, targetValue: T, equals: ((T, T) -> Bool)? = nil) throws {
+ class func testGetPutRow<T: SyncbaseConvertible where T: Equatable>(collection: Collection, key: String, targetValue: T, equals: ((T, T) -> Bool)? = nil) throws {
var value: T? = try collection.get(key)
- XCTAssertNil(value, "Row shouldn't exist yet")
+ XCTAssertNil(value, "Row shouldn't exist yet; yet it has value \(value)")
try collection.put(key, value: targetValue)
value = try collection.get(key)
if let eq = equals {
@@ -84,73 +105,219 @@
XCTAssertNil(value, "Deleted row shouldn't exist")
}
- func testPrimitivesGetPut() {
+ func testRawBytesGetPut() {
withTestCollection { db, collection in
let key = "testrow"
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: true)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: false)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int8.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int16.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int32.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int64.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt8.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt16.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt32.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt64.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt.max)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Float(M_PI), equals: BasicDatabaseTests.floatEq)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: M_PI, equals: BasicDatabaseTests.doubleEq)
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: "oh hai")
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: "你好,世界 👠💪🏿")
- }
- }
-
- func testNonMixedArrayGetPut() {
- let key = "testrow"
- withTestCollection { db, collection in
- try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: [])
- try BasicDatabaseTests.testGetPutRow(collection, key: key,
- targetValue: [UInt.max, UInt.min])
- try BasicDatabaseTests.testGetPutRow(collection, key: key,
- targetValue: [Int.max, Int.min])
- try BasicDatabaseTests.testGetPutRow(collection, key: key,
- targetValue: ["oh hai", "你好,世界 👠💪🏿"])
- }
- }
-
- func testMixedArrayGetPut() {
- let key = "testrow"
- withTestCollection { db, collection in
- try BasicDatabaseTests.testGetPutRow(collection, key: key,
- targetValue: [15, UInt.max, Int.min])
- try BasicDatabaseTests.testGetPutRow(collection, key: key,
- targetValue: [false, 3939, "你好,世界 👠💪🏿"])
- }
- }
-
- func testIntGetPut() {
- withTestCollection { db, collection in
- let key = "testrow"
- let targetValue = 283783
- var value: Int? = try collection.get(key)
- XCTAssertNil(value, "Row shouldn't exist yet")
- try collection.put(key, value: targetValue)
- value = try collection.get(key)
- XCTAssertEqual(value!, targetValue, "Value should be defined and \(targetValue)")
try collection.delete(key)
- value = try collection.get(key)
- XCTAssertNil(value, "Deleted row shouldn't exist")
+ try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSData())
+ try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSJSONSerialization.hackSerializeAnyObject(false))
+ try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSJSONSerialization.hackSerializeAnyObject(M_PI))
+ try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSJSONSerialization.hackSerializeAnyObject("你好,世界 👠💪🏿"))
+ try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: "\0\0\0".dataUsingEncoding(NSUTF8StringEncoding)!)
+ if let p = NSBundle.mainBundle().executablePath,
+ data = NSFileManager.defaultManager().contentsAtPath(p) {
+ try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: data)
+ }
}
}
- // MARK: Helpers
+ func testDeleteRange() {
+ withTestCollection { db, collection in
+ // Generate some test data (1 to 4096 in hex)
+ var data = [String: NSData]()
+ for i in 1...4096 {
+ let key = NSString(format: "%x", i) as String
+ let value = key.dataUsingEncoding(NSUTF8StringEncoding)!
+ data[key] = value
+ try collection.put(key, value: value)
+ }
- class func floatEq(lhs: Float, rhs: Float) -> Bool {
- return fabs(lhs - rhs) <= FLT_EPSILON
+ // Delete single row
+ try collection.deleteRange(RowRangeSingleRow(row: "9"))
+ let value: NSData? = try collection.get("9")
+ XCTAssertNil(value)
+
+ // Delete a*
+ try collection.deleteRange(RowRangePrefix(prefix: "a"))
+ var stream = try collection.scan(RowRangePrefix(prefix: "a"))
+ XCTAssertNil(stream.next())
+
+ // Delete b-bc
+ try collection.deleteRange(RowRangeStandard(start: "b", limit: "bc"))
+ // Get all the keys including bc and after
+ var keys = Array(data.keys.filter { $0.hasPrefix("b") }).sort()
+ let bcIdx = keys.indexOf("bc")!
+ keys = Array(keys.dropFirst(bcIdx))
+ // Verify that's what's in the db
+ stream = try collection.scan(RowRangePrefix(prefix: "b"))
+ for (key, _) in stream {
+ let targetKey = keys[0]
+ XCTAssertEqual(key, targetKey)
+ keys.removeFirst()
+ }
+ XCTAssertTrue(keys.isEmpty)
+ XCTAssertNil(stream.next())
+ }
}
- class func doubleEq(lhs: Double, rhs: Double) -> Bool {
- return fabs(lhs - rhs) <= DBL_EPSILON
+ func testScan() {
+ withTestCollection { db, collection in
+ // Generate some test data (1 to 200 in hex)
+ var data = [String: NSData]()
+ for i in 1...200 {
+ let key = NSString(format: "%x", i) as String
+ let value = key.dataUsingEncoding(NSUTF8StringEncoding)!
+ data[key] = value
+ try collection.put(key, value: value)
+ }
+
+ // All rows
+ var stream = try collection.scan(RowRangeAll())
+ var keys = Array(data.keys).sort() // lexographic sort
+ for (key, getValue) in stream {
+ let value = try getValue() as! NSData
+ let valueStr = NSString(data: value, encoding: NSUTF8StringEncoding)!
+ XCTAssertEqual(key, valueStr)
+
+ let targetKey = keys[0]
+ XCTAssertEqual(key, targetKey)
+ keys.removeFirst()
+ }
+ XCTAssertTrue(keys.isEmpty)
+ XCTAssertNil(stream.next())
+
+ // Single Row
+ stream = try collection.scan(RowRangeSingleRow(row: "a"))
+ guard let (key, getValue) = stream.next() else {
+ XCTFail()
+ return
+ }
+ XCTAssertEqual(key, "a")
+ let value = try getValue() as! NSData
+ let valueStr = NSString(data: value, encoding: NSUTF8StringEncoding)!
+ XCTAssertEqual(key, valueStr)
+ XCTAssertNil(stream.next())
+ // Doing it again should be ok
+ XCTAssertNil(stream.next())
+
+ // Prefix
+ stream = try collection.scan(RowRangePrefix(prefix: "8"))
+ keys = Array(data.keys.filter { $0.hasPrefix("8") }).sort() // lexographic sort
+ for (key, _) in stream {
+ let targetKey = keys[0]
+ XCTAssertEqual(key, targetKey)
+ keys.removeFirst()
+ }
+ XCTAssertTrue(keys.isEmpty)
+ XCTAssertNil(stream.next())
+ }
+ }
+
+ // MARK: Test batches
+
+ func testBatchCommit() {
+ withTestDb { db in
+ let batchDb = try db.beginBatch(nil)
+ let collection = try batchDb.collection("collection2")
+ try collection.create(nil)
+ try collection.put("a", value: NSData())
+ try collection.put("1", value: NSData())
+ try collection.put("2", value: NSData())
+ try batchDb.commit()
+ do {
+ let _: NSData? = try collection.get("a")
+ XCTFail("Should have thrown an UnknownBatch exception")
+ } catch SyncbaseError.UnknownBatch {
+ // Expect this to fail since the batch is already commited -- the collection reference
+ // is now invalid.
+ } catch {
+ XCTFail("Should have thrown an UnknownBatch exception")
+ }
+ let valueA: NSData? = try db.collection("collection2").get("a")
+ let value1: NSData? = try db.collection("collection2").get("1")
+ let value2: NSData? = try db.collection("collection2").get("2")
+ XCTAssertNotNil(valueA)
+ XCTAssertNotNil(value1)
+ XCTAssertNotNil(value2)
+ }
+ }
+
+ func testBatchAbort() {
+ withTestDb { db in
+ let batchDb = try db.beginBatch(nil)
+ let collection = try batchDb.collection("collection2")
+ try collection.create(nil)
+ try collection.put("b", value: NSData())
+ try collection.put("c", value: NSData())
+ try batchDb.abort()
+ do {
+ let _: NSData? = try collection.get("a")
+ XCTFail("Should have thrown an UnknownBatch exception")
+ } catch SyncbaseError.UnknownBatch {
+ // Expect this to fail since the batch is already commited -- the collection reference
+ // is now invalid.
+ } catch {
+ XCTFail("Should have thrown an UnknownBatch exception")
+ }
+ let valueB: NSData? = try db.collection("collection2").get("b")
+ let valueC: NSData? = try db.collection("collection2").get("c")
+ XCTAssertNil(valueB)
+ XCTAssertNil(valueC)
+ }
+ }
+
+ func testRunInBatchAutoCommit() {
+ let completed = expectationWithDescription("Completed runInBatch for auto commit")
+ withTestDbAsync { (db, cleanup) in
+ Batch.runInBatch(
+ db: db,
+ opts: nil,
+ op: { batchDb in
+ let collection = try batchDb.collection("collection3")
+ try collection.create(nil)
+ try collection.put("a", value: NSData())
+ },
+ completionHandler: { err in
+ XCTAssertNil(err)
+ do {
+ let collection = try db.collection("collection3")
+ let value: NSData? = try collection.get("a")
+ XCTAssertNotNil(value)
+ } catch let e {
+ XCTFail("Unexpected error: \(e)")
+ }
+ cleanup()
+ completed.fulfill()
+ })
+ }
+ waitForExpectationsWithTimeout(2) { XCTAssertNil($0) }
+ }
+
+ func testRunInBatchAbort() {
+ let completed = expectationWithDescription("Completed runInBatch for abort")
+ withTestDbAsync { (db, cleanup) in
+ Batch.runInBatch(
+ db: db,
+ opts: nil,
+ op: { batchDb in
+ let collection = try batchDb.collection("collection4")
+ try collection.create(nil)
+ try collection.put("a", value: NSData())
+ try batchDb.abort()
+ },
+ completionHandler: { err in
+ XCTAssertNil(err)
+ do {
+ let collection = try db.collection("collection4")
+ let value: NSData? = try collection.get("a")
+ XCTAssertNil(value)
+ } catch let e {
+ XCTFail("Unexpected error: \(e)")
+ }
+ cleanup()
+ completed.fulfill()
+ })
+ }
+ waitForExpectationsWithTimeout(2) { XCTAssertNil($0) }
}
}
diff --git a/SyncbaseCore/Tests/JsonTests.swift b/SyncbaseCore/Tests/JsonTests.swift
deleted file mode 100644
index 0a91288..0000000
--- a/SyncbaseCore/Tests/JsonTests.swift
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-import XCTest
-@testable import SyncbaseCore
-
-class JSONTests: XCTestCase {
- // Emulate syncbase's put
- func toJSON(any: SyncbaseJsonConvertible) -> (NSData, JsonDataType) {
- return try! any.toSyncbaseJson()
- }
-
- func toString(json: NSData) -> String {
- return String(data: json, encoding: NSUTF8StringEncoding)!
- }
-
- func testBasicEncoding() {
- var (data, type) = try! 5.toSyncbaseJson()
- XCTAssertEqual(toString(data), "5")
- XCTAssertEqual(type, JsonDataType.Int)
- (data, type) = toJSON(5)
- XCTAssertEqual(toString(data), "5")
- XCTAssertEqual(type, JsonDataType.Int)
- (data, type) = try! true.toSyncbaseJson()
- XCTAssertEqual(toString(data), "true")
- XCTAssertEqual(type, JsonDataType.Bool)
- (data, type) = try! false.toSyncbaseJson()
- XCTAssertEqual(toString(data), "false")
- XCTAssertEqual(type, JsonDataType.Bool)
- (data, type) = try! Int8(5).toSyncbaseJson()
- XCTAssertEqual(toString(data), "5")
- XCTAssertEqual(type, JsonDataType.Int8)
- (data, type) = try! Int16(5).toSyncbaseJson()
- XCTAssertEqual(toString(data), "5")
- XCTAssertEqual(type, JsonDataType.Int16)
- (data, type) = try! Int32(5).toSyncbaseJson()
- XCTAssertEqual(toString(data), "5")
- XCTAssertEqual(type, JsonDataType.Int32)
- (data, type) = try! Int64(5).toSyncbaseJson()
- XCTAssertEqual(toString(data), "5")
- XCTAssertEqual(type, JsonDataType.Int64)
- (data, type) = try! Int64(Int64(Int32.max) + 10).toSyncbaseJson()
- XCTAssertEqual(toString(data), "\(Int64(Int32.max) + 10)")
- XCTAssertEqual(type, JsonDataType.Int64)
- (data, type) = try! Float(5.0).toSyncbaseJson()
- XCTAssertEqual(toString(data), "5") // 5 is ok here since we pass knowledge it's a float
- XCTAssertEqual(type, JsonDataType.Float)
- (data, type) = try! Double(5.1).toSyncbaseJson()
- XCTAssertEqual(toString(data), "5.1") // 5 is ok here since we pass knowledge it's a float
- XCTAssertEqual(type, JsonDataType.Double)
- (data, type) = try! "Hello world! 👠".toSyncbaseJson()
- XCTAssertEqual(toString(data), "\"Hello world! 👠\"")
- XCTAssertEqual(type, JsonDataType.String)
- (data, type) = try! [1, 2, 3, 4].toSyncbaseJson()
- XCTAssertEqual(toString(data), "[1,2,3,4]")
- XCTAssertEqual(type, JsonDataType.Array)
- (data, type) = toJSON([1, 2, 3, 4])
- XCTAssertEqual(toString(data), "[1,2,3,4]")
- XCTAssertEqual(type, JsonDataType.Array)
- (data, type) = try! ["a", "b", "c"].toSyncbaseJson()
- XCTAssertEqual(toString(data), "[\"a\",\"b\",\"c\"]")
- XCTAssertEqual(type, JsonDataType.Array)
- (data, type) = try! ["a": 1].toSyncbaseJson()
- XCTAssertEqual(toString(data), "{\"a\":1}")
- XCTAssertEqual(type, JsonDataType.Dictionary)
- (data, type) = try! ["b": true].toSyncbaseJson()
- XCTAssertEqual(toString(data), "{\"b\":true}")
- XCTAssertEqual(type, JsonDataType.Dictionary)
- (data, type) = try! ["c": "👠"].toSyncbaseJson()
- XCTAssertEqual(toString(data), "{\"c\":\"👠\"}")
- XCTAssertEqual(type, JsonDataType.Dictionary)
- (data, type) = toJSON(["c": "👠"])
- XCTAssertEqual(toString(data), "{\"c\":\"👠\"}")
- XCTAssertEqual(type, JsonDataType.Dictionary)
- }
-}
diff --git a/SyncbaseCore/Tests/NSNumberTests.swift b/SyncbaseCore/Tests/NSNumberTests.swift
deleted file mode 100644
index 110e2ee..0000000
--- a/SyncbaseCore/Tests/NSNumberTests.swift
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-import XCTest
-@testable import SyncbaseCore
-
-class NSNumberTests: XCTestCase {
- func testISNSNumber() {
- XCTAssertTrue(NSNumber.isNSNumber(NSNumber(bool: true)))
- XCTAssertFalse(NSNumber.isNSNumber(true))
- XCTAssertTrue(NSNumber.isNSNumber(true as AnyObject))
- XCTAssertTrue(NSNumber.isNSNumber(true as NSNumber))
- XCTAssertFalse(NSNumber.isNSNumber(""))
- XCTAssertFalse(NSNumber.isNSNumber(5))
- XCTAssertTrue(NSNumber.isNSNumber(5 as AnyObject))
- }
-
- func testConservationOfPrecision() {
- var anyobj: AnyObject?
- var nsnumber: NSNumber?
- var bool: Bool? = true
- var int: Int? = Int.max
- var int8: Int8? = Int8.max
- var int16: Int16? = Int16.max
- var int32: Int32? = Int32.max
- var int64: Int64? = Int64.max
- var float: Float? = Float(Int32.max)
- var double: Double? = Double(Int64.max)
- var long: CLong? = CLong.max
- var uint: UInt? = UInt.max
- var uint8: UInt8? = UInt8.max
- var uint16: UInt16? = UInt16.max
- var uint32: UInt32? = UInt32.max
- var uint64: UInt64? = UInt64.max
- var ulong: CUnsignedLong? = CUnsignedLong.max
-
- // Preserve booleans
- XCTAssertTrue((true as NSNumber).isTargetCastable(&bool))
- XCTAssertTrue((true as NSNumber).isTargetCastable(&anyobj))
- XCTAssertTrue((true as NSNumber).isTargetCastable(&nsnumber))
- XCTAssertFalse((true as NSNumber).isTargetCastable(&int))
- XCTAssertFalse((true as NSNumber).isTargetCastable(&float))
- XCTAssertFalse((true as NSNumber).isTargetCastable(&long))
-
- // Preserved size
- XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int8))
- XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int16))
- XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int32))
- XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int))
- XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(char: int8!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(char: int8!).isTargetCastable(&double))
-
- XCTAssertFalse(NSNumber(short: int16!).isTargetCastable(&int8))
- XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int16))
- XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int32))
- XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int))
- XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(short: int16!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(short: int16!).isTargetCastable(&double))
-
- XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&int16))
- XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&int32))
- XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&int))
- XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&uint))
- XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&double))
-
- if sizeof(CLong) == sizeof(Int64) {
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int16))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int32))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&long))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&uint))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&double))
- } else {
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int16))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int32))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&double))
- }
-
- XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int16))
- XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int32))
- if sizeof(Int) == sizeof(Int64) {
- XCTAssertTrue(NSNumber(longLong: int64!).isTargetCastable(&int))
- } else {
- XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int))
- }
- XCTAssertTrue(NSNumber(longLong: int64!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(longLong: int64!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&double))
-
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int16))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int32))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint8))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint16))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint32))
- XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint64))
- XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&ulong))
- // TODO(zinman): Be smarter about when we allow this or not
- if sizeof(Int) == sizeof(Int64) {
- XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int))
- } else {
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int))
- }
- XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int64))
- XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&float))
- XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&double))
-
- XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int16))
- XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int32))
- XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int64))
- XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int))
- XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&long))
- XCTAssertTrue(NSNumber(float: float!).isTargetCastable(&float))
- XCTAssertTrue(NSNumber(float: float!).isTargetCastable(&double))
-
- XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int8))
- XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int16))
- XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int32))
- XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int64))
- XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&long))
- XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&float))
- XCTAssertTrue(NSNumber(double: double!).isTargetCastable(&double))
- }
-}