mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-10 10:18:38 +09:00
Dactyloidae iOS initial commit
This commit is contained in:
parent
daa6179d22
commit
7154a0497e
2123 changed files with 197052 additions and 0 deletions
62
mobile/ios/SharedTests/ArrayExtensionTests.swift
Normal file
62
mobile/ios/SharedTests/ArrayExtensionTests.swift
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* 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
|
||||
import XCTest
|
||||
|
||||
class ArrayExtensionTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testUnique() {
|
||||
let a = [1, 2, 3, 4, 5, 6, 1, 2]
|
||||
let result = a.unique { return $0 }
|
||||
XCTAssertEqual(result, [1, 2, 3, 4, 5, 6])
|
||||
|
||||
let b = [1, 2, 3]
|
||||
let resultB = b.unique { return $0 }
|
||||
XCTAssertEqual(resultB, [1, 2, 3])
|
||||
}
|
||||
|
||||
func testUnion() {
|
||||
let a = [1, 2, 3, 4, 5, 6]
|
||||
let b = [7, 8, 9, 10]
|
||||
XCTAssertEqual(a.union(b) { return $0 },
|
||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
|
||||
let c = [1, 2, 3, 4, 5, 6]
|
||||
let d = [4, 5, 6, 7, 8, 9, 10]
|
||||
XCTAssertEqual(c.union(d) { return $0 }, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
|
||||
let e = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
let f = [4, 5, 6, 7, 8, 9, 10]
|
||||
XCTAssertEqual(e.union(f) { return $0 }, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
|
||||
let g = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
let h = [Int]()
|
||||
XCTAssertEqual(g.union(h) { return $0 }, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
|
||||
let i = [Int]()
|
||||
let j = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
XCTAssertEqual(i.union(j) { return $0 }, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||
}
|
||||
|
||||
func testSameElements() {
|
||||
let k = [1, 2, 3, 4, 5]
|
||||
let l = [1, 2, 3, 4, 5]
|
||||
let m = [2, 4, 6, 8, 10]
|
||||
let n: [Int]?
|
||||
n = k
|
||||
XCTAssertTrue(k.sameElements(l))
|
||||
XCTAssertFalse(l.sameElements(m))
|
||||
XCTAssertTrue((n?.sameElements(k))!)
|
||||
}
|
||||
|
||||
}
|
||||
167
mobile/ios/SharedTests/AsyncReducerTests.swift
Normal file
167
mobile/ios/SharedTests/AsyncReducerTests.swift
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* 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 XCTest
|
||||
import Deferred
|
||||
@testable import Shared
|
||||
|
||||
private let timeoutPeriod: TimeInterval = 600
|
||||
|
||||
class AsyncReducerTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testSimpleBehaviour() {
|
||||
let expectation = self.expectation(description: #function)
|
||||
happyCase(expectation, combine: simpleAdder)
|
||||
}
|
||||
|
||||
func testWaitingFillerBehaviour() {
|
||||
let expectation = self.expectation(description: #function)
|
||||
happyCase(expectation, combine: waitingFillingAdder)
|
||||
}
|
||||
|
||||
func testWaitingFillerAppendingBehaviour() {
|
||||
let expectation = self.expectation(description: #function)
|
||||
appendingCase(expectation, combine: waitingFillingAdder)
|
||||
}
|
||||
|
||||
func testFailingCombine() {
|
||||
let expectation = self.expectation(description: #function)
|
||||
let combine = { (a: Int, b: Int) -> Deferred<Maybe<Int>> in
|
||||
if a >= 6 {
|
||||
return deferMaybe(TestError())
|
||||
}
|
||||
return deferMaybe(a + b)
|
||||
}
|
||||
let reducer = AsyncReducer(initialValue: 0, combine: combine)
|
||||
reducer.terminal.upon { res in
|
||||
XCTAssert(res.isFailure)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
self.append(reducer, items: 1, 2, 3, 4, 5)
|
||||
waitForExpectations(timeout: timeoutPeriod, handler: nil)
|
||||
}
|
||||
|
||||
func testFailingAppend() {
|
||||
let expectation = self.expectation(description: #function)
|
||||
|
||||
let reducer = AsyncReducer(initialValue: 0, combine: simpleAdder)
|
||||
reducer.terminal.upon { res in
|
||||
XCTAssert(res.isSuccess)
|
||||
XCTAssertEqual(res.successValue!, 15)
|
||||
}
|
||||
|
||||
self.append(reducer, items: 1, 2, 3, 4, 5)
|
||||
|
||||
delay(0.1) {
|
||||
do {
|
||||
let _ = try reducer.append(6, 7, 8)
|
||||
XCTFail("Can't append to a reducer that's already finished")
|
||||
} catch let error {
|
||||
XCTAssert(true, "Properly received error on finished reducer \(error)")
|
||||
}
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: timeoutPeriod, handler: nil)
|
||||
}
|
||||
|
||||
func testAccumulation() {
|
||||
var addDuring: [String] = ["bar", "baz"]
|
||||
var reducer: AsyncReducer<[String: Bool], String>!
|
||||
|
||||
func combine(_ t: [String: Bool], u: String) -> Deferred<Maybe<[String: Bool]>> {
|
||||
var out = t
|
||||
out[u] = true
|
||||
|
||||
// Pretend that some new work arrived while we were handling this.
|
||||
if let nextUp = addDuring.popLast() {
|
||||
let _ = try! reducer.append(nextUp)
|
||||
}
|
||||
|
||||
return deferMaybe(out)
|
||||
}
|
||||
|
||||
// Start with 'foo'.
|
||||
reducer = AsyncReducer(initialValue: deferMaybe([:]), combine: combine)
|
||||
let _ = try! reducer.append("foo")
|
||||
|
||||
// Wait for the result. We should have handled all three by the time this returns.
|
||||
let result = reducer.terminal.value
|
||||
XCTAssertTrue(result.isSuccess)
|
||||
XCTAssertEqual(["foo": true, "bar": true, "baz": true], result.successValue!)
|
||||
}
|
||||
}
|
||||
|
||||
extension AsyncReducerTests {
|
||||
func happyCase(_ expectation: XCTestExpectation, combine: @escaping (Int, Int) -> Deferred<Maybe<Int>>) {
|
||||
let reducer = AsyncReducer(initialValue: 0, combine: combine)
|
||||
reducer.terminal.upon { res in
|
||||
XCTAssert(res.isSuccess)
|
||||
XCTAssertEqual(res.successValue!, 15)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
self.append(reducer, items: 1, 2, 3, 4, 5)
|
||||
waitForExpectations(timeout: timeoutPeriod, handler: nil)
|
||||
}
|
||||
|
||||
func appendingCase(_ expectation: XCTestExpectation, combine: @escaping (Int, Int) -> Deferred<Maybe<Int>>) {
|
||||
let reducer = AsyncReducer(initialValue: 0, combine: combine)
|
||||
reducer.terminal.upon { res in
|
||||
XCTAssert(res.isSuccess)
|
||||
XCTAssertEqual(res.successValue!, 15)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
self.append(reducer, items: 1, 2)
|
||||
|
||||
delay(0.1) {
|
||||
self.append(reducer, items: 3, 4, 5)
|
||||
}
|
||||
waitForExpectations(timeout: timeoutPeriod, handler: nil)
|
||||
}
|
||||
|
||||
func append(_ reducer: AsyncReducer<Int, Int>, items: Int...) {
|
||||
do {
|
||||
let _ = try reducer.append(items)
|
||||
} catch let error {
|
||||
XCTFail("Append failed with \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestError: MaybeErrorType {
|
||||
var description = "Error"
|
||||
}
|
||||
|
||||
private let serialQueue = DispatchQueue(label: "com.mozilla.test.serial", attributes: [])
|
||||
private let concurrentQueue = DispatchQueue(label: "com.mozilla.test.concurrent", attributes: DispatchQueue.Attributes.concurrent)
|
||||
|
||||
func delay(_ delay: Double, closure:@escaping () -> Void) {
|
||||
concurrentQueue.asyncAfter(
|
||||
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
|
||||
}
|
||||
|
||||
private func simpleAdder(_ a: Int, b: Int) -> Deferred<Maybe<Int>> {
|
||||
return deferMaybe(a + b)
|
||||
}
|
||||
|
||||
private func waitingFillingAdder(_ a: Int, b: Int) -> Deferred<Maybe<Int>> {
|
||||
let deferred = Deferred<Maybe<Int>>()
|
||||
delay(0.1) {
|
||||
deferred.fill(Maybe(success: a + b))
|
||||
}
|
||||
return deferred
|
||||
}
|
||||
|
||||
45
mobile/ios/SharedTests/AuthenticationKeychainInfoTests.swift
Normal file
45
mobile/ios/SharedTests/AuthenticationKeychainInfoTests.swift
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* 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
|
||||
import XCTest
|
||||
import SwiftKeychainWrapper
|
||||
|
||||
class AuthenticationKeychainInfoTests: XCTestCase {
|
||||
|
||||
func testEncodingAndDecoding() {
|
||||
let passcode = "1234"
|
||||
let authInfo = AuthenticationKeychainInfo(passcode: passcode)
|
||||
authInfo.updateRequiredPasscodeInterval(.fiveMinutes)
|
||||
authInfo.recordValidation()
|
||||
authInfo.recordFailedAttempt() // failed attempt should be 1
|
||||
authInfo.lockOutUser() //lock out a user so a lockoutInterval is set.
|
||||
authInfo.useTouchID = true
|
||||
|
||||
let savedInterval = authInfo.lockOutInterval
|
||||
let savedValidation = authInfo.lastPasscodeValidationInterval
|
||||
|
||||
KeychainWrapper.sharedAppContainerKeychain.setAuthenticationInfo(authInfo) //Save to disk
|
||||
let decodedAuthInfo = KeychainWrapper.sharedAppContainerKeychain.authenticationInfo()! //Fetch from disk
|
||||
|
||||
XCTAssertEqual(savedInterval, decodedAuthInfo.lockOutInterval)
|
||||
XCTAssertEqual(passcode, decodedAuthInfo.passcode)
|
||||
XCTAssertEqual(1, decodedAuthInfo.failedAttempts, "We performed a recordFailedAttempt. This should be 1.")
|
||||
XCTAssertTrue(decodedAuthInfo.useTouchID)
|
||||
XCTAssertEqual(savedValidation, decodedAuthInfo.lastPasscodeValidationInterval)
|
||||
XCTAssertEqual(PasscodeInterval.fiveMinutes, decodedAuthInfo.requiredPasscodeInterval)
|
||||
}
|
||||
|
||||
func testNilIntervalsArentZero() {
|
||||
let passcode = "1234"
|
||||
let authInfo = AuthenticationKeychainInfo(passcode: passcode)
|
||||
|
||||
KeychainWrapper.sharedAppContainerKeychain.setAuthenticationInfo(authInfo) //Save to disk
|
||||
let decodedAuthInfo = KeychainWrapper.sharedAppContainerKeychain.authenticationInfo()! //Fetch from disk
|
||||
|
||||
XCTAssertNil(decodedAuthInfo.lockOutInterval, "The lockoutInterval was never used. It should be nil")
|
||||
}
|
||||
|
||||
}
|
||||
127
mobile/ios/SharedTests/DeferredTests.swift
Normal file
127
mobile/ios/SharedTests/DeferredTests.swift
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* 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/. */
|
||||
|
||||
@testable import Shared
|
||||
import Deferred
|
||||
import XCTest
|
||||
|
||||
// Trivial test for using Deferred.
|
||||
|
||||
class DeferredTests: XCTestCase {
|
||||
func testDeferred() {
|
||||
let d = Deferred<Int>()
|
||||
XCTAssertNil(d.peek(), "Value not yet filled.")
|
||||
|
||||
let expectation = self.expectation(description: "Waiting on value.")
|
||||
d.upon({ x in
|
||||
expectation.fulfill()
|
||||
})
|
||||
|
||||
d.fill(5)
|
||||
waitForExpectations(timeout: 10) { (error) in
|
||||
XCTAssertNil(error, "\(error.debugDescription)")
|
||||
}
|
||||
|
||||
XCTAssertEqual(5, d.peek()!, "Value is filled.")
|
||||
}
|
||||
|
||||
func testMultipleUponBlocks() {
|
||||
let e1 = self.expectation(description: "First.")
|
||||
let e2 = self.expectation(description: "Second.")
|
||||
let d = Deferred<Int>()
|
||||
d.upon { x in
|
||||
XCTAssertEqual(x, 5)
|
||||
e1.fulfill()
|
||||
}
|
||||
d.upon { x in
|
||||
XCTAssertEqual(x, 5)
|
||||
e2.fulfill()
|
||||
}
|
||||
d.fill(5)
|
||||
waitForExpectations(timeout: 10, handler: nil)
|
||||
}
|
||||
|
||||
func testOperators() {
|
||||
let e1 = self.expectation(description: "First.")
|
||||
let e2 = self.expectation(description: "Second.")
|
||||
|
||||
let f1: () -> Deferred<Maybe<Int>> = {
|
||||
return deferMaybe(5)
|
||||
}
|
||||
|
||||
let f2: (_ x: Int) -> Deferred<Maybe<String>> = {
|
||||
if $0 == 5 {
|
||||
e1.fulfill()
|
||||
}
|
||||
return deferMaybe("Hello!")
|
||||
}
|
||||
|
||||
// Type signatures:
|
||||
let combined: () -> Deferred<Maybe<String>> = { f1() >>== f2 }
|
||||
let result: Deferred<Maybe<String>> = combined()
|
||||
|
||||
result.upon {
|
||||
XCTAssertEqual("Hello!", $0.successValue!)
|
||||
e2.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 10, handler: nil)
|
||||
}
|
||||
|
||||
func testPassAccumulate() {
|
||||
let leak = self.expectation(description: "deinit")
|
||||
|
||||
class TestClass {
|
||||
let end: XCTestExpectation
|
||||
init(e: XCTestExpectation) {
|
||||
end = e
|
||||
accumulate([self.aSimpleFunction]).upon { _ in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func aSimpleFunction() -> Success {
|
||||
return succeed()
|
||||
}
|
||||
deinit {
|
||||
end.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
var myclass: TestClass? = TestClass(e: leak)
|
||||
myclass = nil
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
|
||||
func testFailAccumulate() {
|
||||
let leak = self.expectation(description: "deinit")
|
||||
|
||||
class TestError: MaybeErrorType {
|
||||
var description = "Error"
|
||||
}
|
||||
|
||||
class TestClass {
|
||||
let end: XCTestExpectation
|
||||
init(e: XCTestExpectation) {
|
||||
end = e
|
||||
accumulate([self.aSimpleFunction]).upon { _ in
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func aSimpleFunction() -> Success {
|
||||
return Deferred(value: Maybe(failure: TestError()))
|
||||
}
|
||||
deinit {
|
||||
end.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
var myclass: TestClass? = TestClass(e: leak)
|
||||
myclass = nil
|
||||
waitForExpectations(timeout: 3, handler: nil)
|
||||
}
|
||||
|
||||
}
|
||||
147
mobile/ios/SharedTests/FeatureSwitchTests.swift
Normal file
147
mobile/ios/SharedTests/FeatureSwitchTests.swift
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* 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/. */
|
||||
|
||||
@testable import Shared
|
||||
import XCTest
|
||||
|
||||
class FeatureSwitchTests: XCTestCase {
|
||||
let buildChannel = AppConstants.BuildChannel
|
||||
|
||||
func testPersistent() {
|
||||
let featureSwitch = FeatureSwitch(named: "test-persistent-over-restarts", allowPercentage: 50, buildChannel: buildChannel)
|
||||
let prefs = MockProfilePrefs()
|
||||
var membership = featureSwitch.isMember(prefs)
|
||||
var changed = 0
|
||||
for _ in 0..<100 {
|
||||
if featureSwitch.isMember(prefs) != membership {
|
||||
membership = !membership
|
||||
changed += 1
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(changed, 0, "Users should get and keep the feature over restarts")
|
||||
}
|
||||
|
||||
func testConsistentWhenChangingPercentage() {
|
||||
let featureID = "test-persistent-over-releases"
|
||||
let prefs = MockProfilePrefs()
|
||||
let guardFeatureSwitch = FeatureSwitch(named: featureID, allowPercentage: 50, buildChannel: buildChannel)
|
||||
while guardFeatureSwitch.alwaysMembership(prefs) {
|
||||
guardFeatureSwitch.resetMembership(prefs)
|
||||
}
|
||||
var membership = false
|
||||
var changed = 0
|
||||
for percent in 0..<100 {
|
||||
let featureSwitch = FeatureSwitch(named: featureID, allowPercentage: percent, buildChannel: buildChannel)
|
||||
if featureSwitch.isMember(prefs) != membership {
|
||||
membership = !membership
|
||||
changed += 1
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(changed, 1, "Users should get and keep the feature if the feature is becoming successful")
|
||||
}
|
||||
|
||||
func testReallyConsistentWhenChangingPercentage() {
|
||||
for _ in 0..<1000 {
|
||||
testConsistentWhenChangingPercentage()
|
||||
}
|
||||
}
|
||||
|
||||
func testUserEnabled() {
|
||||
let prefs = MockProfilePrefs()
|
||||
let featureSwitch = FeatureSwitch(named: "test-user-enabled", allowPercentage: 0, buildChannel: buildChannel)
|
||||
XCTAssertFalse(featureSwitch.isMember(prefs), "The feature should be disabled")
|
||||
featureSwitch.setMembership(true, for: prefs) // enable the feature
|
||||
XCTAssertTrue(featureSwitch.isMember(prefs), "The feature should be enabled")
|
||||
featureSwitch.setMembership(false, for: prefs) // disable the feature
|
||||
XCTAssertFalse(featureSwitch.isMember(prefs), "The feature should be disabled again")
|
||||
}
|
||||
|
||||
func testForceDisabled() {
|
||||
let prefs = MockProfilePrefs()
|
||||
let featureSwitch = FeatureSwitch(named: "test-user-disabled", allowPercentage: 100, buildChannel: buildChannel)
|
||||
XCTAssertTrue(featureSwitch.isMember(prefs), "The feature should be enabled")
|
||||
featureSwitch.setMembership(false, for: prefs) // disable the feature
|
||||
XCTAssertFalse(featureSwitch.isMember(prefs), "The feature should be disabled again")
|
||||
}
|
||||
}
|
||||
|
||||
extension FeatureSwitchTests {
|
||||
func test0Percent() {
|
||||
let featureSwitch = FeatureSwitch(named: "test-never", allowPercentage: 0, buildChannel: buildChannel)
|
||||
testExactly(featureSwitch, expected: 0)
|
||||
testApprox(featureSwitch, expected: 0)
|
||||
}
|
||||
|
||||
func test100Percent() {
|
||||
let featureSwitch = FeatureSwitch(named: "test-always", allowPercentage: 100, buildChannel: buildChannel)
|
||||
testExactly(featureSwitch, expected: 100)
|
||||
testApprox(featureSwitch, expected: 100)
|
||||
}
|
||||
|
||||
func test50Percent() {
|
||||
let featureSwitch = FeatureSwitch(named: "test-half-the-population", allowPercentage: 50, buildChannel: buildChannel)
|
||||
testApprox(featureSwitch, expected: 50)
|
||||
}
|
||||
|
||||
func test30Percent() {
|
||||
let featureSwitch = FeatureSwitch(named: "test-30%-population", allowPercentage: 30, buildChannel: buildChannel)
|
||||
testApprox(featureSwitch, expected: 30)
|
||||
}
|
||||
|
||||
func testPerformance() {
|
||||
let featureSwitch = FeatureSwitch(named: "test-30%-population", allowPercentage: 30, buildChannel: buildChannel)
|
||||
let prefs = MockProfilePrefs()
|
||||
measure {
|
||||
for _ in 0..<1000 {
|
||||
_ = featureSwitch.isMember(prefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAppConstantsWin() {
|
||||
// simulate in release channel, but switched off in AppConstants.
|
||||
let featureFlaggedOff = FeatureSwitch(named: "test-release-flagged-off", false, allowPercentage: 100, buildChannel: buildChannel)
|
||||
testExactly(featureFlaggedOff, expected: 0)
|
||||
|
||||
// simulate in non-release channel, but switched on in AppConstants.
|
||||
let buildChannelAndFlaggedOn = FeatureSwitch(named: "test-flagged-on", true, allowPercentage: 0)
|
||||
testExactly(buildChannelAndFlaggedOn, expected: 100)
|
||||
|
||||
// simulate in non-release channel, but switched off in AppConstants.
|
||||
let buildChannelAndFlaggedOff = FeatureSwitch(named: "test-flagged-off", false, allowPercentage: 100)
|
||||
testExactly(buildChannelAndFlaggedOff, expected: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private extension FeatureSwitchTests {
|
||||
func sampleN(_ featureSwitch: FeatureSwitch, testCount: Int = 1000) -> Int {
|
||||
var count = 0
|
||||
for _ in 0..<testCount {
|
||||
let prefs = MockProfilePrefs()
|
||||
if featureSwitch.isMember(prefs) {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func testExactly(_ featureSwitch: FeatureSwitch, expected: Int) {
|
||||
let testCount = 1000
|
||||
let count = sampleN(featureSwitch, testCount: testCount)
|
||||
let normalizedExpectedCount = (testCount * expected) / 100
|
||||
XCTAssertEqual(count, normalizedExpectedCount)
|
||||
}
|
||||
|
||||
func testApprox(_ featureSwitch: FeatureSwitch, expected: Int, epsilon: Int = 2) {
|
||||
let testCount = 10000
|
||||
let count = sampleN(featureSwitch, testCount: testCount)
|
||||
let acceptableRange = Range(uncheckedBounds: (
|
||||
lower: testCount * (expected - epsilon) / 100,
|
||||
upper: testCount * (expected + epsilon) / 100))
|
||||
|
||||
XCTAssertTrue(acceptableRange.contains(count), "\(count) in \(acceptableRange)?")
|
||||
}
|
||||
}
|
||||
24
mobile/ios/SharedTests/HexExtensionsTests.swift
Normal file
24
mobile/ios/SharedTests/HexExtensionsTests.swift
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* 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 XCTest
|
||||
|
||||
class HexExtensionsTests: XCTestCase {
|
||||
func testHexEncodedString() {
|
||||
XCTAssertEqual("Hello, world!".data(using: String.Encoding.utf8)!.hexEncodedString, "48656c6c6f2c20776f726c6421")
|
||||
XCTAssertEqual("Hello, world!!".data(using: String.Encoding.utf8)!.hexEncodedString, "48656c6c6f2c20776f726c642121")
|
||||
}
|
||||
|
||||
func testHexDecodedData() {
|
||||
XCTAssertEqual("48656c6c6f2c20776f726c6421".hexDecodedData, "Hello, world!".data(using: String.Encoding.utf8))
|
||||
XCTAssertEqual("48656c6c6f2c20776f726c642121".hexDecodedData, "Hello, world!!".data(using: String.Encoding.utf8))
|
||||
}
|
||||
|
||||
func testHexDecodedDataWithInvalidInput() {
|
||||
XCTAssertEqual("".hexDecodedData, Data())
|
||||
XCTAssertEqual("cheese".hexDecodedData, Data())
|
||||
XCTAssertEqual("a".hexDecodedData, Data())
|
||||
}
|
||||
}
|
||||
24
mobile/ios/SharedTests/Info.plist
Normal file
24
mobile/ios/SharedTests/Info.plist
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>10.6</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/* 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 UIKit
|
||||
import XCTest
|
||||
@testable import Shared
|
||||
|
||||
class NSMutableAttributedStringExtensionsTests: XCTestCase {
|
||||
fileprivate func checkCharacterAtPosition(_ position: Int, isColored color: UIColor, inString string: NSAttributedString) -> Bool {
|
||||
let attributes = string.attributes(at: position, effectiveRange: nil)
|
||||
if let foregroundColor = attributes[NSForegroundColorAttributeName] as? UIColor {
|
||||
if foregroundColor == color {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func testColorsSubstring() {
|
||||
let substring = "bc"
|
||||
let example = NSMutableAttributedString(string: "abcd")
|
||||
example.colorSubstring(substring, withColor: UIColor.red)
|
||||
|
||||
XCTAssertFalse(checkCharacterAtPosition(0, isColored: UIColor.red, inString: example))
|
||||
for position in 1..<3 {
|
||||
XCTAssertTrue(checkCharacterAtPosition(position, isColored: UIColor.red, inString: example))
|
||||
}
|
||||
XCTAssertFalse(checkCharacterAtPosition(3, isColored: UIColor.red, inString: example))
|
||||
}
|
||||
|
||||
func testDoesNothingWithEmptySubstring() {
|
||||
let substring = ""
|
||||
let example = NSMutableAttributedString(string: "abcd")
|
||||
example.colorSubstring(substring, withColor: UIColor.red)
|
||||
for position in 0..<example.string.characters.count {
|
||||
XCTAssertFalse(checkCharacterAtPosition(position, isColored: UIColor.red, inString: example))
|
||||
}
|
||||
}
|
||||
|
||||
func testDoesNothingWhenSubstringNotFound() {
|
||||
let substring = "yyz"
|
||||
let example = NSMutableAttributedString(string: "abcd")
|
||||
example.colorSubstring(substring, withColor: UIColor.red)
|
||||
for position in 0..<example.string.characters.count {
|
||||
XCTAssertFalse(checkCharacterAtPosition(position, isColored: UIColor.red, inString: example))
|
||||
}
|
||||
}
|
||||
}
|
||||
497
mobile/ios/SharedTests/NSURLExtensionsTests.swift
Normal file
497
mobile/ios/SharedTests/NSURLExtensionsTests.swift
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
/* 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 UIKit
|
||||
import XCTest
|
||||
@testable import Shared
|
||||
|
||||
class NSURLExtensionsTests: XCTestCase {
|
||||
func testRemovesHTTPFromURL() {
|
||||
let url = URL(string: "http://google.com")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "google.com")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testRemovesHTTPAndTrailingSlashFromURL() {
|
||||
let url = URL(string: "http://google.com/")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "google.com")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testRemovesHTTPButNotTrailingSlashFromURL() {
|
||||
let url = URL(string: "http://google.com/foo/")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "google.com/foo/")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testKeepsHTTPSInURL() {
|
||||
let url = URL(string: "https://google.com")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "https://google.com")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testKeepsHTTPSAndRemovesTrailingSlashInURL() {
|
||||
let url = URL(string: "https://google.com/")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "https://google.com")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testKeepsHTTPSAndTrailingSlashInURL() {
|
||||
let url = URL(string: "https://google.com/foo/")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "https://google.com/foo/")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func testKeepsAboutSchemeInURL() {
|
||||
let url = URL(string: "about:home")
|
||||
if let actual = url?.absoluteDisplayString {
|
||||
XCTAssertEqual(actual, "about:home")
|
||||
} else {
|
||||
XCTFail("Actual url is nil")
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: Public Suffix
|
||||
func testNormalBaseDomainWithSingleSubdomain() {
|
||||
// TLD Entry: co.uk
|
||||
let url = "http://a.bbc.co.uk".asURL!
|
||||
let expected = url.publicSuffix!
|
||||
XCTAssertEqual("co.uk", expected)
|
||||
}
|
||||
|
||||
func testCanadaComputers() {
|
||||
let url = "http://m.canadacomputers.com".asURL!
|
||||
let actual = url.baseDomain!
|
||||
XCTAssertEqual("canadacomputers.com", actual)
|
||||
}
|
||||
|
||||
func testMultipleSuffixesInsideURL() {
|
||||
let url = "http://com:org@m.canadacomputers.co.uk".asURL!
|
||||
let actual = url.baseDomain!
|
||||
XCTAssertEqual("canadacomputers.co.uk", actual)
|
||||
}
|
||||
|
||||
func testNormalBaseDomainWithManySubdomains() {
|
||||
// TLD Entry: co.uk
|
||||
let url = "http://a.b.c.d.bbc.co.uk".asURL!
|
||||
let expected = url.publicSuffix!
|
||||
XCTAssertEqual("co.uk", expected)
|
||||
}
|
||||
|
||||
func testWildCardDomainWithSingleSubdomain() {
|
||||
// TLD Entry: *.kawasaki.jp
|
||||
let url = "http://a.kawasaki.jp".asURL!
|
||||
let expected = url.publicSuffix!
|
||||
XCTAssertEqual("a.kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
func testWildCardDomainWithManySubdomains() {
|
||||
// TLD Entry: *.kawasaki.jp
|
||||
let url = "http://a.b.c.d.kawasaki.jp".asURL!
|
||||
let expected = url.publicSuffix!
|
||||
XCTAssertEqual("d.kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
func testExceptionDomain() {
|
||||
// TLD Entry: !city.kawasaki.jp
|
||||
let url = "http://city.kawasaki.jp".asURL!
|
||||
let expected = url.publicSuffix!
|
||||
XCTAssertEqual("kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
//MARK: Base Domain
|
||||
func testNormalBaseSubdomain() {
|
||||
// TLD Entry: co.uk
|
||||
let url = "http://bbc.co.uk".asURL!
|
||||
let expected = url.baseDomain!
|
||||
XCTAssertEqual("bbc.co.uk", expected)
|
||||
}
|
||||
|
||||
func testNormalBaseSubdomainWithAdditionalSubdomain() {
|
||||
// TLD Entry: co.uk
|
||||
let url = "http://a.bbc.co.uk".asURL!
|
||||
let expected = url.baseDomain!
|
||||
XCTAssertEqual("bbc.co.uk", expected)
|
||||
}
|
||||
|
||||
func testBaseDomainForWildcardDomain() {
|
||||
// TLD Entry: *.kawasaki.jp
|
||||
let url = "http://a.b.kawasaki.jp".asURL!
|
||||
let expected = url.baseDomain!
|
||||
XCTAssertEqual("a.b.kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
func testBaseDomainForWildcardDomainWithAdditionalSubdomain() {
|
||||
// TLD Entry: *.kawasaki.jp
|
||||
let url = "http://a.b.c.kawasaki.jp".asURL!
|
||||
let expected = url.baseDomain!
|
||||
XCTAssertEqual("b.c.kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
func testBaseDomainForExceptionDomain() {
|
||||
// TLD Entry: !city.kawasaki.jp
|
||||
let url = "http://city.kawasaki.jp".asURL!
|
||||
let expected = url.baseDomain!
|
||||
XCTAssertEqual("city.kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
func testBaseDomainForExceptionDomainWithAdditionalSubdomain() {
|
||||
// TLD Entry: !city.kawasaki.jp
|
||||
let url = "http://a.city.kawasaki.jp".asURL!
|
||||
let expected = url.baseDomain!
|
||||
XCTAssertEqual("city.kawasaki.jp", expected)
|
||||
}
|
||||
|
||||
func testBugzillaURLDomain() {
|
||||
let url = "https://bugzilla.mozilla.org/enter_bug.cgi?format=guided#h=dupes|Data%20%26%20BI%20Services%20Team|"
|
||||
let nsURL = url.asURL
|
||||
XCTAssertNotNil(nsURL, "URL parses.")
|
||||
|
||||
let host = nsURL!.normalizedHost
|
||||
XCTAssertEqual(host!, "bugzilla.mozilla.org")
|
||||
XCTAssertEqual(nsURL!.fragment!, "h=dupes%7CData%20%26%20BI%20Services%20Team%7C")
|
||||
}
|
||||
|
||||
func testIPv6Domain() {
|
||||
let url = "http://[::1]/foo/bar".asURL!
|
||||
XCTAssertTrue(url.isIPv6)
|
||||
XCTAssertNil(url.baseDomain)
|
||||
XCTAssertEqual(url.normalizedHost!, "[::1]")
|
||||
}
|
||||
|
||||
func testisAboutHomeURL() {
|
||||
let goodurls = [
|
||||
"http://localhost:1234/about/home/#panel=0",
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//localhost%3A6571/about/home/%23panel%3D1",
|
||||
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"http://localhost:6571/sessionrestore.html",
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//mozilla.com",
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//mozilla.com/about/home/%23panel%3D1",
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isAboutHomeURL, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isAboutHomeURL, $0) }
|
||||
}
|
||||
|
||||
func testisAboutURL() {
|
||||
let goodurls = [
|
||||
"http://localhost:1234/about/home/#panel=0",
|
||||
"http://localhost:1234/about/firefox"
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"http://localhost:6571/sessionrestore.html",
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//mozilla.com",
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//mozilla.com/about/home/%23panel%3D1",
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isAboutURL, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isAboutURL, $0) }
|
||||
}
|
||||
|
||||
func testisErrorPage() {
|
||||
let goodurls = [
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//mozilla.com",
|
||||
"http://localhost:6572/errors/error.html?url=blah",
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"http://localhost:6571/sessionrestore.html",
|
||||
"http://localhost:1234/about/home/#panel=0"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isErrorPageURL, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isErrorPageURL, $0) }
|
||||
}
|
||||
|
||||
func testoriginalURLFromErrorURL() {
|
||||
let goodurls = [
|
||||
("http://localhost:6571/errors/error.html?url=http%3A//mozilla.com", URL(string: "http://mozilla.com")),
|
||||
("http://localhost:6571/errors/error.html?url=http%3A//localhost%3A6571/about/home/%23panel%3D1", URL(string: "http://localhost:6571/about/home/#panel=1")),
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"http://localhost:6571/sessionrestore.html",
|
||||
"http://localhost:1234/about/home/#panel=0",
|
||||
"http://localhost:6571/errors/error.html"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertEqual(URL(string:$0.0)!.originalURLFromErrorURL, $0.1) }
|
||||
badurls.forEach { XCTAssertNil(URL(string:$0)!.originalURLFromErrorURL) }
|
||||
}
|
||||
|
||||
func testisReaderModeURL() {
|
||||
let goodurls = [
|
||||
"http://localhost:6571/reader-mode/page",
|
||||
"http://localhost:6571/reader-mode/page?url=https%3A%2F%2Fen%2Em%2Ewikipedia%2Eorg%2Fwiki%2FMain%5FPage",
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"http://localhost:6571/sessionrestore.html",
|
||||
"http://localhost:1234/about/home/#panel=0"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isReaderModeURL, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isReaderModeURL, $0) }
|
||||
}
|
||||
|
||||
func testdecodeReaderModeURL() {
|
||||
let goodurls = [
|
||||
("http://localhost:6571/reader-mode/page?url=https%3A%2F%2Fen%2Em%2Ewikipedia%2Eorg%2Fwiki%2FMain%5FPage", URL(string: "https://en.m.wikipedia.org/wiki/Main_Page"))
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"http://localhost:6571/sessionrestore.html",
|
||||
"http://localhost:1234/about/home/#panel=0",
|
||||
"http://localhost:6571/reader-mode/page"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertEqual(URL(string:$0.0)!.decodeReaderModeURL, $0.1) }
|
||||
badurls.forEach { XCTAssertNil(URL(string:$0)!.decodeReaderModeURL, $0) } }
|
||||
|
||||
func testencodeReaderModeURL() {
|
||||
let ReaderURL = "http://localhost:6571/reader-mode/page"
|
||||
let goodurls = [
|
||||
("https://en.m.wikipedia.org/wiki/Main_Page", URL(string: "http://localhost:6571/reader-mode/page?url=https%3A%2F%2Fen%2Em%2Ewikipedia%2Eorg%2Fwiki%2FMain%5FPage"))
|
||||
]
|
||||
goodurls.forEach { XCTAssertEqual(URL(string:$0.0)!.encodeReaderModeURL(ReaderURL), $0.1) }
|
||||
}
|
||||
|
||||
func testhavingRemovedAuthorisationComponents() {
|
||||
let goodurls = [
|
||||
("https://Aladdin:OpenSesame@www.example.com/index.html", "https://www.example.com/index.html"),
|
||||
("https://www.example.com/noauth", "https://www.example.com/noauth")
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertEqual(URL(string:$0.0)!.havingRemovedAuthorisationComponents().absoluteString, $0.1) }
|
||||
}
|
||||
|
||||
func testschemeIsValid() {
|
||||
let goodurls = [
|
||||
"http://localhost:6571/reader-mode/page",
|
||||
"https://google.com",
|
||||
"tel:6044044004"
|
||||
]
|
||||
let badurls = [
|
||||
"blah://google.com",
|
||||
"hax://localhost:6571/sessionrestore.html",
|
||||
"leet://codes.com"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.schemeIsValid, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.schemeIsValid, $0) }
|
||||
}
|
||||
|
||||
func testIsLocalUtility() {
|
||||
let goodurls = [
|
||||
"http://localhost:6571/reader-mode/page",
|
||||
"http://LOCALhost:6571/about/sessionrestore.html",
|
||||
"http://127.0.0.1:6571/errors/error.html"
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"tel:6044044004",
|
||||
"hax://localhost:6571/testhomepage",
|
||||
"http://127.0.0.1:6571/test/atesthomepage.html"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isLocalUtility, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isLocalUtility, $0) }
|
||||
}
|
||||
|
||||
func testisLocal() {
|
||||
let goodurls = [
|
||||
"http://localhost:6571/reader-mode/page",
|
||||
"http://LOCALhost:6571/sessionrestore.html",
|
||||
"http://127.0.0.1:6571/sessionrestore.html",
|
||||
"http://:6571/sessionrestore.html"
|
||||
|
||||
]
|
||||
let badurls = [
|
||||
"http://google.com",
|
||||
"tel:6044044004",
|
||||
"hax://localhost:6571/about"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isLocal, $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isLocal, $0) }
|
||||
}
|
||||
|
||||
func testisWebPage() {
|
||||
let goodurls = [
|
||||
"http://localhost:6571/reader-mode/page",
|
||||
"https://127.0.0.1:6571/sessionrestore.html",
|
||||
"data://:6571/sessionrestore.html"
|
||||
|
||||
]
|
||||
let badurls = [
|
||||
"about://google.com",
|
||||
"tel:6044044004",
|
||||
"hax://localhost:6571/about"
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertTrue(URL(string:$0)!.isWebPage(), $0) }
|
||||
badurls.forEach { XCTAssertFalse(URL(string:$0)!.isWebPage(), $0) }
|
||||
}
|
||||
|
||||
func testdomainURL() {
|
||||
let urls = [
|
||||
("https://www.example.com/index.html", "https://example.com/"),
|
||||
("https://mail.example.com/index.html", "https://mail.example.com/"),
|
||||
("https://mail.example.co.uk/index.html", "https://mail.example.co.uk/"),
|
||||
]
|
||||
urls.forEach { XCTAssertEqual(URL(string:$0.0)!.domainURL.absoluteString, $0.1) }
|
||||
}
|
||||
|
||||
func testdisplayURL() {
|
||||
let goodurls = [
|
||||
("http://localhost:6571/reader-mode/page?url=https%3A%2F%2Fen%2Em%2Ewikipedia%2Eorg%2Fwiki%2F", "https://en.m.wikipedia.org/wiki/"),
|
||||
("http://user:pass@localhost:6571/errors/error.html?url=http%3A//mozilla.com", "http://mozilla.com"),
|
||||
("http://user:pass@localhost:6571/errors/error.html?url=http%3A//mozilla.com", "http://mozilla.com"),
|
||||
("http://localhost:6571/errors/error.html?url=http%3A%2F%2Flocalhost%3A6571%2Freader-mode%2Fpage%3Furl%3Dhttps%253A%252F%252Fen%252Em%252Ewikipedia%252Eorg%252Fwiki%252F", "https://en.m.wikipedia.org/wiki/"),
|
||||
("https://mail.example.co.uk/index.html", "https://mail.example.co.uk/index.html"),
|
||||
]
|
||||
let badurls = [
|
||||
"http://localhost:6571/errors/error.html?url=http%3A//localhost%3A6571/about/home/%23panel%3D1",
|
||||
"http://localhost:6571/errors/error.html",
|
||||
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertEqual(URL(string:$0.0)!.displayURL?.absoluteString, $0.1) }
|
||||
badurls.forEach { XCTAssertNil(URL(string:$0)!.displayURL) }
|
||||
}
|
||||
|
||||
func testnormalizedHostAndPath() {
|
||||
let goodurls = [
|
||||
("https://www.example.com/index.html", "example.com/index.html"),
|
||||
("https://mail.example.com/index.html", "mail.example.com/index.html"),
|
||||
("https://mail.example.co.uk/index.html", "mail.example.co.uk/index.html"),
|
||||
("https://m.example.co.uk/index.html", "example.co.uk/index.html")
|
||||
]
|
||||
let badurls = [
|
||||
"http:///errors/error.html",
|
||||
"http://:6571/about/home",
|
||||
]
|
||||
|
||||
goodurls.forEach { XCTAssertEqual(URL(string:$0.0)!.normalizedHostAndPath, $0.1) }
|
||||
badurls.forEach { XCTAssertNil(URL(string:$0)!.normalizedHostAndPath) }
|
||||
}
|
||||
|
||||
func testhostSLD() {
|
||||
let urls = [
|
||||
("https://www.example.com/index.html", "example"),
|
||||
("https://m.foo.com/bar/baz?noo=abc#123", "foo"),
|
||||
("https://user:pass@m.foo.com/bar/baz?noo=abc#123", "foo"),
|
||||
]
|
||||
urls.forEach { XCTAssertEqual(URL(string:$0.0)!.hostSLD, $0.1) }
|
||||
}
|
||||
|
||||
func testorigin() {
|
||||
let urls = [
|
||||
("https://www.example.com/index.html", "https://www.example.com"),
|
||||
("https://user:pass@m.foo.com/bar/baz?noo=abc#123", "https://m.foo.com"),
|
||||
]
|
||||
|
||||
let badurls = [
|
||||
"data://google.com"
|
||||
]
|
||||
urls.forEach { XCTAssertEqual(URL(string:$0.0)!.origin, $0.1) }
|
||||
badurls.forEach { XCTAssertNil(URL(string:$0)!.origin) }
|
||||
}
|
||||
|
||||
func testhostPort() {
|
||||
let urls = [
|
||||
("https://www.example.com", "www.example.com"),
|
||||
("https://user:pass@www.example.com", "www.example.com"),
|
||||
("http://localhost:6000/blah", "localhost:6000")
|
||||
]
|
||||
|
||||
let badurls = [
|
||||
"blah",
|
||||
"http://"
|
||||
]
|
||||
urls.forEach { XCTAssertEqual(URL(string:$0.0)!.hostPort, $0.1) }
|
||||
badurls.forEach { XCTAssertNil(URL(string:$0)!.hostPort) }
|
||||
}
|
||||
|
||||
func testgetQuery() {
|
||||
let url = URL(string: "http://example.com/path?a=1&b=2&c=3")!
|
||||
let params = ["a": "1", "b": "2", "c": "3"]
|
||||
|
||||
let urlParams = url.getQuery()
|
||||
params.forEach { XCTAssertEqual(urlParams[$0], $1, "The values in params should be the same in urlParams") }
|
||||
}
|
||||
|
||||
func testwithQueryParams() {
|
||||
let url = URL(string: "http://example.com/path")!
|
||||
let params = ["a": "1", "b": "2", "c": "3"]
|
||||
|
||||
let newURL = url.withQueryParams(params.map { URLQueryItem(name: $0, value: $1) })
|
||||
|
||||
//make sure the new url has all the right params.
|
||||
let newURLParams = newURL.getQuery()
|
||||
params.forEach { XCTAssertEqual(newURLParams[$0], $1, "The values in params should be the same in newURLParams") }
|
||||
}
|
||||
|
||||
func testWithQueryParam() {
|
||||
let urlA = URL(string: "http://foo.com/bar/")!
|
||||
let urlB = URL(string: "http://bar.com/noo")!
|
||||
let urlC = urlA.withQueryParam("ppp", value: "123")
|
||||
let urlD = urlB.withQueryParam("qqq", value: "123")
|
||||
let urlE = urlC.withQueryParam("rrr", value: "aaa")
|
||||
|
||||
XCTAssertEqual("http://foo.com/bar/?ppp=123", urlC.absoluteString)
|
||||
XCTAssertEqual("http://bar.com/noo?qqq=123", urlD.absoluteString)
|
||||
XCTAssertEqual("http://foo.com/bar/?ppp=123&rrr=aaa", urlE.absoluteString)
|
||||
}
|
||||
|
||||
func testHidingFromDataDetectors() {
|
||||
guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
|
||||
let urls = ["https://example.com", "example.com", "http://example.com"]
|
||||
for u in urls {
|
||||
let url = URL(string: u)!
|
||||
|
||||
let original = url.absoluteDisplayString
|
||||
let matches = detector.matches(in: original, options: [], range: NSMakeRange(0, original.count))
|
||||
guard matches.count > 0 else {
|
||||
print("\(url) doesn't match as a URL")
|
||||
continue
|
||||
}
|
||||
|
||||
let modified = url.absoluteDisplayExternalString
|
||||
XCTAssertNotEqual(original, modified)
|
||||
|
||||
let newMatches = detector.matches(in: modified, options: [], range: NSMakeRange(0, modified.count))
|
||||
|
||||
XCTAssertEqual(0, newMatches.count, "\(modified) is not a valid URL")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
18
mobile/ios/SharedTests/ResultTests.swift
Normal file
18
mobile/ios/SharedTests/ResultTests.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* 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 Shared
|
||||
import XCTest
|
||||
|
||||
// Trivial test for using Result.
|
||||
class ResultTests: XCTestCase {
|
||||
func testResult() {
|
||||
let r = Maybe<Int>(success: 5)
|
||||
if let i = r.successValue {
|
||||
XCTAssertEqual(5, i)
|
||||
} else {
|
||||
XCTFail("Expected success.")
|
||||
}
|
||||
}
|
||||
}
|
||||
119
mobile/ios/SharedTests/RollingFileLoggerTests.swift
Normal file
119
mobile/ios/SharedTests/RollingFileLoggerTests.swift
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/* 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 XCTest
|
||||
import XCGLogger
|
||||
@testable import Shared
|
||||
|
||||
class RollingFileLoggerTests: XCTestCase {
|
||||
var logger: RollingFileLogger!
|
||||
var logDir: String = ""
|
||||
var sizeLimit: Int = 5000
|
||||
|
||||
fileprivate lazy var formatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyyMMdd'T'HHmmssZ"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
logDir = (NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!) + "/Logs"
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: logDir, withIntermediateDirectories: false, attributes: nil)
|
||||
} catch _ {
|
||||
}
|
||||
logger = RollingFileLogger(filenameRoot: "test", logDirectoryPath: logDir, sizeLimit: Int64(sizeLimit))
|
||||
}
|
||||
|
||||
func testNewLogCreatesLogFileWithTimestamp() {
|
||||
let date = Date()
|
||||
let expected = "test.\(formatter.string(from: date)).log"
|
||||
let expectedPath = "\(logDir)/\(expected)"
|
||||
logger.newLogWithDate(date)
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: expectedPath), "Log file should exist")
|
||||
|
||||
let testMessage = "Logging some text"
|
||||
logger.info(testMessage)
|
||||
let logData = try? Data(contentsOf: URL(fileURLWithPath: expectedPath))
|
||||
XCTAssertNotNil(logData, "Log data should not be nil")
|
||||
let logString = NSString(data: logData!, encoding: String.Encoding.utf8.rawValue)
|
||||
XCTAssertTrue(logString!.contains(testMessage), "Log should contain our test message that we wrote")
|
||||
}
|
||||
|
||||
func testNewLogDeletesPreviousLogIfItsTooLarge() {
|
||||
let manager = FileManager.default
|
||||
let dirURL = URL(fileURLWithPath: logDir)
|
||||
let prefix = "test"
|
||||
let expectedPath = createNewLogFileWithSize(sizeLimit + 1)
|
||||
|
||||
let directorySize = try! manager.getAllocatedSizeOfDirectoryAtURL(dirURL, forFilesPrefixedWith: prefix)
|
||||
|
||||
// Pre-condition: Folder needs to be larger than the size limit
|
||||
XCTAssertGreaterThan(directorySize, Int64(sizeLimit), "Log folder should be larger than size limit")
|
||||
|
||||
let exceedsSmaller = try! manager.allocatedSizeOfDirectoryAtURL(dirURL, forFilesPrefixedWith: prefix, isLargerThanBytes: directorySize - 1)
|
||||
let doesNotExceedLarger = try! manager.allocatedSizeOfDirectoryAtURL(dirURL, forFilesPrefixedWith: prefix, isLargerThanBytes: Int64(sizeLimit + 2))
|
||||
XCTAssertTrue(exceedsSmaller)
|
||||
XCTAssertTrue(doesNotExceedLarger)
|
||||
|
||||
let newDate = Date().addingTimeInterval(60*60) // Create a log file using a date an hour ahead
|
||||
let newExpected = "\(prefix).\(formatter.string(from: newDate)).log"
|
||||
let newExpectedPath = "\(logDir)/\(newExpected)"
|
||||
logger.newLogWithDate(newDate)
|
||||
|
||||
XCTAssertTrue(manager.fileExists(atPath: newExpectedPath), "New log file should exist")
|
||||
XCTAssertTrue(manager.fileExists(atPath: expectedPath), "Old log file exists until pruned")
|
||||
logger.deleteOldLogsDownToSizeLimit()
|
||||
XCTAssertFalse(manager.fileExists(atPath: expectedPath), "Old log file should NOT exist")
|
||||
}
|
||||
|
||||
func testNewLogDeletesOldestLogFileToMakeRoomForNewFile() {
|
||||
let manager = FileManager.default
|
||||
let dirURL = URL(fileURLWithPath: logDir)
|
||||
let prefix = "test"
|
||||
|
||||
// Create 5 log files with spread out over 5 hours and reorder paths so oldest is first
|
||||
let logFilePaths = [0, 1, 2, 3, 4].map { self.createNewLogFileWithSize(200, withDate: Date().addingTimeInterval(60 * 60 * $0)) }
|
||||
.sorted { $0 < $1 }
|
||||
|
||||
let directorySize = try! manager.getAllocatedSizeOfDirectoryAtURL(dirURL, forFilesPrefixedWith: prefix)
|
||||
|
||||
// Pre-condition: Folder needs to be larger than the size limit
|
||||
XCTAssertGreaterThan(directorySize, Int64(sizeLimit), "Log folder should be larger than size limit")
|
||||
|
||||
let newDate = Date().addingTimeInterval(60*60*5) // Create a log file using a date an hour ahead
|
||||
let newExpected = "\(prefix).\(formatter.string(from: newDate)).log"
|
||||
let newExpectedPath = "\(logDir)/\(newExpected)"
|
||||
logger.newLogWithDate(newDate)
|
||||
|
||||
XCTAssertTrue(manager.fileExists(atPath: newExpectedPath), "New log file should exist")
|
||||
XCTAssertTrue(manager.fileExists(atPath: logFilePaths.first!), "Old log file exists until pruned")
|
||||
logger.deleteOldLogsDownToSizeLimit()
|
||||
XCTAssertFalse(manager.fileExists(atPath: logFilePaths.first!), "Oldest log file should NOT exist")
|
||||
}
|
||||
|
||||
/**
|
||||
Create a log file using the test logger and returns the path to that log file
|
||||
|
||||
- parameter size: Size to make the log file
|
||||
|
||||
- returns: Path to log file
|
||||
*/
|
||||
fileprivate func createNewLogFileWithSize(_ size: Int, withDate date: Date = Date()) -> String {
|
||||
let expected = "test.\(formatter.string(from: date)).log"
|
||||
let expectedPath = "\(logDir)/\(expected)"
|
||||
logger.newLogWithDate(date)
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: expectedPath), "Log file should exist")
|
||||
|
||||
let logFileHandle = FileHandle(forWritingAtPath: expectedPath)
|
||||
XCTAssertNotNil(logFileHandle, "File should exist")
|
||||
let garbageBytes = malloc(size)
|
||||
let blankData = Data(bytes: garbageBytes!, count: size)
|
||||
logFileHandle!.write(blankData)
|
||||
logFileHandle!.closeFile()
|
||||
return expectedPath
|
||||
}
|
||||
}
|
||||
17
mobile/ios/SharedTests/SupportUtilsTests.swift
Normal file
17
mobile/ios/SharedTests/SupportUtilsTests.swift
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* 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
|
||||
import XCTest
|
||||
|
||||
class SupportUtilsTests: XCTestCase {
|
||||
func testURLForTopic() {
|
||||
let appVersion = AppInfo.appVersion
|
||||
let languageIdentifier = Locale.preferredLanguages.first!
|
||||
XCTAssertEqual(SupportUtils.URLForTopic("Bacon")?.absoluteString, "https://support.mozilla.org/1/mobile/\(appVersion)/iOS/\(languageIdentifier)/Bacon")
|
||||
XCTAssertEqual(SupportUtils.URLForTopic("Cheese & Crackers")?.absoluteString, "https://support.mozilla.org/1/mobile/\(appVersion)/iOS/\(languageIdentifier)/Cheese%20&%20Crackers")
|
||||
XCTAssertEqual(SupportUtils.URLForTopic("Möbelträgerfüße")?.absoluteString, "https://support.mozilla.org/1/mobile/\(appVersion)/iOS/\(languageIdentifier)/M%C3%B6beltr%C3%A4gerf%C3%BC%C3%9Fe")
|
||||
}
|
||||
}
|
||||
108
mobile/ios/SharedTests/UtilsTests.swift
Normal file
108
mobile/ios/SharedTests/UtilsTests.swift
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* 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
|
||||
import XCTest
|
||||
|
||||
/**
|
||||
* Test for our own utils.
|
||||
*/
|
||||
class UtilsTests: XCTestCase {
|
||||
func testMapUtils() {
|
||||
let m: [String: Int] = ["foo": 123, "bar": 456]
|
||||
let f: (Int) -> Int? = { v in
|
||||
return (v > 200) ? 999 : nil
|
||||
}
|
||||
|
||||
let o = mapValues(m, f: f)
|
||||
XCTAssertEqual(2, o.count)
|
||||
XCTAssertTrue(o["foo"]! == nil)
|
||||
XCTAssertTrue(o["bar"]! == 999)
|
||||
|
||||
let filtered = optFilter(o)
|
||||
XCTAssertEqual(1, filtered.count)
|
||||
XCTAssertTrue(filtered["bar"] == 999)
|
||||
}
|
||||
|
||||
func testOptFilter() {
|
||||
let a: [Int?] = [nil, 1, nil, 2, 3, 4]
|
||||
let b = optFilter(a)
|
||||
XCTAssertEqual(4, b.count)
|
||||
XCTAssertEqual([1, 2, 3, 4], b)
|
||||
}
|
||||
|
||||
func testOptArrayEqual() {
|
||||
let x: [String] = ["a", "b", "c"]
|
||||
let y: [String]? = ["a", "b", "c"]
|
||||
let z: [String]? = nil
|
||||
|
||||
XCTAssertTrue(optArrayEqual(x, rhs: y))
|
||||
XCTAssertTrue(optArrayEqual(x, rhs: x))
|
||||
XCTAssertTrue(optArrayEqual(y, rhs: y))
|
||||
XCTAssertTrue(optArrayEqual(z, rhs: z))
|
||||
XCTAssertFalse(optArrayEqual(x, rhs: z))
|
||||
XCTAssertFalse(optArrayEqual(z, rhs: y))
|
||||
}
|
||||
|
||||
func testChunk() {
|
||||
let examples: [([Int], Int, [[Int]])] = [
|
||||
([], 2, []),
|
||||
([1, 2], 0, [[1], [2]]),
|
||||
([1, 2], 1, [[1], [2]]),
|
||||
([1, 2, 3], 2, [[1, 2], [3]]),
|
||||
([1, 2], 3, [[1, 2]]),
|
||||
([1, 2, 3], 1, [[1], [2], [3]]),
|
||||
]
|
||||
for (arr, by, expected) in examples {
|
||||
// Turn the ArraySlices back into Arrays for comparison.
|
||||
let actual = chunk(arr as [Int], by: by).map { Array($0) }
|
||||
XCTAssertEqual(expected as NSArray, actual as NSArray) //wtf. why is XCTAssert being so weeird
|
||||
}
|
||||
}
|
||||
|
||||
func testChunkCollection() {
|
||||
let examples: [([Int], Int, [[Int]])] = [
|
||||
([], 2, []),
|
||||
([1, 2], 0, [[1], [2]]),
|
||||
([1, 2], 1, [[1], [2]]),
|
||||
([1, 2, 3], 2, [[1, 2], [3]]),
|
||||
([1, 2], 3, [[1, 2]]),
|
||||
([1, 2, 3], 1, [[1], [2], [3]]),
|
||||
]
|
||||
for (arr, by, expected) in examples {
|
||||
let actual = chunkCollection(arr, by: by) { xs in [xs] }
|
||||
XCTAssertEqual(expected as NSArray, actual as NSArray)
|
||||
}
|
||||
}
|
||||
|
||||
func testParseTimestamps() {
|
||||
let millis = "1492316843992" // Firefox for iOS produced millisecond timestamps. Oops.
|
||||
let decimal = "1492316843.99"
|
||||
let truncated = "1492316843"
|
||||
let huge = "1844674407370955161512"
|
||||
|
||||
XCTAssertNil(decimalSecondsStringToTimestamp(""))
|
||||
XCTAssertNil(decimalSecondsStringToTimestamp(huge))
|
||||
XCTAssertNil(decimalSecondsStringToTimestamp("foo"))
|
||||
|
||||
XCTAssertNil(someKindOfTimestampStringToTimestamp(""))
|
||||
XCTAssertNil(someKindOfTimestampStringToTimestamp(huge))
|
||||
XCTAssertNil(someKindOfTimestampStringToTimestamp("foo"))
|
||||
|
||||
let ts1: Timestamp = 1492316843990
|
||||
XCTAssertEqual(decimalSecondsStringToTimestamp(decimal) ?? 0, ts1)
|
||||
XCTAssertEqual(someKindOfTimestampStringToTimestamp(decimal) ?? 0, ts1)
|
||||
|
||||
let ts2: Timestamp = 1492316843000
|
||||
XCTAssertEqual(decimalSecondsStringToTimestamp(truncated) ?? 0, ts2)
|
||||
XCTAssertEqual(someKindOfTimestampStringToTimestamp(truncated) ?? 0, ts2)
|
||||
|
||||
let ts3: Timestamp = 1492316843992000
|
||||
XCTAssertEqual(decimalSecondsStringToTimestamp(millis) ?? 0, ts3) // Oops.
|
||||
|
||||
let ts4: Timestamp = 1492316843992
|
||||
XCTAssertEqual(someKindOfTimestampStringToTimestamp(millis) ?? 0, ts4)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue