はじめに
Appleは2015年春、ResearchKitのソースコードをGitHubで公開しPRを受け入れて開発を進めており、Swiftも例に漏れずオープンソース化としてGitHubに公開されることはみなさんの想定内ではないかと思います。しかし、今月Swift.orgが公開され、Swiftに関するパッケージマネージャや数々のドキュメントも公開されることで、みなさんも深く興味を持たれていることでしょう。
この記事では、実際にSwift FoundationのXCTestをXcodeでデバッグ実行する手順をまとめました。
ちなみにSwift Foundationのリポジトリは下記です
https://github.com/apple/swift-corelibs-foundation/tree/master/TestFoundation
Swift FoundationのXCTestをXcodeでデバッグ実行する
基本的にGetting Startedを読めばいいとは思うものの自分の手順を箇条書きしておきます。
デバッグ実行のための手順
-
CMakeをインストールする
- CMake.appを/Applicationsに移動
- export PATH=/Applications/CMake.app/Contents/bin:$PATH
- 補足: brewでCMakeインストールしようとしたらエラーになったのでパッケージでいれた
- swiftのコードをcloneしておく(clone時にディレクトリ名を指定しているのは後でシンボリックリンクにしても良い)
- git clone git@github.com:apple/swift.git swift
- git clone git@github.com:apple/swift-clang.git clang
- git clone git@github.com:apple/swift-llvm.git llvm
- git clone git@github.com/apple/swift-corelibs-xctest.git
- git clone git@github.com/apple/swift-corelibs-foundation.git
- $ ./swift/utils/build-script
- Xcode 7.2を入れる(App Store版で問題ない)
- Xcode Swift2.2 SnapshotをDLしインストールする
- 補足: pkgでインストール後 /Library/Developer/Toolchains/にswift-latest.xctoolchainが入る
- $ xcrun launch-with-toolchain /Library/Developer/Toolchains/swift-latest.xctoolchain
- xcrun実行後にXcodeが開くのでそこでswift-corelibs-foundation/Foundation.xcworkspaceを開く
- Xcodeの実行ターゲットで「CoreFoundation」を選びデバッグ実行する
Swift Foundationのテストケースについて
おまけにSwift Foundationのクラス別のテストケースについて少しだけまとめています。
Pull Requestが日々マージされていてもうすでに変更済みかもしれないため、この記事は参考程度でお願いします。
NSNull
現在(2015.12.10)はもうなくなってしまったが、NSNullにはシンプルなテストがあった。
test_null
呼び出したNSNullオブジェクトを他のNSNullオブジェクトと比較し、同じであれば正しくNSNullが定義されているかどうかを確認していた。
func test_null() {
let nullIsDefined = NSNull.null() == NSNull.null()
XCTAssertTrue(nullIsDefined)
}
何をしているか
- NSNullはシングルトンオブジェクトのため2つのインスタンスが同じかでそれを確認する
NSArray
test_BasicConstruction
test_BasicConstructionは2つのNSArrayを初期化して数だけを数えている。初期化しただけの要素数は0かどうか、Stringを2つ要素としたNSArrayの要素数が2かテストするだけだ。これ以上数を追加して数を確認しても意味が無いと判断できる。
func test_BasicConstruction() {
let array = NSArray()
let array2 : NSArray = ["foo", "bar"].bridge()
XCTAssertEqual(array.count, 0)
XCTAssertEqual(array2.count, 2)
}
何をしているか
- 初期化しただけのNSArrayの要素数は0か
- 初期化時に要素を追加したものの要素数は追加した数あるか
メモ
ちなみにbridgeメソッドはArrayをNSArrayに変換しているのだろう。これ以降も頻繁に使われる。
test_enumeration
NSArrayに追加した要素の列挙をテストしている。要素数を3つとしたNSArrayの要素順序や、それを逆順にしたものをテストし、要素数を0にした場合は順序というか要素そのものが含まれていないことを含めて確認しようとしている。
func test_enumeration() {
let array : NSArray = ["foo", "bar", "baz"].bridge()
let e = array.objectEnumerator()
XCTAssertEqual((e.nextObject() as! NSString).bridge(), "foo")
XCTAssertEqual((e.nextObject() as! NSString).bridge(), "bar")
XCTAssertEqual((e.nextObject() as! NSString).bridge(), "baz")
XCTAssertNil(e.nextObject())
XCTAssertNil(e.nextObject())
let r = array.reverseObjectEnumerator()
XCTAssertEqual((r.nextObject() as! NSString).bridge(), "baz")
XCTAssertEqual((r.nextObject() as! NSString).bridge(), "bar")
XCTAssertEqual((r.nextObject() as! NSString).bridge(), "foo")
XCTAssertNil(r.nextObject())
XCTAssertNil(r.nextObject())
let empty = NSArray().objectEnumerator()
XCTAssertNil(empty.nextObject())
XCTAssertNil(empty.nextObject())
let reverseEmpty = NSArray().reverseObjectEnumerator()
XCTAssertNil(reverseEmpty.nextObject())
XCTAssertNil(reverseEmpty.nextObject())
}
何をしているか
- addされた1つずつの要素は存在しているか
- nextObjectメソッドでもそれが確認できるか
- 要素がカラの場合はそれを確認できるか
test_sequenceType
NSArrayを複数要素で初期化し、Arrayに同じ要素を連続して比較している。これはわざわざArrayをfor inでループさせて追加している意味がわからなかったが、NSArrayから順に取り出したものが元の通りに並んでいるかを見ているのだろう。
func test_sequenceType() {
let array : NSArray = ["foo", "bar", "baz"].bridge()
var res = [String]()
for obj in array {
res.append((obj as! NSString).bridge())
}
XCTAssertEqual(res, ["foo", "bar", "baz"])
}
何をしているか
- 3つの要素で初期化したものと、順次appendで追加したものを比較している
メモ
もし、自分だったら次のように元となるArrayはそのまま比較に使うかな
func test_sequenceType() {
let src = ["foo", "bar", "baz"]
let array : NSArray = src.bridge()
var res = [String]()
for obj in array {
res.append((obj as! NSString).bridge())
}
XCTAssertEqual(res, src)
}
これ以外も結構コピペでテストコード書いているなという印象がある。
test_getObjects
NSArrayを複数の要素で初期化し、指定する範囲で取り出してそれが指定通りかを確認している。これは既に挙げた他のテストとは違いgetObjectsメソッドが正しく動作しているかを確認しているテストだろう。他のテストにより初期化や要素の順序の正しさは担保できているのだから、このテストはgetObjectsの動作を確かめたいという意志を感じる。
func test_getObjects() {
let array : NSArray = ["foo", "bar", "baz", "foo1", "bar2", "baz3",].bridge()
var objects = [AnyObject]()
array.getObjects(&objects, range: NSMakeRange(1, 3))
XCTAssertEqual(objects.count, 3)
let fetched = [
(objects[0] as! NSString).bridge(),
(objects[1] as! NSString).bridge(),
(objects[2] as! NSString).bridge(),
]
XCTAssertEqual(fetched, ["bar", "baz", "foo1"])
}
何をしているか
- getObjectsで指定範囲の要素を取り出せるか数で確認
- 取り出した要素が指定通りか確認
NSDictionary
NSDictionaryもNSArrayと基本は同じでコンテナとしての特性を確認している。
test_BasicConstruction
初期化して数を確認、NSArrayとほぼ同じ。
func test_BasicConstruction() {
let dict = NSDictionary()
let dict2: NSDictionary = ["foo": "bar"].bridge()
XCTAssertEqual(dict.count, 0)
XCTAssertEqual(dict2.count, 1)
}
test_HeterogeneousConstruction
現段階のコミットではコメントアウトされているが、NSDictionaryの要素に、StringとNumberを初期化時に渡し、それぞれが追加されているかを確認している。
func test_HeterogeneousConstruction() {
// let dict2: NSDictionary = [
// "foo": "bar",
// 1 : 2
// ]
// XCTAssertEqual(dict2.count, 2)
// XCTAssertEqual(dict2["foo"] as? NSString, NSString(UTF8String:"bar"))
// XCTAssertEqual(dict2[1] as? NSNumber, NSNumber(int: 2))
}
何をしているか
- NSDictionaryは違う種類の要素を持てるかを確認している
test_ArrayConstruction
これもコメントアウトされているが、やっているのはNSDictionaryのKeyとValueをそれぞれArrayで初期化している。
func test_ArrayConstruction() {
// let objects = ["foo", "bar", "baz"]
// let keys = ["foo", "bar", "baz"]
// let dict = NSDictionary(objects: objects, forKeys: keys as [NSObject])
// XCTAssertEqual(dict.count, 3)
}
何をしているか
- NSDictionaryは要素をArrayで追加できるか確認している
test_enumeration
NSDictionaryを複数要素で初期化し、そのKeyとValueを取り出して想定通りの順序で取り出せるかArrayと比較して確認している。NSDictionaryに追加した要素について、順序は持たないため、keyEnumeratorでKeyやValueの名前が要素順となるということも示されている。
func test_enumeration() {
let dict : NSDictionary = ["foo" : "bar", "whiz" : "bang", "toil" : "trouble"].bridge()
let e = dict.keyEnumerator()
var keys = Set<String>()
keys.insert((e.nextObject()! as! NSString).bridge())
keys.insert((e.nextObject()! as! NSString).bridge())
keys.insert((e.nextObject()! as! NSString).bridge())
XCTAssertNil(e.nextObject())
XCTAssertNil(e.nextObject())
XCTAssertEqual(keys, ["foo", "whiz", "toil"])
let o = dict.objectEnumerator()
var objs = Set<String>()
objs.insert((o.nextObject()! as! NSString).bridge())
objs.insert((o.nextObject()! as! NSString).bridge())
objs.insert((o.nextObject()! as! NSString).bridge())
XCTAssertNil(o.nextObject())
XCTAssertNil(o.nextObject())
XCTAssertEqual(objs, ["bar", "bang", "trouble"])
}
何をしているか
- NSDictionaryの要素はKeyとValueがkeyEnumerator、objectEnumeratorで想定している順に取り出せるか確認している
- 要素が想定しているものかどうか確認している
test_sequenceType
NSDictionaryから順に取り出し、それを確認している。
func test_sequenceType() {
let dict : NSDictionary = ["foo" : "bar", "whiz" : "bang", "toil" : "trouble"].bridge()
var result = [String:String]()
for (key, value) in dict {
result[key as! String] = (value as! NSString).bridge()
}
XCTAssertEqual(result, ["foo" : "bar", "whiz" : "bang", "toil" : "trouble"])
}
メモ
順に取り出したとしてそれが想定通りかは前述のtest_enumerationでわかっているし、意味があるとは自分には思えない。
NSString
NSStringは文字列を初期化と変換の2パターンのテストがある。
test_BridgeConstruction
func test_BridgeConstruction() {
let literalConversion: NSString = "literal"
XCTAssertEqual(literalConversion.length, 7)
let nonLiteralConversion: NSString = "test\(self)".bridge()
XCTAssertTrue(nonLiteralConversion.length > 4)
let nonLiteral2: NSString = String(4).bridge()
let t = nonLiteral2.characterAtIndex(0)
XCTAssertTrue(t == 52)
let externalString: NSString = String.localizedNameOfStringEncoding(String.defaultCStringEncoding()).bridge()
XCTAssertTrue(externalString.length > 4)
let cluster: NSString = "✌🏾"
XCTAssertEqual(cluster.length, 3)
}
何をしているか
- NSStringの初期化をlengthでUnicode文字数の数で確認している。
何をしているか
- アルファベット文字列のlengthがUnicode文字数でそのまま7か
- 文字"4"のUnicodeが52かどうか
- エンコーディング名のlengthが4より大きいかどうか
- Unicode絵文字✌🏾(U+270C)がNSStringのlengthで3かどうか
test_isEqualToStringWithSwiftString
func test_isEqualToStringWithSwiftString() {
let string: NSString = "literal"
let swiftString = "literal"
XCTAssertTrue(string.isEqualToString(swiftString))
}
何をしているか
- NSStringのisEqualToStringメソッドによってNSStringとStringで同じ文字列かどうかを比較する。
test_FromASCIIData
internal let mockASCIIStringBytes: [UInt8] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x53, 0x77, 0x69, 0x66, 0x74, 0x21]
internal let mockASCIIString = "Hello Swift!"
func test_FromASCIIData() {
let bytes = mockASCIIStringBytes
let string = NSString(bytes: bytes, length: bytes.count, encoding: NSASCIIStringEncoding)
XCTAssertNotNil(string)
XCTAssertTrue(string?.isEqualToString(mockASCIIString) ?? false)
}
何をしているか
- バイト列からNSStringに変換し、結果が想定通りかどうかを確認している
NSFileManger
test_createDirectory
ディレクトリを作成し削除できるかどうかを確認していて、成功時にAssertionをtrueにするようなことはやっておらず、失敗した場合のみFailを呼び出している。
func test_createDirectory() {
let fm = NSFileManager.defaultManager()
let path = "/tmp/testdir"
ignoreError { try fm.removeItemAtPath(path) }
do {
try fm.createDirectoryAtPath(path, withIntermediateDirectories: false, attributes: nil)
} catch _ {
XCTFail()
}
var isDir = false
let exists = fm.fileExistsAtPath(path, isDirectory: &isDir)
XCTAssertTrue(exists)
XCTAssertTrue(isDir)
do {
try fm.removeItemAtPath(path)
} catch {
XCTFail("Failed to clean up file")
}
}
何をしているか
- /tmp/restdirが作成できなければFail
- /tmp/restdirが削除できなければFail
メモ
この後も頻繁に出るので説明すると、ignoreErrorは各テスト前にファイルやディレクトリを削除しておきたいが、存在しない場合はエラーとなるためそれを握りつぶしているだけ。
ディレクトリを作成するfm.createDirectoryAtPathは戻り値がなくthrowsによるエラーハンドリングを必要とする。そのため、do catchによるエラーハンドリングでキャッチできる処理をテストしており、失敗した場合に動作するcatchでFailにすれば結果が分かるのでわざわざ成功時にAssertを利用するようなことはしていない。
test_createFile
ファイルを追加し削除できるかどうかを確認している。ディレクトリの場合と異なり、createFileAtPathメソッドはBoolで結果を返すため、AssertTrueで確認している。
func test_createFile() {
let fm = NSFileManager.defaultManager()
let path = "/tmp/testfile"
ignoreError { try fm.removeItemAtPath(path) }
XCTAssertTrue(fm.createFileAtPath(path, contents: NSData(), attributes: nil))
var isDir = false
let exists = fm.fileExistsAtPath(path, isDirectory: &isDir)
XCTAssertTrue(exists)
XCTAssertFalse(isDir)
do {
try fm.removeItemAtPath(path)
} catch {
XCTFail("Failed to clean up file")
}
}
何をしているか
- ファイルが作成できたかどうか
- 作成したファイルが存在するかどうか
- ファイルのつもりでディレクトリを作成してしまってないか
- 作成したファイルが削除できるかどうか
test_fileAttributes
作成したファイルのアトリビュートを調べるattributesOfItemAtPathメソッドが動作するかを確認しようとしている。もともとSwiftのOSS公開当初はコード中に// TODO: Actually verify the contents of the dictionary.
とあり、とりあえず戻り値NSDictionaryで要素が0より大きいかしか見ていなかったのだが、すぐにテストが増えた。
func test_fileAttributes() {
let fm = NSFileManager.defaultManager()
let path = "/tmp/testfile"
ignoreError { try fm.removeItemAtPath(path) }
XCTAssertTrue(fm.createFileAtPath(path, contents: NSData(), attributes: nil))
do {
let attrs = try fm.attributesOfItemAtPath(path)
XCTAssertTrue(attrs.count > 0)
let fileSize = attrs[NSFileSize] as? NSNumber
XCTAssertEqual(fileSize!.longLongValue, 0)
let fileModificationDate = attrs[NSFileModificationDate] as? NSDate
XCTAssertGreaterThan(NSDate().timeIntervalSince1970, fileModificationDate!.timeIntervalSince1970)
let filePosixPermissions = attrs[NSFilePosixPermissions] as? NSNumber
XCTAssertNotEqual(filePosixPermissions!.longLongValue, 0)
let fileReferenceCount = attrs[NSFileReferenceCount] as? NSNumber
XCTAssertEqual(fileReferenceCount!.longLongValue, 1)
let fileSystemNumber = attrs[NSFileSystemNumber] as? NSNumber
XCTAssertNotEqual(fileSystemNumber!.longLongValue, 0)
let fileSystemFileNumber = attrs[NSFileSystemFileNumber] as? NSNumber
XCTAssertNotEqual(fileSystemFileNumber!.longLongValue, 0)
let fileType = attrs[NSFileType] as? String
XCTAssertEqual(fileType!, NSFileTypeRegular)
let fileOwnerAccountID = attrs[NSFileOwnerAccountID] as? NSNumber
XCTAssertNotEqual(fileOwnerAccountID!.longLongValue, 0)
} catch let err {
XCTFail("\(err)")
}
do {
try fm.removeItemAtPath(path)
} catch {
XCTFail("Failed to clean up files")
}
}
何をしているか
- attributesOfItemAtPath:メソッドの戻り値はDictionaryであり要素を調べている
- 要素数が0より大きいか見ている
- 要素は初期値でその値を比較している
- ファイル作成日付と現在日時を比較し過去に作成されたか
メモ
attributes用のDictionaryのキーなどはリファレンスを見れば分かる
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/index.html#//apple_ref/doc/constant_group/File_Attribute_Keys
test_directoryEnumerator
enumeratorAtURLメソッドおよびcontentsOfDirectoryAtURLメソッドを使い任意のパスのURLの数を確認している。
func test_directoryEnumerator() {
let fm = NSFileManager.defaultManager()
let path = "/tmp/testdir"
let itemPath = "/tmp/testdir/item"
ignoreError { try fm.removeItemAtPath(path) }
do {
try fm.createDirectoryAtPath(path, withIntermediateDirectories: false, attributes: nil)
fm.createFileAtPath(itemPath, contents: NSData(), attributes: nil)
} catch _ {
XCTFail()
}
if let e = NSFileManager.defaultManager().enumeratorAtURL(NSURL(fileURLWithPath: path), includingPropertiesForKeys: nil, options: [], errorHandler: nil) {
var foundItems = [String:Int]()
while let item = e.nextObject() as? NSURL {
if let p = item.path {
foundItems[p] = e.level
}
}
XCTAssertEqual(foundItems[itemPath], 1)
} else {
XCTFail()
}
let subDirPath = "/tmp/testdir/testdir2"
let subDirItemPath = "/tmp/testdir/testdir2/item"
do {
try fm.createDirectoryAtPath(subDirPath, withIntermediateDirectories: false, attributes: nil)
fm.createFileAtPath(subDirItemPath, contents: NSData(), attributes: nil)
} catch _ {
XCTFail()
}
if let e = NSFileManager.defaultManager().enumeratorAtURL(NSURL(fileURLWithPath: path), includingPropertiesForKeys: nil, options: [], errorHandler: nil) {
var foundItems = [String:Int]()
while let item = e.nextObject() as? NSURL {
if let p = item.path {
foundItems[p] = e.level
}
}
XCTAssertEqual(foundItems[itemPath], 1)
XCTAssertEqual(foundItems[subDirPath], 1)
XCTAssertEqual(foundItems[subDirItemPath], 2)
} else {
XCTFail()
}
if let e = NSFileManager.defaultManager().enumeratorAtURL(NSURL(fileURLWithPath: path), includingPropertiesForKeys: nil, options: [.SkipsSubdirectoryDescendants], errorHandler: nil) {
var foundItems = [String:Int]()
while let item = e.nextObject() as? NSURL {
if let p = item.path {
foundItems[p] = e.level
}
}
XCTAssertEqual(foundItems[itemPath], 1)
XCTAssertEqual(foundItems[subDirPath], 1)
} else {
XCTFail()
}
if let e = NSFileManager.defaultManager().enumeratorAtURL(NSURL(fileURLWithPath: path), includingPropertiesForKeys: nil, options: [], errorHandler: nil) {
var foundItems = [String:Int]()
while let item = e.nextObject() as? NSURL {
if let p = item.path {
foundItems[p] = e.level
}
}
XCTAssertEqual(foundItems[itemPath], 1)
XCTAssertEqual(foundItems[subDirPath], 1)
} else {
XCTFail()
}
var didGetError = false
let handler : (NSURL, NSError) -> Bool = { (NSURL, NSError) in
didGetError = true
return true
}
if let e = NSFileManager.defaultManager().enumeratorAtURL(NSURL(fileURLWithPath: "/nonexistant-path"), includingPropertiesForKeys: nil, options: [], errorHandler: handler) {
XCTAssertNil(e.nextObject())
} else {
XCTFail()
}
XCTAssertTrue(didGetError)
do {
let contents = try NSFileManager.defaultManager().contentsOfDirectoryAtURL(NSURL(fileURLWithPath: path), includingPropertiesForKeys: nil, options: []).map {
return $0.path!
}
XCTAssertEqual(contents.count, 2)
XCTAssertTrue(contents.contains(itemPath))
XCTAssertTrue(contents.contains(subDirPath))
} catch {
XCTFail()
}
do {
try fm.removeItemAtPath(path)
} catch {
XCTFail("Failed to clean up files")
}
}
何をしているか
- 作成したパスにenumeratorAtURLで取得できるかどうか
- サブディレクトリを作成していき、そのサブディレクトリの数をenumratorの結果で確認
- enumeratorAtURLのオプションを設定せず確認
- enumeratorAtURLのオプション.SkipsSubdirectoryDescendantsを設定して確認
- enumeratorAtURLのオプションを設定せず確認(再度やる意味あるの?)
- 存在しないはずのパスにenumeratorAtURLで取得できないことを確認
- errorHandlerでのチェックもしている
NSNumber
テストしているのはNSNumberを任意の値で初期化して生成した値をInt8,Int16,Int32などにメソッドで取得し、それぞれの値と比較している。
数が非常に多いので興味深いものを抜粋する
Int8(-37)をboolValueとして比較
XCTAssertEqual(NSNumber(char: Int8(-37)).boolValue, true);
何をしているか
- 10進数-37は2進数にして最下位ビットだけ見ると1なのでtrue(のような感じ?
Int8.maxをboolValueとして比較
XCTAssertEqual(NSNumber(char: Int8.max).boolValue, true)
何をしているか
- 10進数Int8.maxは127で、2進数にして最下位ビットが1なのでtrue(のような感じ?
Int8.maxをboolValueとして比較
XCTAssertEqual(NSNumber(unsignedChar: UInt8.min).boolValue, false)
何をしているか
- 10進数Int8.minは-128で、2進数の最下位ビット0なのでfalse(のような感じ?
NSUUID
NSUUIDのテストはUUID本来の仕組みもわかって面白い。
test_Equality
NSUUIDを比較し同じかどうかのテストをしている。
func test_Equality() {
let uuidA = NSUUID(UUIDString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")
let uuidB = NSUUID(UUIDString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f")
let uuidC = NSUUID(UUIDBytes: [0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f])
let uuidD = NSUUID()
XCTAssertEqual(uuidA, uuidB, "String case must not matter.")
XCTAssertEqual(uuidA, uuidC, "A UUID initialized with a string must be equal to the same UUID initialized with its UnsafePointer<UInt8> equivalent representation.")
XCTAssertNotEqual(uuidC, uuidD, "Two different UUIDs must not be equal.")
}
何をやっているか
- 大文字で初期化したuuidAと小文字で初期化したuuidBのUUIDを比較し同じかどうか
- 文字列で初期化したuuidAとUnsafePointerで生成したUUIDが同じかどうか
- 初期化の引数を与えず新規作成したuuidDとは違うことを確認
メモ
assertionのメッセージに "String case must not matter."と書かれていて急に分かりやくなってる。
test_InvalidUUID
func test_InvalidUUID() {
let uuid = NSUUID(UUIDString: "Invalid UUID")
XCTAssertNil(uuid, "The convenience initializer `init?(UUIDString string:)` must return nil for an invalid UUID string.")
}
何をやっているか
- 初期化用のStringにテキトウな文字列を渡した場合はUUIDとして初期化されずnilを返している
test_InitializationWithNil
func test_InitializationWithNil() {
let uuid = NSUUID(UUIDBytes: nil)
XCTAssertEqual(uuid, NSUUID(UUIDBytes: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]), "The convenience initializer `init(UUIDBytes bytes:)` must return the Nil UUID when UUIDBytes is nil.")
}
何をやっているか
- nilで初期化したUUIDと0の配列で初期化したUUIDを比較している
メモ
- 両方共nilが返ってきていてnil同士の比較をしている?
- それだったら片方をnilと比較し、その後にお互いを比較したほうが文脈だけでassertメッセージはいらないよね?
test_UUIDString
func test_UUIDString() {
let uuid = NSUUID(UUIDBytes: [0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f])
XCTAssertEqual(uuid.UUIDString, "e621e1f8-c36c-495a-93fc-0c247a3e6e5f", "The UUIDString representation must be lowercase as defined by RFC 4122.")
}
何をやっているか
- Byte列で生成したUUIDと文字列を比較している
- すでにtest_Equalityで比較しているしいらないよね?
test_NSCoding
func test_NSCoding() {
let uuidA = NSUUID()
let uuidB = NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(uuidA)) as! NSUUID
XCTAssertEqual(uuidA, uuidB, "Archived then unarchived uuid must be equal.")
}
何をやっているか
- NSUUID(uuidA)をアーカイブし、アンアーカイブしたもの(uuidB)と、元のNSUUID(uuidA)を比較している
メモ
- NSUUIDはNSCopyingに準拠しているのでこういうことが出来るよね、ということ?
NSCalendar
test_gettingDatesOnGregorianCalendar
func test_gettingDatesOnGregorianCalendar() {
let date = NSDate(timeIntervalSince1970: 1449332351)
guard let components = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)?.components([.Year, .Month, .Day], fromDate: date) else {
XCTFail("Could not get date from the gregorian calendar")
return
}
XCTAssertEqual(components.year, 2015)
XCTAssertEqual(components.month, 12)
XCTAssertEqual(components.day, 5)
}
何をやっているか
- 特定のUNIXTimestapから西暦のNSDateComponentsを取得できるか
- NSDateComponentsのyear, month, dayメソッドが想定通りか
メモ
- 問題なさそうだが手元で実行してみるとこのテストはFailする
- day, 5としているが自分の手元の環境だと6になる
- 恐らくビルドしたMacのタイムゾーンに依存しているため日本のタイムゾーンでは6日になる
- NSCalendarにNSTimeZoneを設定すればよいが、現在NSTimeZoneの初期化が実装されていない
- NSTimeZoneのinitメソッドが軒並み実装されていないので、特定のTimeZoneがCalendarに設定できない?