Files
BitTorrentSwift/BitTorrent/Models/TorrentMetaInfoTests.swift
2017-07-23 23:35:50 +01:00

342 lines
14 KiB
Swift

//
// TorrentMetaInfoTests.swift
// BitTorrent
//
// Created by Ben Davis on 25/03/2016.
// Copyright © 2016 Ben Davis. All rights reserved.
//
import XCTest
@testable import BitTorrent
@testable import BEncode
extension Array where Element: Equatable {
static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool {
for i in [0..<lhs.count] {
if lhs[i] != rhs[i] {
return false
}
}
return true
}
}
class TorrentMetaInfoTests: XCTestCase {
let filePieceLength = 16384
let singleFilePiece = Data(bytes: [0x3f, 0x3f, 0x11, 0x09, 0x64, 0x07, 0x00, 0x3f, 0x42, 0x35,
0x3f, 0x3f, 0x59, 0x2e, 0x23, 0x13, 0x3f, 0x18, 0x23, 0x3e])
let torrentName = "Torrent Name"
let singleFileName = "test.txt"
let singleFileLength = 117
let singleFileMD5 = "c23d5ddec291bf9e27cb84657144388dc352472ca89709bbda80e680c82470e1"
let multipleFile1MD5 = "d8e624fda84296ba9e5af415faa5c2aa68960ce0578116a8507340438893bd85"
let multipleFile2MD5 = "e2ddfed708ea34db57b4a1ef7691776db5e24dff813706e4c695a6668f8fbabf"
let multipleFileLength1 = 116
let multipleFileLength2 = 115
let multipleFilePath1 = ["test", "path"]
let multipleFilePath1String = "test/path"
let multipleFilePath2 = ["test", "path2"]
let multipleFilePath2String = "test/path2"
let announceURL = URL(string: "announceURL.com")!
let announceList: [[String]] = [ [ "http://tracker1.com", "udp://tracker2.com:123/announce" ],
["udp://backup1.com:321/announce"] ]
let announceListURLs: [[URL]] = [ [ URL(string: "http://tracker1.com")!,
URL(string: "udp://tracker2.com:123/announce")! ],
[URL(string: "udp://backup1.com:321/announce")!] ]
let creationDateInt: Int = 123456789
let creationDate = Date(timeIntervalSince1970: 123456789)
let comment = "Comment"
let createdBy = "Created By"
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
// MARK: - Create some example dictionaries
func singleFileInfoDictionary() -> [String:AnyObject] {
return [
"piece length" : filePieceLength as AnyObject,
"pieces" : createMultiplePieces() as AnyObject,
"name" : singleFileName as AnyObject,
"length" : singleFileLength as AnyObject,
]
}
func createMultiplePieces() -> Data {
return singleFilePiece + singleFilePiece
}
func multiFileInfoDictionary() -> [String:AnyObject] {
return multiFileInfoDictionary([
testFileDictionary(multipleFileLength1, path: multipleFilePath1, md5sum: multipleFile1MD5),
testFileDictionary(multipleFileLength2, path: multipleFilePath2, md5sum: multipleFile2MD5),
])
}
func multiFileInfoDictionary(_ files: [ [ String : AnyObject ] ]) -> [String:AnyObject] {
return [
"piece length" : filePieceLength as AnyObject,
"pieces" : createMultiplePieces() as AnyObject,
"name" : torrentName as AnyObject,
"files" : files as AnyObject,
]
}
func testFileDictionary(_ length: Int, path: [String], md5sum: String?) -> [ String : AnyObject ] {
var result: [ String : AnyObject ] = [
"length" : length as AnyObject,
"path" : path as AnyObject,
]
if let md5sum = md5sum {
result.updateValue(md5sum as AnyObject, forKey:"md5sum")
}
return result
}
func exampleMetaInfoDictionary() -> Data {
return metaInfoDictionaryWithInfoDictionary(singleFileInfoDictionary())
}
func metaInfoDictionaryWithInfoDictionary(_ infoDictionary: [String : AnyObject]) -> Data {
return metaInfoDictionaryWithDictionary( unEncodedMetaInfoWithInfoDictionary(infoDictionary) )
}
func metaInfoDictionaryWithDictionary(_ dictionary: [String : AnyObject]) -> Data {
return try! BEncode.BEncoder.encode( dictionary )
}
func unEncodedMetaInfoWithInfoDictionary(_ infoDictionary: [String : AnyObject]) -> [String : AnyObject] {
return [
"info" : infoDictionary as AnyObject,
"announce" : announceURL.absoluteString as AnyObject,
"announce-list": announceList as AnyObject,
"creation date": creationDateInt as AnyObject,
"comment": comment as AnyObject,
"created by": createdBy as AnyObject,
]
}
// MARK: -
func testCanInitialiseWithDictionary() {
let metaInfo = TorrentMetaInfo(data: exampleMetaInfoDictionary())!
XCTAssertEqual(metaInfo.announce, announceURL)
XCTAssertEqual(metaInfo.announceList!, announceListURLs)
XCTAssertEqual(metaInfo.creationDate!, creationDate)
XCTAssertEqual(metaInfo.comment!, comment)
XCTAssertEqual(metaInfo.createdBy!, createdBy)
}
func testInfoDictionarySplitsPiecesInto20ByteChecksums() {
let metaInfo = TorrentMetaInfo(data: exampleMetaInfoDictionary())!
let info = metaInfo.info
XCTAssertEqual(info.pieces[0], singleFilePiece)
XCTAssertEqual(info.pieces[1], singleFilePiece)
}
func testProducesCorrectInfoHash() {
let path = Bundle(for: type(of: self)).path(forResource: "TestText", ofType: "torrent")
let data = try! Data(contentsOf: URL(fileURLWithPath: path!))
let metaInfo = TorrentMetaInfo(data: data)!
let hash = Data(bytes:[ 0xf0, 0xb8, 0x71, 0x98, 0x99, 0x53, 0x97, 0x3f, 0xbf, 0xa9,
0x4d, 0xc8, 0x14, 0x98, 0xee, 0x8d, 0x20, 0x5b, 0xb2, 0x23])
XCTAssertEqual(hash, metaInfo.infoHash)
}
func testInitializerReturnsNilOnInvalidFields() {
var metainfoDictionary = unEncodedMetaInfoWithInfoDictionary(singleFileInfoDictionary())
metainfoDictionary.removeValue(forKey: "announce")
var metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithDictionary(metainfoDictionary))
XCTAssertNil(metaInfo)
metainfoDictionary = unEncodedMetaInfoWithInfoDictionary(singleFileInfoDictionary())
metainfoDictionary.removeValue(forKey: "info")
metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithDictionary(metainfoDictionary))
XCTAssertNil(metaInfo)
metainfoDictionary = unEncodedMetaInfoWithInfoDictionary(singleFileInfoDictionary())
let exampleInvalidField = [[Data(bytes: [255])]]
metainfoDictionary.updateValue(exampleInvalidField as AnyObject, forKey: "announce-list")
metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithDictionary(metainfoDictionary))
XCTAssertNil(metaInfo)
}
// MARK: - Test info dictionary
func testProducesCorrectCommonInfoDictionaryProperties() {
let metaInfo = TorrentMetaInfo(data: exampleMetaInfoDictionary())!
let info = metaInfo.info
XCTAssertEqual(info.name, singleFileName)
XCTAssertEqual(info.pieceLength, filePieceLength)
let piecesConcatenated = info.pieces.reduce(Data()) {
(result, item) in
return result + item
}
XCTAssertEqual(piecesConcatenated, createMultiplePieces())
}
func testIsPrivateIffPrivateKeyIsPresentAnd1() {
var infoDictionary = singleFileInfoDictionary()
infoDictionary.updateValue(1 as AnyObject, forKey: "private")
var metainfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))!
XCTAssertTrue(metainfo.info.isPrivate)
infoDictionary.updateValue(0 as AnyObject, forKey: "private")
metainfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))!
XCTAssertFalse(metainfo.info.isPrivate)
infoDictionary.removeValue(forKey: "private")
metainfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))!
XCTAssertFalse(metainfo.info.isPrivate)
}
// MARK: Single file info dictionary
func testInfoDictionaryForSingleFileTorrent() {
let metaInfo = TorrentMetaInfo(data: exampleMetaInfoDictionary())!
let info = metaInfo.info
XCTAssertEqual(info.length, singleFileLength)
XCTAssertEqual(info.files.count, 1)
let file = info.files[0]
XCTAssertEqual(file.length, singleFileLength)
XCTAssertEqual(file.path, singleFileName)
}
func testInfoDictionaryContainsMD5ChecksumIfPresentInSingleFile() {
var infoDictionary = singleFileInfoDictionary()
var metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))!
var file = metaInfo.info.files[0]
XCTAssertNil(file.md5sum)
infoDictionary.updateValue(singleFileMD5 as AnyObject, forKey: "md5sum")
metaInfo = TorrentMetaInfo(data: metaInfoDictionaryWithInfoDictionary(infoDictionary))!
file = metaInfo.info.files[0]
XCTAssertEqual(file.md5sum, singleFileMD5)
}
func testInitializerReturnsNilOnInvalidInfoDictionaryForSingleFile() {
var infoDictionary = singleFileInfoDictionary()
infoDictionary.removeValue(forKey: "name")
var metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
infoDictionary = singleFileInfoDictionary()
infoDictionary.removeValue(forKey: "piece length")
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
infoDictionary = singleFileInfoDictionary()
infoDictionary.removeValue(forKey: "pieces")
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
infoDictionary = singleFileInfoDictionary()
let exampleData = Data(bytes: [1,2,3])
infoDictionary.updateValue(exampleData as AnyObject, forKey: "pieces")
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
infoDictionary = singleFileInfoDictionary()
infoDictionary.updateValue(NSMutableData(length: 21)!, forKey: "pieces")
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
}
// MARK: Multiple file info dictionary
func testInfoDictionaryForMultipleFileTorrent() {
let infoDictionary = multiFileInfoDictionary()
let metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
let metaInfo = TorrentMetaInfo(data: metaInfoDictionary)!
let info = metaInfo.info
XCTAssertEqual(info.length, multipleFileLength1 + multipleFileLength2)
XCTAssertEqual(info.files.count, 2)
let file1 = info.files[0]
XCTAssertEqual(file1.length, multipleFileLength1)
XCTAssertEqual(file1.path, multipleFilePath1String)
let file2 = info.files[1]
XCTAssertEqual(file2.length, multipleFileLength2)
XCTAssertEqual(file2.path, multipleFilePath2String)
}
func testInfoDictionaryContainsMD5ChecksumIfPresentInMultipleFiles() {
var infoDictionary = multiFileInfoDictionary()
var metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
var metaInfo = TorrentMetaInfo(data: metaInfoDictionary)!
var file1 = metaInfo.info.files[0]
var file2 = metaInfo.info.files[1]
XCTAssertEqual(file1.md5sum, multipleFile1MD5)
XCTAssertEqual(file2.md5sum, multipleFile2MD5)
infoDictionary.updateValue(multipleFile1MD5 as AnyObject, forKey: "md5sum")
infoDictionary = multiFileInfoDictionary([
[ "length" : multipleFileLength1 as AnyObject, "path" : multipleFilePath1 as AnyObject ],
[ "length" : multipleFileLength2 as AnyObject, "path" : multipleFilePath2 as AnyObject ],
])
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
metaInfo = TorrentMetaInfo(data: metaInfoDictionary)!
file1 = metaInfo.info.files[0]
file2 = metaInfo.info.files[1]
XCTAssertNil(file1.md5sum)
XCTAssertNil(file2.md5sum)
}
func testInitializerReturnsNilOnInvalidInfoDictionaryForMultipleFiles() {
var infoDictionary = multiFileInfoDictionary()
infoDictionary.removeValue(forKey: "files")
var metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
infoDictionary = multiFileInfoDictionary([
[ "length" : multipleFileLength1 as AnyObject, "path" : multipleFilePath1 as AnyObject ],
[ "length" : multipleFileLength2 as AnyObject ],
])
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
infoDictionary = multiFileInfoDictionary([
[ "length" : multipleFileLength1 as AnyObject, "path" : multipleFilePath1 as AnyObject ],
[ "path" : multipleFilePath2 as AnyObject ],
])
metaInfoDictionary = metaInfoDictionaryWithInfoDictionary(infoDictionary)
XCTAssertNil(TorrentMetaInfo(data: metaInfoDictionary))
}
}