Dactyloidae iOS initial commit

This commit is contained in:
wuggy 2026-06-26 21:04:09 -07:00
commit 7154a0497e
2123 changed files with 197052 additions and 0 deletions

View 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))!)
}
}

View 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
}

View 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")
}
}

View 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)
}
}

View 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)?")
}
}

View 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())
}
}

View 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>

View file

@ -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))
}
}
}

View 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")
}
}
}

View 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.")
}
}
}

View 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
}
}

View 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")
}
}

View 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)
}
}