mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 11:53:07 +09:00
303 lines
13 KiB
Swift
303 lines
13 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import Foundation
|
|
import Shared
|
|
@testable import Storage
|
|
import Deferred
|
|
|
|
import XCTest
|
|
|
|
open class MockRemoteClientsAndTabs: RemoteClientsAndTabs {
|
|
open let clientsAndTabs: [ClientAndTabs]
|
|
|
|
public init() {
|
|
let now = Date.now()
|
|
let client1GUID = Bytes.generateGUID()
|
|
let client2GUID = Bytes.generateGUID()
|
|
let u11 = URL(string: "http://test.com/test1")!
|
|
let tab11 = RemoteTab(clientGUID: client1GUID, URL: u11, title: "Test 1", history: [ ], lastUsed: (now - OneMinuteInMilliseconds), icon: nil)
|
|
|
|
let u12 = URL(string: "http://test.com/test2")!
|
|
let tab12 = RemoteTab(clientGUID: client1GUID, URL: u12, title: "Test 2", history: [], lastUsed: (now - OneHourInMilliseconds), icon: nil)
|
|
|
|
let tab21 = RemoteTab(clientGUID: client2GUID, URL: u11, title: "Test 1", history: [], lastUsed: (now - OneDayInMilliseconds), icon: nil)
|
|
|
|
let u22 = URL(string: "http://different.com/test2")!
|
|
let tab22 = RemoteTab(clientGUID: client2GUID, URL: u22, title: "Different Test 2", history: [], lastUsed: now + OneHourInMilliseconds, icon: nil)
|
|
|
|
let client1 = RemoteClient(guid: client1GUID, name: "Test client 1", modified: (now - OneMinuteInMilliseconds), type: "mobile", formfactor: "largetablet", os: "iOS", version: "55.0.1", fxaDeviceId: "fxa1")
|
|
let client2 = RemoteClient(guid: client2GUID, name: "Test client 2", modified: (now - OneHourInMilliseconds), type: "desktop", formfactor: "laptop", os: "Darwin", version: "55.0.1", fxaDeviceId: "fxa2")
|
|
|
|
let localClient = RemoteClient(guid: nil, name: "Test local client", modified: (now - OneMinuteInMilliseconds), type: "mobile", formfactor: "largetablet", os: "iOS", version: "55.0.1", fxaDeviceId: "fxa3")
|
|
let localUrl1 = URL(string: "http://test.com/testlocal1")!
|
|
let localTab1 = RemoteTab(clientGUID: nil, URL: localUrl1, title: "Local test 1", history: [], lastUsed: (now - OneMinuteInMilliseconds), icon: nil)
|
|
let localUrl2 = URL(string: "http://test.com/testlocal2")!
|
|
let localTab2 = RemoteTab(clientGUID: nil, URL: localUrl2, title: "Local test 2", history: [], lastUsed: (now - OneMinuteInMilliseconds), icon: nil)
|
|
|
|
// Tabs are ordered most-recent-first.
|
|
self.clientsAndTabs = [ClientAndTabs(client: client1, tabs: [tab11, tab12]),
|
|
ClientAndTabs(client: client2, tabs: [tab22, tab21]),
|
|
ClientAndTabs(client: localClient, tabs: [localTab1, localTab2])]
|
|
}
|
|
|
|
open func onRemovedAccount() -> Success {
|
|
return succeed()
|
|
}
|
|
|
|
open func wipeClients() -> Success {
|
|
return succeed()
|
|
}
|
|
|
|
open func wipeRemoteTabs() -> Deferred<Maybe<()>> {
|
|
return succeed()
|
|
}
|
|
|
|
open func wipeTabs() -> Success {
|
|
return succeed()
|
|
}
|
|
|
|
open func insertOrUpdateClients(_ clients: [RemoteClient]) -> Deferred<Maybe<Int>> {
|
|
return deferMaybe(0)
|
|
}
|
|
|
|
open func insertOrUpdateClient(_ client: RemoteClient) -> Deferred<Maybe<Int>> {
|
|
return deferMaybe(0)
|
|
}
|
|
|
|
open func insertOrUpdateTabs(_ tabs: [RemoteTab]) -> Deferred<Maybe<Int>> {
|
|
return insertOrUpdateTabsForClientGUID(nil, tabs: [RemoteTab]())
|
|
}
|
|
|
|
open func insertOrUpdateTabsForClientGUID(_ clientGUID: String?, tabs: [RemoteTab]) -> Deferred<Maybe<Int>> {
|
|
return deferMaybe(-1)
|
|
}
|
|
|
|
open func getClientsAndTabs() -> Deferred<Maybe<[ClientAndTabs]>> {
|
|
return deferMaybe(self.clientsAndTabs)
|
|
}
|
|
|
|
open func getClients() -> Deferred<Maybe<[RemoteClient]>> {
|
|
return deferMaybe(self.clientsAndTabs.map { $0.client })
|
|
}
|
|
|
|
public func getClient(guid: GUID) -> Deferred<Maybe<RemoteClient?>> {
|
|
return deferMaybe(self.clientsAndTabs.find { clientAndTabs in
|
|
return clientAndTabs.client.guid == guid
|
|
}?.client)
|
|
}
|
|
|
|
public func getClient(fxaDeviceId: GUID) -> Deferred<Maybe<RemoteClient?>> {
|
|
return deferMaybe(self.clientsAndTabs.find { clientAndTabs in
|
|
return clientAndTabs.client.fxaDeviceId == fxaDeviceId
|
|
}?.client)
|
|
}
|
|
|
|
open func getClientWithId(_ clientID: GUID) -> Deferred<Maybe<RemoteClient?>> {
|
|
return getClient(guid: clientID)
|
|
}
|
|
|
|
open func getClientGUIDs() -> Deferred<Maybe<Set<GUID>>> {
|
|
return deferMaybe(Set<GUID>(optFilter(self.clientsAndTabs.map { $0.client.guid })))
|
|
}
|
|
|
|
open func getTabsForClientWithGUID(_ guid: GUID?) -> Deferred<Maybe<[RemoteTab]>> {
|
|
return deferMaybe(optFilter(self.clientsAndTabs.map { $0.client.guid == guid ? $0.tabs : nil })[0])
|
|
}
|
|
|
|
open func deleteClient(guid: GUID) -> Success { return succeed() }
|
|
|
|
open func deleteCommands() -> Success { return succeed() }
|
|
open func deleteCommands(_ clientGUID: GUID) -> Success { return succeed() }
|
|
|
|
open func getCommands() -> Deferred<Maybe<[GUID: [SyncCommand]]>> { return deferMaybe([GUID: [SyncCommand]]()) }
|
|
|
|
open func insertCommand(_ command: SyncCommand, forClients clients: [RemoteClient]) -> Deferred<Maybe<Int>> { return deferMaybe(0) }
|
|
open func insertCommands(_ commands: [SyncCommand], forClients clients: [RemoteClient]) -> Deferred<Maybe<Int>> { return deferMaybe(0) }
|
|
}
|
|
|
|
func removeLocalClient(_ a: ClientAndTabs) -> Bool {
|
|
return a.client.guid != nil
|
|
}
|
|
|
|
func byGUID(_ a: ClientAndTabs, b: ClientAndTabs) -> Bool {
|
|
guard let aGUID = a.client.guid, let bGUID = b.client.guid else {
|
|
return false
|
|
}
|
|
return aGUID < bGUID
|
|
}
|
|
|
|
func byURL(_ a: RemoteTab, b: RemoteTab) -> Bool {
|
|
return a.URL.absoluteString < b.URL.absoluteString
|
|
}
|
|
|
|
class SQLRemoteClientsAndTabsTests: XCTestCase {
|
|
var clientsAndTabs: SQLiteRemoteClientsAndTabs!
|
|
|
|
lazy var clients: [ClientAndTabs] = MockRemoteClientsAndTabs().clientsAndTabs
|
|
|
|
override func setUp() {
|
|
let files = MockFiles()
|
|
do {
|
|
try files.remove("browser.db")
|
|
} catch _ {
|
|
}
|
|
clientsAndTabs = SQLiteRemoteClientsAndTabs(db: BrowserDB(filename: "browser.db", schema: BrowserSchema(), files: files))
|
|
}
|
|
|
|
func testInsertGetClear() {
|
|
// Insert some test data.
|
|
var remoteDevicesToInsert: [RemoteDevice] = []
|
|
// Filter the local client from mock test data.
|
|
let remoteClients = clients.filter(removeLocalClient)
|
|
for c in remoteClients {
|
|
let e = self.expectation(description: "Insert.")
|
|
clientsAndTabs.insertOrUpdateClient(c.client).upon {
|
|
XCTAssertTrue($0.isSuccess)
|
|
e.fulfill()
|
|
}
|
|
let remoteDevice = RemoteDevice(id: c.client.fxaDeviceId!, name: "FxA Device", type: "desktop", isCurrentDevice: false, lastAccessTime: 12345678)
|
|
remoteDevicesToInsert.append(remoteDevice)
|
|
clientsAndTabs.insertOrUpdateTabsForClientGUID(c.client.guid, tabs: c.tabs).succeeded()
|
|
}
|
|
_ = clientsAndTabs.replaceRemoteDevices(remoteDevicesToInsert).succeeded()
|
|
|
|
let f = self.expectation(description: "Get after insert.")
|
|
clientsAndTabs.getClientsAndTabs().upon {
|
|
if let got = $0.successValue {
|
|
let expected = remoteClients.sorted(by: byGUID)
|
|
let actual = got.sorted(by: byGUID)
|
|
|
|
// This comparison will fail if the order of the tabs changes. We sort the result
|
|
// as part of the DB query, so it's not actively sorted in Swift.
|
|
XCTAssertEqual(expected, actual)
|
|
} else {
|
|
XCTFail("Expected clients!")
|
|
}
|
|
f.fulfill()
|
|
}
|
|
|
|
// Update the test data with a client with new tabs, and one with no tabs.
|
|
let client0NewTabs = clients[1].tabs.map { $0.withClientGUID(self.clients[0].client.guid) }
|
|
let client1NewTabs: [RemoteTab] = []
|
|
let expected = [
|
|
ClientAndTabs(client: clients[0].client, tabs: client0NewTabs),
|
|
ClientAndTabs(client: clients[1].client, tabs: client1NewTabs),
|
|
].sorted(by: byGUID)
|
|
|
|
func doUpdate(_ guid: String?, tabs: [RemoteTab]) {
|
|
let g0 = self.expectation(description: "Update client: \(guid ?? "nil").")
|
|
clientsAndTabs.insertOrUpdateTabsForClientGUID(guid, tabs: tabs).upon {
|
|
if let rowID = $0.successValue {
|
|
XCTAssertTrue(rowID > -1)
|
|
} else {
|
|
XCTFail("Didn't successfully update.")
|
|
}
|
|
g0.fulfill()
|
|
}
|
|
}
|
|
|
|
doUpdate(clients[0].client.guid, tabs: client0NewTabs)
|
|
doUpdate(clients[1].client.guid, tabs: client1NewTabs)
|
|
// Also update the local tabs list. It should still not appear in the expected tabs below.
|
|
doUpdate(clients[2].client.guid, tabs: client1NewTabs)
|
|
|
|
let h = self.expectation(description: "Get after update.")
|
|
clientsAndTabs.getClientsAndTabs().upon {
|
|
if let clients = $0.successValue {
|
|
XCTAssertEqual(expected, clients.sorted(by: byGUID))
|
|
} else {
|
|
XCTFail("Expected clients!")
|
|
}
|
|
h.fulfill()
|
|
}
|
|
|
|
// Now clear everything, and verify we have no clients or tabs whatsoever.
|
|
let i = self.expectation(description: "Clear.")
|
|
clientsAndTabs.clear().upon {
|
|
XCTAssertTrue($0.isSuccess)
|
|
i.fulfill()
|
|
}
|
|
|
|
let j = self.expectation(description: "Get after clear.")
|
|
clientsAndTabs.getClientsAndTabs().upon {
|
|
if let clients = $0.successValue {
|
|
XCTAssertEqual(0, clients.count)
|
|
} else {
|
|
XCTFail("Expected clients!")
|
|
}
|
|
j.fulfill()
|
|
}
|
|
|
|
self.waitForExpectations(timeout: 10, handler: nil)
|
|
}
|
|
|
|
func testGetTabsForClient() {
|
|
for c in clients {
|
|
let e = self.expectation(description: "Insert.")
|
|
clientsAndTabs.insertOrUpdateClient(c.client).upon {
|
|
XCTAssertTrue($0.isSuccess)
|
|
e.fulfill()
|
|
}
|
|
clientsAndTabs.insertOrUpdateTabsForClientGUID(c.client.guid, tabs: c.tabs).succeeded()
|
|
}
|
|
|
|
let e = self.expectation(description: "Get after insert.")
|
|
let ct = clients[0]
|
|
clientsAndTabs.getTabsForClientWithGUID(ct.client.guid).upon {
|
|
if let got = $0.successValue {
|
|
// This comparison will fail if the order of the tabs changes. We sort the result
|
|
// as part of the DB query, so it's not actively sorted in Swift.
|
|
XCTAssertEqual(ct.tabs.count, got.count)
|
|
XCTAssertEqual(ct.tabs.sorted(by: byURL), got.sorted(by: byURL))
|
|
} else {
|
|
XCTFail("Expected tabs!")
|
|
}
|
|
e.fulfill()
|
|
}
|
|
|
|
let f = self.expectation(description: "Get after insert.")
|
|
let localClient = clients[0]
|
|
clientsAndTabs.getTabsForClientWithGUID(localClient.client.guid).upon {
|
|
if let got = $0.successValue {
|
|
// This comparison will fail if the order of the tabs changes. We sort the result
|
|
// as part of the DB query, so it's not actively sorted in Swift.
|
|
XCTAssertEqual(localClient.tabs.count, got.count)
|
|
XCTAssertEqual(localClient.tabs.sorted(by: byURL), got.sorted(by: byURL))
|
|
} else {
|
|
XCTFail("Expected tabs!")
|
|
}
|
|
f.fulfill()
|
|
}
|
|
|
|
self.waitForExpectations(timeout: 10, handler: nil)
|
|
}
|
|
|
|
func remoteDeviceFactory(_ row: SDRow) -> RemoteDevice {
|
|
return RemoteDevice(
|
|
id: row["guid"] as? String,
|
|
name: row["name"] as! String,
|
|
type: row["type"] as? String,
|
|
isCurrentDevice: row["is_current_device"] as! Int > 0,
|
|
lastAccessTime: row["last_access_time"] as? Timestamp)
|
|
}
|
|
|
|
func testReplaceRemoteDevices() {
|
|
let device1 = RemoteDevice(id: "fx1", name: "Device 1", type: "mobile", isCurrentDevice: false, lastAccessTime: 12345678)
|
|
let device2 = RemoteDevice(id: "fx2", name: "Device 2 (local)", type: "desktop", isCurrentDevice: true, lastAccessTime: nil)
|
|
let device3 = RemoteDevice(id: nil, name: "Device 3 (fauly)", type: "desktop", isCurrentDevice: false, lastAccessTime: 12345678)
|
|
let device4 = RemoteDevice(id: "fx4", name: "Device 4 (fauly)", type: nil, isCurrentDevice: false, lastAccessTime: 12345678)
|
|
|
|
_ = clientsAndTabs.replaceRemoteDevices([device1, device2, device3, device4]).succeeded()
|
|
|
|
let devices = clientsAndTabs.db.runQuery("SELECT * FROM \(TableRemoteDevices)", args: nil, factory: remoteDeviceFactory).value.successValue!.asArray()
|
|
XCTAssertEqual(devices.count, 1) // Fauly devices + local device were not inserted.
|
|
|
|
let device5 = RemoteDevice(id: "fx5", name: "Device 5", type: "mobile", isCurrentDevice: false, lastAccessTime: 12345678)
|
|
_ = clientsAndTabs.replaceRemoteDevices([device5]).succeeded()
|
|
|
|
let newDevices = clientsAndTabs.db.runQuery("SELECT * FROM \(TableRemoteDevices)", args: nil, factory: remoteDeviceFactory).value.successValue!.asArray()
|
|
XCTAssertEqual(newDevices.count, 1) // replaceRemoteDevices wipes the whole list before inserting.
|
|
}
|
|
}
|