blob: 4ef5fd4136c6412d8fd21ae86bb518b0aa60dbfa [file] [log] [blame]
// 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 Syncbase
import enum Syncbase.Syncbase
import class Syncbase.Database
@testable import SyncbaseCore
let testQueue = dispatch_queue_create("SyncbaseQueue", DISPATCH_QUEUE_SERIAL)
class BasicDatabaseTests: XCTestCase {
override class func setUp() {
configureDb(disableUserdataSyncgroup: true, disableSyncgroupPublishing: true)
}
override class func tearDown() {
Syncbase.shutdown()
}
func testDatabaseInit() {
withDb { db in
// Must be idempotent.
try db.createIfMissing()
try db.createIfMissing()
}
}
func testCollection() {
withDb { db in
var collections = try db.collections()
XCTAssertEqual(collections.count, 0)
let collection = try db.createCollection(prefix: "testCollection")
// Must be idempotent.
try collection.createIfMissing()
try collection.createIfMissing()
collections = try db.collections()
XCTAssertEqual(collections.count, 1)
XCTAssertTrue(collection.collectionId.name.hasPrefix("testCollection"))
XCTAssertGreaterThan(collection.collectionId.name.characters.count, "testCollection_".characters.count)
// Should be empty.
XCTAssertFalse(try collection.exists("a"))
try collection.destroy()
collections = try db.collections()
XCTAssertEqual(collections.count, 0)
}
}
// TODO(zinman): Add more unit tests.
}
class AdvertiseScanTests: XCTestCase {
override class func setUp() {
configureDb(disableUserdataSyncgroup: true, disableSyncgroupPublishing: true)
}
override class func tearDown() {
Syncbase.shutdown()
}
func testAdvertise() {
XCTAssertFalse(Syncbase.isAdvertisingPresenceInNeighborhood())
withDb { _ in
// Just a simple test of the API as a sanity check -- we'd need an integration test across
// devices or multiple simulators to make sure advertising properly worked. Testing within one
// process inside the simulator doesn't allow us to test Bluetooth (not possible anyway on the
// simulator -- see https://forums.developer.apple.com/thread/47230 ) or mDNS across a real
// network. Luckily the Go code has integration tests for discovery already.
try Syncbase.startAdvertisingPresenceInNeighborhood()
XCTAssertTrue(Syncbase.isAdvertisingPresenceInNeighborhood())
try Syncbase.stopAdvertisingPresenceInNeighborhood()
XCTAssertFalse(Syncbase.isAdvertisingPresenceInNeighborhood())
try Syncbase.startAdvertisingPresenceInNeighborhood([User(alias: "zinman@google.com")])
XCTAssertTrue(Syncbase.isAdvertisingPresenceInNeighborhood())
try Syncbase.stopAdvertisingPresenceInNeighborhood()
XCTAssertFalse(Syncbase.isAdvertisingPresenceInNeighborhood())
}
}
func testScan() {
withDb { _ in
let handler = ScanNeighborhoodForUsersHandler(
onFound: { user in
XCTFail("Unexpected onFound user during unit test: \(user)")
},
onLost: { user in
XCTFail("Unexpected onLost user during unit test: \(user)")
})
try Syncbase.startScanForUsersInNeighborhood(handler)
Syncbase.neighborhoodScansMu.lock()
XCTAssertEqual(Syncbase.neighborhoodScans.count, 1)
Syncbase.neighborhoodScansMu.unlock()
Syncbase.stopScanForUsersInNeighborhood(handler)
Syncbase.neighborhoodScansMu.lock()
XCTAssertEqual(Syncbase.neighborhoodScans.count, 0)
Syncbase.neighborhoodScansMu.unlock()
try Syncbase.startScanForUsersInNeighborhood(handler)
Syncbase.stopAllScansForUsersInNeighborhood()
}
}
}
class UserdataTest: SyncgroupTest {
func testUserdata() {
withDb { db in
let coreCollections = try db.coreDatabase.listCollections()
XCTAssertEqual(coreCollections.count, 1)
XCTAssertEqual(coreCollections[0].name, Syncbase.UserdataSyncgroupName)
// Expect filtered from HLAPI.
let collections = try db.collections()
XCTAssertEqual(collections.count, 0)
let coreSyncgroups = try db.coreDatabase.listSyncgroups()
XCTAssertEqual(coreSyncgroups.count, 1)
XCTAssertEqual(coreSyncgroups[0].name, Syncbase.UserdataSyncgroupName)
// Expect filtered from HLAPI.
let syncgroups = try db.syncgroups()
XCTAssertEqual(syncgroups.count, 0)
let verSpec = try db.coreDatabase.syncgroup(Syncbase.UserdataSyncgroupName).getSpec()
XCTAssertEqual(verSpec.spec.collections.count, 1)
// TODO(razvanm): Make the userdata syncgroup private.
XCTAssertEqual(verSpec.spec.isPrivate, false)
// Shouldn't crash unpacking.
db.userdataCollection
}
}
}
class InviteUserdataFilteredTest: SyncgroupTest {
func testUserdataFiltered() {
withDb { db in
db.addSyncgroupInviteHandler(SyncgroupInviteHandler(
onInvite: { invite in
XCTFail("Not expecting any invites")
},
onError: { err in
XCTFail("Not expecting any errors: \(err)")
}))
NSThread.sleepForTimeInterval(0.1)
db.removeAllSyncgroupInviteHandlers()
}
}
}
class InvitedSyncgroupsTest: SyncgroupTest {
static let coreSyncgroupId = SyncbaseCore.Identifier(
name: "someSg",
blessing: "dev.v.io:o:some.apps.googleusercontent.com:someone@google.com")
static let coreInvite = SyncbaseCore.SyncgroupInvite(
syncgroupId: coreSyncgroupId,
addresses: [""],
blessingNames: [coreSyncgroupId.blessing])
func testInvitedSyncgroupsNotIgnoredWhenNew() {
withDb { db in
var didGetInvite = false
let handler = SyncgroupInviteHandler(
onInvite: { invite in
if invite.syncgroupId.name == InvitedSyncgroupsTest.coreSyncgroupId.name {
didGetInvite = true
}
},
onError: { err in
XCTFail("Not expecting any errors: \(err)")
})
db.addSyncgroupInviteHandler(handler)
let coreHandler = Database.syncgroupInviteHandlers[handler]
coreHandler?.onInvite(InvitedSyncgroupsTest.coreInvite)
XCTAssertTrue(didGetInvite)
db.removeSyncgroupInviteHandler(handler)
}
}
func testInvitedSyncgroupsIgnoredWhenJoined() {
withDb { db in
// We should get the invite when it's not in our userdata
let coreInvite = SyncbaseCore.SyncgroupInvite(
syncgroupId: InvitedSyncgroupsTest.coreSyncgroupId,
addresses: [""],
blessingNames: [InvitedSyncgroupsTest.coreSyncgroupId.blessing])
var didGetInvite = false
var handler = SyncgroupInviteHandler(
onInvite: { invite in
if invite.syncgroupId == Identifier(coreId: InvitedSyncgroupsTest.coreSyncgroupId) {
didGetInvite = true
}
},
onError: { err in
XCTFail("Not expecting any errors: \(err)")
})
db.addSyncgroupInviteHandler(handler)
var coreHandler = Database.syncgroupInviteHandlers[handler]
coreHandler?.onInvite(coreInvite)
XCTAssertTrue(didGetInvite)
db.removeSyncgroupInviteHandler(handler)
// Add it to userdata
try Syncbase.addSyncgroupToUserdata(Identifier(coreId: InvitedSyncgroupsTest.coreSyncgroupId))
// Now we shouldn't get it
handler = SyncgroupInviteHandler(
onInvite: { invite in
XCTFail("Not expecting any invites: \(invite)")
},
onError: { err in
XCTFail("Not expecting any errors: \(err)")
})
db.addSyncgroupInviteHandler(handler)
coreHandler = Database.syncgroupInviteHandlers[handler]
coreHandler?.onInvite(coreInvite)
db.removeSyncgroupInviteHandler(handler)
}
}
}
class WatchSyncgroupTest: SyncgroupTest {
func testAddingSyncgroup() {
withDb { db in
let initialSemaphore = dispatch_semaphore_create(0)
let changeSemaphore = dispatch_semaphore_create(0)
let sg1 = Identifier(coreId: SyncbaseCore.Identifier(name: "sg1", blessing: "..."))
var didChange = false
try db.addInternalUserdataWatchChangeHandler(handler: WatchChangeHandler(
onInitialState: { _ in
dispatch_semaphore_signal(initialSemaphore)
},
onChangeBatch: { changes in
XCTAssertFalse(didChange)
XCTAssertEqual(changes.count, 1)
let change = changes.first!
XCTAssertEqual(change.row, try? Syncbase.UserdataCollectionPrefix + sg1.encode())
XCTAssertEqual(change.changeType, WatchChange.ChangeType.Put)
XCTAssertEqual(change.value, NSData())
didChange = true
dispatch_semaphore_signal(changeSemaphore)
},
onError: failOnNonContextError))
if dispatch_semaphore_wait(initialSemaphore, secondsGCD(1)) != 0 {
XCTFail("Timed out")
}
try Syncbase.addSyncgroupToUserdata(sg1)
if dispatch_semaphore_wait(changeSemaphore, secondsGCD(1)) != 0 {
XCTFail("Timed out")
}
XCTAssertEqual(didChange, true)
}
}
func testWatchIgnoresInternalUserData() {
withDb { db in
var initialSemaphore = dispatch_semaphore_create(0)
let changeSemaphore = dispatch_semaphore_create(0)
// Only our own userdata additions should appear.
try db.addWatchChangeHandler(handler: WatchChangeHandler(
onInitialState: { changes in
dispatch_semaphore_signal(initialSemaphore)
},
onChangeBatch: { changes in
XCTAssertEqual(changes.count, 1)
let change = changes[0]
XCTAssertEqual(change.collectionId?.name, Syncbase.UserdataSyncgroupName)
XCTAssertEqual(change.row, "testValue")
dispatch_semaphore_signal(changeSemaphore)
},
onError: failOnNonContextError))
if dispatch_semaphore_wait(initialSemaphore, secondsGCD(1)) != 0 {
XCTFail("Timed out")
}
try db.userdataCollection.put("testValue", value: NSData())
if dispatch_semaphore_wait(changeSemaphore, secondsGCD(1)) != 0 {
XCTFail("Timed out")
}
db.removeAllWatchChangeHandlers()
// Userdata only appears when explicitly asking for it.
initialSemaphore = dispatch_semaphore_create(0)
try db.addInternalUserdataWatchChangeHandler(
handler: WatchChangeHandler(
onInitialState: { changes in
XCTAssertFalse(changes.isEmpty)
for change in changes {
XCTAssert(change.collectionId?.name == Syncbase.UserdataSyncgroupName)
XCTAssert(change.row?.hasPrefix(Syncbase.UserdataCollectionPrefix) ?? false)
}
dispatch_semaphore_signal(initialSemaphore)
},
onChangeBatch: { changes in
XCTFail("Unexpected changes: \(changes)")
},
onError: failOnNonContextError))
if dispatch_semaphore_wait(initialSemaphore, secondsGCD(1)) != 0 {
XCTFail("Timed out")
}
}
}
}