From 7195cb3b2776047b310fbcc3dfb391c5bc238456 Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Fri, 21 Feb 2025 17:08:47 +0100 Subject: [PATCH 1/8] Convert all unit tests to Swift Testing --- Package.resolved | 36 - Package.swift | 4 +- Tests/SwiftGit2Tests/Fixtures.swift | 57 +- Tests/SwiftGit2Tests/FixturesSpec.swift | 24 +- Tests/SwiftGit2Tests/OIDSpec.swift | 103 +- Tests/SwiftGit2Tests/ObjectsSpec.swift | 681 ++++---- Tests/SwiftGit2Tests/ReferencesSpec.swift | 310 ++-- Tests/SwiftGit2Tests/RemotesSpec.swift | 92 +- Tests/SwiftGit2Tests/RepositorySpec.swift | 1943 ++++++++++----------- Tests/SwiftGit2Tests/ResultShims.swift | 2 + 10 files changed, 1596 insertions(+), 1656 deletions(-) diff --git a/Package.resolved b/Package.resolved index 7034b462..8c0c2fa3 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,41 +1,5 @@ { "pins" : [ - { - "identity" : "cwlcatchexception", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mattgallagher/CwlCatchException.git", - "state" : { - "revision" : "3b123999de19bf04905bc1dfdb76f817b0f2cc00", - "version" : "2.1.2" - } - }, - { - "identity" : "cwlpreconditiontesting", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", - "state" : { - "revision" : "dc9af4781f2afdd1e68e90f80b8603be73ea7abc", - "version" : "2.2.0" - } - }, - { - "identity" : "nimble", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Quick/Nimble.git", - "state" : { - "revision" : "efe11bbca024b57115260709b5c05e01131470d0", - "version" : "13.2.1" - } - }, - { - "identity" : "quick", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Quick/Quick.git", - "state" : { - "revision" : "a9e6c24033a25e158384d523a556c0b96c44a839", - "version" : "7.4.0" - } - }, { "identity" : "ziparchive", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index c347f7bf..ee18b35d 100644 --- a/Package.swift +++ b/Package.swift @@ -18,8 +18,6 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/Quick/Quick.git", from: "7.0.0"), - .package(url: "https://github.com/Quick/Nimble.git", from: "13.0.0"), .package(url: "https://github.com/ZipArchive/ZipArchive.git", from: "2.5.5"), ], targets: [ @@ -29,7 +27,7 @@ let package = Package( ), .testTarget( name: "SwiftGit2Tests", - dependencies: ["SwiftGit2", "Clibgit2", "Quick", "Nimble", "ZipArchive"], + dependencies: ["SwiftGit2", "Clibgit2", "ZipArchive"], resources: [.copy("Fixtures")] ), .target( diff --git a/Tests/SwiftGit2Tests/Fixtures.swift b/Tests/SwiftGit2Tests/Fixtures.swift index 71df55dd..ddb92225 100644 --- a/Tests/SwiftGit2Tests/Fixtures.swift +++ b/Tests/SwiftGit2Tests/Fixtures.swift @@ -11,58 +11,57 @@ import ZipArchive final class Fixtures { - // MARK: Lifecycle + let directoryURL: URL - class var sharedInstance: Fixtures { - enum Singleton { - static let instance = Fixtures() - } - return Singleton.instance - } + // MARK: - Setup and Teardown - init() { + init() throws{ directoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) .appendingPathComponent("org.libgit2.SwiftGit2") - .appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString) + .appendingPathComponent(UUID().uuidString) + try setUp() } - // MARK: - Setup and Teardown - - let directoryURL: URL + deinit { + do { + try tearDown() + } catch { + print("Warning: failed to tear down fixtures: \(error)") + } + } - func setUp() { - try! FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + private func setUp() throws { + try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) - let bundle = Bundle.module - let zipURLs = bundle.urls(forResourcesWithExtension: "zip", subdirectory: "Fixtures")! + let zipURLs = Bundle.module.urls(forResourcesWithExtension: "zip", subdirectory: "Fixtures")! for URL in zipURLs { SSZipArchive.unzipFile(atPath: URL.path, toDestination: directoryURL.path) } } - func tearDown() { - try! FileManager.default.removeItem(at: directoryURL) + private func tearDown() throws { + try FileManager.default.removeItem(at: directoryURL) } // MARK: - Helpers - func repository(named name: String) -> Repository { + func repository(named name: String) throws -> Repository { let url = directoryURL.appendingPathComponent(name, isDirectory: true) - return Repository.at(url).value! + return try Repository.at(url).get() } // MARK: - The Fixtures - class var detachedHeadRepository: Repository { - return Fixtures.sharedInstance.repository(named: "detached-head") - } + func detachedHeadRepository() throws -> Repository { + return try repository(named: "detached-head") + } - class var simpleRepository: Repository { - return Fixtures.sharedInstance.repository(named: "simple-repository") - } + func simpleRepository() throws -> Repository { + return try repository(named: "simple-repository") + } - class var mantleRepository: Repository { - return Fixtures.sharedInstance.repository(named: "Mantle") - } + func mantleRepository() throws -> Repository { + return try repository(named: "Mantle") + } } diff --git a/Tests/SwiftGit2Tests/FixturesSpec.swift b/Tests/SwiftGit2Tests/FixturesSpec.swift index 6f7b063d..c841026c 100644 --- a/Tests/SwiftGit2Tests/FixturesSpec.swift +++ b/Tests/SwiftGit2Tests/FixturesSpec.swift @@ -6,19 +6,19 @@ // Copyright (c) 2014 GitHub, Inc. All rights reserved. // -import Quick import SwiftGit2 +import Testing +import Clibgit2 -class FixturesSpec: QuickSpec { - override class func spec() { - beforeSuite { - _ = SwiftGit2Init() - Fixtures.sharedInstance.setUp() - } +class FixturesSpec { + let fixtures: Fixtures - afterSuite { - Fixtures.sharedInstance.tearDown() - _ = SwiftGit2Shutdown() - } - } + init() throws { + _ = SwiftGit2Init() + self.fixtures = try Fixtures() + } + + deinit { + _ = SwiftGit2Shutdown() + } } diff --git a/Tests/SwiftGit2Tests/OIDSpec.swift b/Tests/SwiftGit2Tests/OIDSpec.swift index 54c2549f..e6fbb2fa 100644 --- a/Tests/SwiftGit2Tests/OIDSpec.swift +++ b/Tests/SwiftGit2Tests/OIDSpec.swift @@ -7,66 +7,63 @@ // import SwiftGit2 -import Nimble -import Quick +import Testing -class OIDSpec: QuickSpec { - override class func spec() { - describe("OID(string:)") { - it("should be nil if string is too short") { - expect(OID(string: "123456789012345678901234567890123456789")).to(beNil()) - } +@Suite("OID") class OIDSpec { + @Suite("OID(string:)") struct InitializeWithString { + @Test("should be nil if string is too short") func tooShort() { + #expect(OID(string: "123456789012345678901234567890123456789") == nil) + } - it("should be nil if string is too long") { - expect(OID(string: "12345678901234567890123456789012345678901")).to(beNil()) - } + @Test("should be nil if string is too long") func tooLong() { + #expect(OID(string: "12345678901234567890123456789012345678901") == nil) + } - it("should not be nil if string is just right") { - expect(OID(string: "1234567890123456789012345678ABCDEFabcdef")).notTo(beNil()) - } + @Test("should not be nil if string is just right") func justRight() { + #expect(OID(string: "1234567890123456789012345678ABCDEFabcdef") != nil) + } - it("should be nil with non-hex characters") { - expect(OID(string: "123456789012345678901234567890123456789j")).to(beNil()) - } - } + @Test("should be nil with non-hex characters") func invalidCharacters() { + #expect(OID(string: "123456789012345678901234567890123456789j") == nil) + } + } - describe("OID(oid)") { - it("should equal an OID with the same git_oid") { - let oid = OID(string: "1234567890123456789012345678901234567890")! - expect(OID(oid.oid)).to(equal(oid)) - } - } + @Suite("OID(oid)") struct InitializeWithOID { + @Test("should equal an OID with the same git_oid") func equal() throws { + let oid = try #require(OID(string: "1234567890123456789012345678901234567890")) + #expect(OID(oid.oid) == oid) + } + } - describe("OID.description") { - it("should return the SHA") { - let SHA = "1234567890123456789012345678901234567890" - let oid = OID(string: SHA)! - expect(oid.description).to(equal(SHA)) - } - } + @Suite("OID.description") struct Description { + @Test("should return the SHA") func sha() throws { + let SHA = "1234567890123456789012345678901234567890" + let oid = try #require(OID(string: SHA)) + #expect(oid.description == SHA) + } + } - describe("==(OID, OID)") { - it("should be equal when identical") { - let SHA = "1234567890123456789012345678901234567890" - let oid1 = OID(string: SHA)! - let oid2 = OID(string: SHA)! - expect(oid1).to(equal(oid2)) - } + @Suite("==(OID, OID)") struct Equality { + @Test("should be equal when identical") func equal() throws { + let SHA = "1234567890123456789012345678901234567890" + let oid1 = try #require(OID(string: SHA)) + let oid2 = try #require(OID(string: SHA)) + #expect(oid1 == oid2) + } - it("should be not equal when different") { - let oid1 = OID(string: "1234567890123456789012345678901234567890")! - let oid2 = OID(string: "0000000000000000000000000000000000000000")! - expect(oid1).notTo(equal(oid2)) - } - } + @Test("should be not equal when different") func notEqual() throws { + let oid1 = try #require(OID(string: "1234567890123456789012345678901234567890")) + let oid2 = try #require(OID(string: "0000000000000000000000000000000000000000")) + #expect(oid1 != oid2) + } + } - describe("OID.hashValue") { - it("should be equal when OIDs are equal") { - let SHA = "1234567890123456789012345678901234567890" - let oid1 = OID(string: SHA)! - let oid2 = OID(string: SHA)! - expect(oid1.hashValue).to(equal(oid2.hashValue)) - } - } - } + @Suite("OID.hashValue") struct HashValue { + @Test("should be equal when OIDs are equal") func equal() throws { + let SHA = "1234567890123456789012345678901234567890" + let oid1 = try #require(OID(string: SHA)) + let oid2 = try #require(OID(string: SHA)) + #expect(oid1.hashValue == oid2.hashValue) + } + } } diff --git a/Tests/SwiftGit2Tests/ObjectsSpec.swift b/Tests/SwiftGit2Tests/ObjectsSpec.swift index 73a61641..59f3a9ab 100644 --- a/Tests/SwiftGit2Tests/ObjectsSpec.swift +++ b/Tests/SwiftGit2Tests/ObjectsSpec.swift @@ -8,8 +8,7 @@ import Foundation import SwiftGit2 -import Nimble -import Quick +import Testing import Clibgit2 private extension Repository { @@ -26,358 +25,348 @@ private extension Repository { } } -class SignatureSpec: FixturesSpec { - override class func spec() { - describe("Signature(signature)") { - it("should initialize its properties") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let raw_signature = repo.withGitObject(oid) { git_commit_author($0).pointee } - let signature = Signature(raw_signature) - - expect(signature.name).to(equal("Matt Diephouse")) - expect(signature.email).to(equal("matt@diephouse.com")) - expect(signature.time).to(equal(Date(timeIntervalSince1970: 1416186947))) - expect(signature.timeZone.abbreviation()).to(equal("GMT-5")) - } - } - - describe("==(Signature, Signature)") { - it("should be true with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let author1 = repo.withGitObject(oid) { commit in - Signature(git_commit_author(commit).pointee) - } - let author2 = author1 - - expect(author1).to(equal(author2)) - } - - it("should be false with unequal objects") { - let repo = Fixtures.simpleRepository - let oid1 = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - let oid2 = OID(string: "24e1e40ee77525d9e279f079f9906ad6d98c8940")! - - let author1 = repo.withGitObject(oid1) { commit in - Signature(git_commit_author(commit).pointee) - } - let author2 = repo.withGitObject(oid2) { commit in - Signature(git_commit_author(commit).pointee) - } - - expect(author1).notTo(equal(author2)) - } - } - - describe("Signature.hashValue") { - it("should be equal with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let author1 = repo.withGitObject(oid) { commit in - Signature(git_commit_author(commit).pointee) - } - let author2 = author1 - - expect(author1.hashValue).to(equal(author2.hashValue)) - } - } - } +@Suite("Signature") class SignatureSpec { + @Suite("Signature(signature)") class Initializer: FixturesSpec { + @Test("should initialize its properties") func properties() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let raw_signature = repo.withGitObject(oid) { git_commit_author($0).pointee } + let signature = Signature(raw_signature) + + #expect(signature.name == "Matt Diephouse") + #expect(signature.email == "matt@diephouse.com") + #expect(signature.time == Date(timeIntervalSince1970: 1416186947)) + #expect(signature.timeZone.abbreviation() == "GMT-5") + } + } + + @Suite("==(Signature, Signature)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let author1 = repo.withGitObject(oid) { commit in + Signature(git_commit_author(commit).pointee) + } + let author2 = author1 + + #expect(author1 == author2) + } + + @Test("should be false with unequal objects") func unequalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid1 = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + let oid2 = try #require(OID(string: "24e1e40ee77525d9e279f079f9906ad6d98c8940")) + + let author1 = repo.withGitObject(oid1) { commit in + Signature(git_commit_author(commit).pointee) + } + let author2 = repo.withGitObject(oid2) { commit in + Signature(git_commit_author(commit).pointee) + } + + #expect(author1 != author2) + } + } + + @Suite("Signature.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let author1 = repo.withGitObject(oid) { commit in + Signature(git_commit_author(commit).pointee) + } + let author2 = author1 + + #expect(author1.hashValue == author2.hashValue) + } + } } -class CommitSpec: QuickSpec { - override class func spec() { - describe("Commit(pointer)") { - it("should initialize its properties") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "24e1e40ee77525d9e279f079f9906ad6d98c8940")! - - let commit = repo.withGitObject(oid) { Commit($0) } - let author = repo.withGitObject(oid) { commit in - Signature(git_commit_author(commit).pointee) - } - let committer = repo.withGitObject(oid) { commit in - Signature(git_commit_committer(commit).pointee) - } - let tree = PointerTo(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")!) - let parents: [PointerTo] = [ - PointerTo(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")!), - ] - expect(commit.oid).to(equal(oid)) - expect(commit.tree).to(equal(tree)) - expect(commit.parents).to(equal(parents)) - expect(commit.message).to(equal("List branches in README\n")) - expect(commit.author).to(equal(author)) - expect(commit.committer).to(equal(committer)) - } - - it("should handle 0 parents") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let commit = repo.withGitObject(oid) { Commit($0) } - expect(commit.parents).to(equal([])) - } - - it("should handle multiple parents") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "c4ed03a6b7d7ce837d31d83757febbe84dd465fd")! - - let commit = repo.withGitObject(oid) { Commit($0) } - let parents: [PointerTo] = [ - PointerTo(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")!), - PointerTo(OID(string: "57f6197561d1f99b03c160f4026a07f06b43cf20")!), - ] - expect(commit.parents).to(equal(parents)) - } - } - - describe("==(Commit, Commit)") { - it("should be true with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let commit1 = repo.withGitObject(oid) { Commit($0) } - let commit2 = commit1 - expect(commit1).to(equal(commit2)) - } - - it("should be false with unequal objects") { - let repo = Fixtures.simpleRepository - let oid1 = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - let oid2 = OID(string: "c4ed03a6b7d7ce837d31d83757febbe84dd465fd")! - - let commit1 = repo.withGitObject(oid1) { Commit($0) } - let commit2 = repo.withGitObject(oid2) { Commit($0) } - expect(commit1).notTo(equal(commit2)) - } - } - - describe("Commit.hashValue") { - it("should be equal with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let commit1 = repo.withGitObject(oid) { Commit($0) } - let commit2 = commit1 - expect(commit1.hashValue).to(equal(commit2.hashValue)) - } - } - } +@Suite("Commit") class CommitSpec { + @Suite("Commit(pointer)") class Initializer: FixturesSpec { + @Test("should initialize its properties") func properties() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "24e1e40ee77525d9e279f079f9906ad6d98c8940")) + + let commit = repo.withGitObject(oid) { Commit($0) } + let author = repo.withGitObject(oid) { commit in + Signature(git_commit_author(commit).pointee) + } + let committer = repo.withGitObject(oid) { commit in + Signature(git_commit_committer(commit).pointee) + } + let tree = PointerTo(try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31"))) + let parents: [PointerTo] = [ + PointerTo(try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a"))), + ] + #expect(commit.oid == oid) + #expect(commit.tree == tree) + #expect(commit.parents == parents) + #expect(commit.message == "List branches in README\n") + #expect(commit.author == author) + #expect(commit.committer == committer) + } + + @Test("should handle 0 parents") func zeroParents() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let commit = repo.withGitObject(oid) { Commit($0) } + #expect(commit.parents == []) + } + + @Test("should handle multiple parents") func multipleParents() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "c4ed03a6b7d7ce837d31d83757febbe84dd465fd")) + + let commit = repo.withGitObject(oid) { Commit($0) } + let parents: [PointerTo] = [ + PointerTo(try #require(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e"))), + PointerTo(try #require(OID(string: "57f6197561d1f99b03c160f4026a07f06b43cf20"))), + ] + #expect(commit.parents == parents) + } + } + + @Suite("==(Commit, Commit)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let commit1 = repo.withGitObject(oid) { Commit($0) } + let commit2 = commit1 + #expect(commit1 == commit2) + } + + @Test("should be false with unequal objects") func unequalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid1 = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + let oid2 = try #require(OID(string: "c4ed03a6b7d7ce837d31d83757febbe84dd465fd")) + + let commit1 = repo.withGitObject(oid1) { Commit($0) } + let commit2 = repo.withGitObject(oid2) { Commit($0) } + #expect(commit1 != commit2) + } + } + + @Suite("Commit.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let commit1 = repo.withGitObject(oid) { Commit($0) } + let commit2 = commit1 + #expect(commit1.hashValue == commit2.hashValue) + } + } } -class TreeEntrySpec: QuickSpec { - override class func spec() { - describe("Tree.Entry(attributes:object:name:)") { - it("should set its properties") { - let attributes = Int32(GIT_FILEMODE_BLOB.rawValue) - let object = Pointer.blob(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")!) - let name = "README.md" - - let entry = Tree.Entry(attributes: attributes, object: object, name: name) - expect(entry.attributes).to(equal(attributes)) - expect(entry.object).to(equal(object)) - expect(entry.name).to(equal(name)) - } - } - - describe("Tree.Entry(pointer)") { - it("should set its properties") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - - let entry = repo.withGitObject(oid) { Tree.Entry(git_tree_entry_byindex($0, 0)) } - expect(entry.attributes).to(equal(Int32(GIT_FILEMODE_BLOB.rawValue))) - expect(entry.object).to(equal(Pointer.blob(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")!))) - expect(entry.name).to(equal("README.md")) - } - } - - describe("==(Tree.Entry, Tree.Entry)") { - it("should be true with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - - let entry1 = repo.withGitObject(oid) { Tree.Entry(git_tree_entry_byindex($0, 0)) } - let entry2 = entry1 - expect(entry1).to(equal(entry2)) - } - - it("should be false with unequal objects") { - let repo = Fixtures.simpleRepository - let oid1 = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - let oid2 = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let entry1 = repo.withGitObject(oid1) { Tree.Entry(git_tree_entry_byindex($0, 0)) } - let entry2 = repo.withGitObject(oid2) { Tree.Entry(git_tree_entry_byindex($0, 0)) } - expect(entry1).notTo(equal(entry2)) - } - } - - describe("Tree.Entry.hashValue") { - it("should be equal with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - - let entry1 = repo.withGitObject(oid) { Tree.Entry(git_tree_entry_byindex($0, 0)) } - let entry2 = entry1 - expect(entry1.hashValue).to(equal(entry2.hashValue)) - } - } - } +@Suite("Tree.Entry") class TreeEntrySpec { + @Suite("Tree.Entry(attributes:object:name:)") class InitializerWithProperties { + @Test("should set its properties") func shouldSetProperties() throws { + let attributes = Int32(GIT_FILEMODE_BLOB.rawValue) + let object = Pointer.blob(try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba"))) + let name = "README.md" + + let entry = Tree.Entry(attributes: attributes, object: object, name: name) + #expect(entry.attributes == attributes) + #expect(entry.object == object) + #expect(entry.name == name) + } + } + + @Suite("Tree.Entry(pointer)") class InitializerWithPointer: FixturesSpec { + @Test("should set its properties") func shouldSetProperties() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + + let entry = repo.withGitObject(oid) { Tree.Entry(git_tree_entry_byindex($0, 0)) } + #expect(entry.attributes == Int32(GIT_FILEMODE_BLOB.rawValue)) + let expectedOID = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + #expect(entry.object == Pointer.blob(expectedOID)) + #expect(entry.name == "README.md") + } + } + + @Suite("==(Tree.Entry, Tree.Entry)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + + let entry1 = repo.withGitObject(oid) { Tree.Entry(git_tree_entry_byindex($0, 0)) } + let entry2 = entry1 + #expect(entry1 == entry2) + } + + @Test("should be false with unequal objects") func unequalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid1 = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + let oid2 = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let entry1 = repo.withGitObject(oid1) { Tree.Entry(git_tree_entry_byindex($0, 0)) } + let entry2 = repo.withGitObject(oid2) { Tree.Entry(git_tree_entry_byindex($0, 0)) } + #expect(entry1 != entry2) + } + } + + @Suite("Tree.Entry.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + + let entry1 = repo.withGitObject(oid) { Tree.Entry(git_tree_entry_byindex($0, 0)) } + let entry2 = entry1 + #expect(entry1.hashValue == entry2.hashValue) + } + } } -class TreeSpec: QuickSpec { - override class func spec() { - describe("Tree(pointer)") { - it("should initialize its properties") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - - let tree = repo.withGitObject(oid) { Tree($0) } - let entries = [ - "README.md": Tree.Entry(attributes: Int32(GIT_FILEMODE_BLOB.rawValue), - object: .blob(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")!), - name: "README.md"), - ] - expect(tree.entries).to(equal(entries)) - } - } - - describe("==(Tree, Tree)") { - it("should be true with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - - let tree1 = repo.withGitObject(oid) { Tree($0) } - let tree2 = tree1 - expect(tree1).to(equal(tree2)) - } - - it("should be false with unequal objects") { - let repo = Fixtures.simpleRepository - let oid1 = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - let oid2 = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let tree1 = repo.withGitObject(oid1) { Tree($0) } - let tree2 = repo.withGitObject(oid2) { Tree($0) } - expect(tree1).notTo(equal(tree2)) - } - } - - describe("Tree.hashValue") { - it("should be equal with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")! - - let tree1 = repo.withGitObject(oid) { Tree($0) } - let tree2 = tree1 - expect(tree1.hashValue).to(equal(tree2.hashValue)) - } - } - } +@Suite("Tree") class TreeSpec { + @Suite("Tree(pointer)") class InitializerWithPointer: FixturesSpec { + @Test("should initialize its properties") func initialize() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + + let tree = repo.withGitObject(oid) { Tree($0) } + let entries = [ + "README.md": Tree.Entry(attributes: Int32(GIT_FILEMODE_BLOB.rawValue), + object: .blob(try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba"))), + name: "README.md"), + ] + #expect(tree.entries == entries) + } + } + + @Suite("==(Tree, Tree)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + + let tree1 = repo.withGitObject(oid) { Tree($0) } + let tree2 = tree1 + #expect(tree1 == tree2) + } + + @Test("should be false with unequal objects") func unequalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid1 = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + let oid2 = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let tree1 = repo.withGitObject(oid1) { Tree($0) } + let tree2 = repo.withGitObject(oid2) { Tree($0) } + #expect(tree1 != tree2) + } + } + + @Suite("Tree.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "219e9f39c2fb59ed1dfb3e78ed75055a57528f31")) + + let tree1 = repo.withGitObject(oid) { Tree($0) } + let tree2 = tree1 + #expect(tree1.hashValue == tree2.hashValue) + } + } } -class BlobSpec: QuickSpec { - override class func spec() { - describe("Blob(pointer)") { - it("should initialize its properties") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - - let blob = repo.withGitObject(oid) { Blob($0) } - let contents = "# Simple Repository\nA simple repository used for testing SwiftGit2.\n\n## Branches\n\n- master\n\n" - let data = contents.data(using: String.Encoding.utf8)! - expect(blob.oid).to(equal(oid)) - expect(blob.data).to(equal(data)) - } - } - - describe("==(Blob, Blob)") { - it("should be true with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - - let blob1 = repo.withGitObject(oid) { Blob($0) } - let blob2 = blob1 - expect(blob1).to(equal(blob2)) - } - - it("should be false with unequal objects") { - let repo = Fixtures.simpleRepository - let oid1 = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - let oid2 = OID(string: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")! - - let blob1 = repo.withGitObject(oid1) { Blob($0) } - let blob2 = repo.withGitObject(oid2) { Blob($0) } - expect(blob1).notTo(equal(blob2)) - } - } - - describe("Blob.hashValue") { - it("should be equal with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - - let blob1 = repo.withGitObject(oid) { Blob($0) } - let blob2 = blob1 - expect(blob1.hashValue).to(equal(blob2.hashValue)) - } - } - } +@Suite("Blob") class BlobSpec { + @Suite("Blob(pointer)") class Initializer: FixturesSpec { + @Test("should initialize its properties") func initializesProperties() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + + let blob = repo.withGitObject(oid) { Blob($0) } + let contents = "# Simple Repository\nA simple repository used for testing SwiftGit2.\n\n## Branches\n\n- master\n\n" + let data = contents.data(using: String.Encoding.utf8)! + #expect(blob.oid == oid) + #expect(blob.data == data) + } + } + + @Suite("==(Blob, Blob)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + + let blob1 = repo.withGitObject(oid) { Blob($0) } + let blob2 = blob1 + #expect(blob1 == blob2) + } + + @Test("should be false with unequal objects") func unequalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid1 = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + let oid2 = try #require(OID(string: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")) + + let blob1 = repo.withGitObject(oid1) { Blob($0) } + let blob2 = repo.withGitObject(oid2) { Blob($0) } + #expect(blob1 != blob2) + } + } + + @Suite("Blob.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + + let blob1 = repo.withGitObject(oid) { Blob($0) } + let blob2 = blob1 + #expect(blob1.hashValue == blob2.hashValue) + } + } } -class TagSpec: QuickSpec { - override class func spec() { - describe("Tag(pointer)") { - it("should set its properties") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - - let tag = repo.withGitObject(oid) { Tag($0) } - let tagger = repo.withGitObject(oid) { Signature(git_tag_tagger($0).pointee) } - - expect(tag.oid).to(equal(oid)) - expect(tag.target).to(equal(Pointer.commit(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")!))) - expect(tag.name).to(equal("tag-1")) - expect(tag.tagger).to(equal(tagger)) - expect(tag.message).to(equal("tag-1\n")) - } - } - - describe("==(Tag, Tag)") { - it("should be true with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - - let tag1 = repo.withGitObject(oid) { Tag($0) } - let tag2 = tag1 - expect(tag1).to(equal(tag2)) - } - - it("should be false with unequal objects") { - let repo = Fixtures.simpleRepository - let oid1 = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - let oid2 = OID(string: "13bda91157f255ab224ff88d0a11a82041c9d0c1")! - - let tag1 = repo.withGitObject(oid1) { Tag($0) } - let tag2 = repo.withGitObject(oid2) { Tag($0) } - expect(tag1).notTo(equal(tag2)) - } - } - - describe("Tag.hashValue") { - it("should be equal with equal objects") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - - let tag1 = repo.withGitObject(oid) { Tag($0) } - let tag2 = tag1 - expect(tag1.hashValue).to(equal(tag2.hashValue)) - } - } - } +@Suite("Tag") class TagSpec { + @Suite("Tag(pointer)") class Initializer: FixturesSpec { + @Test("should set its properties") func setsProperties() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + + let tag = repo.withGitObject(oid) { Tag($0) } + let tagger = repo.withGitObject(oid) { Signature(git_tag_tagger($0).pointee) } + + #expect(tag.oid == oid) + let expectedOID = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + #expect(tag.target == Pointer.commit(expectedOID)) + #expect(tag.name == "tag-1") + #expect(tag.tagger == tagger) + #expect(tag.message == "tag-1\n") + } + } + + @Suite("==(Tag, Tag)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + + let tag1 = repo.withGitObject(oid) { Tag($0) } + let tag2 = tag1 + #expect(tag1 == tag2) + } + + @Test("should be false with unequal objects") func unequalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid1 = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + let oid2 = try #require(OID(string: "13bda91157f255ab224ff88d0a11a82041c9d0c1")) + + let tag1 = repo.withGitObject(oid1) { Tag($0) } + let tag2 = repo.withGitObject(oid2) { Tag($0) } + #expect(tag1 != tag2) + } + } + + @Suite("Tag.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objects") func equalObjects() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + + let tag1 = repo.withGitObject(oid) { Tag($0) } + let tag2 = tag1 + #expect(tag1.hashValue == tag2.hashValue) + } + } } diff --git a/Tests/SwiftGit2Tests/ReferencesSpec.swift b/Tests/SwiftGit2Tests/ReferencesSpec.swift index 4b91f130..0d606842 100644 --- a/Tests/SwiftGit2Tests/ReferencesSpec.swift +++ b/Tests/SwiftGit2Tests/ReferencesSpec.swift @@ -6,169 +6,169 @@ // Copyright (c) 2015 GitHub, Inc. All rights reserved. // +import Foundation +import Testing import SwiftGit2 -import Nimble -import Quick import Clibgit2 private extension Repository { - func withGitReference(named name: String, transform: (OpaquePointer) -> T) -> T { - let repository = self.pointer - - var pointer: OpaquePointer? = nil - git_reference_lookup(&pointer, repository, name) - let result = transform(pointer!) - git_reference_free(pointer) - - return result - } + func withGitReference(named name: String, transform: (OpaquePointer) -> T) -> T { + let repository = self.pointer + + var pointer: OpaquePointer? = nil + git_reference_lookup(&pointer, repository, name) + let result = transform(pointer!) + git_reference_free(pointer) + + return result + } } -class ReferenceSpec: FixturesSpec { - override class func spec() { - describe("Reference(pointer)") { - it("should initialize its properties") { - let repo = Fixtures.simpleRepository - let ref = repo.withGitReference(named: "refs/heads/master") { Reference($0) } - expect(ref.longName).to(equal("refs/heads/master")) - expect(ref.shortName).to(equal("master")) - expect(ref.oid).to(equal(OID(string: "c4ed03a6b7d7ce837d31d83757febbe84dd465fd")!)) - } - } - - describe("==(Reference, Reference)") { - it("should be true with equal references") { - let repo = Fixtures.simpleRepository - let ref1 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } - let ref2 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } - expect(ref1).to(equal(ref2)) - } - - it("should be false with unequal references") { - let repo = Fixtures.simpleRepository - let ref1 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } - let ref2 = repo.withGitReference(named: "refs/heads/another-branch") { Reference($0) } - expect(ref1).notTo(equal(ref2)) - } - } - - describe("Reference.hashValue") { - it("should be equal with equal references") { - let repo = Fixtures.simpleRepository - let ref1 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } - let ref2 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } - expect(ref1.hashValue).to(equal(ref2.hashValue)) - } - } - } +@Suite("Reference") class ReferenceSpec { + @Suite("Reference(pointer)") class Initialization: FixturesSpec { + @Test("should initialize its properties") func properties() throws { + let repo = try fixtures.simpleRepository() + let ref = repo.withGitReference(named: "refs/heads/master") { Reference($0) } + #expect(ref.longName == "refs/heads/master") + #expect(ref.shortName == "master") + let expectedOID = try #require(OID(string: "c4ed03a6b7d7ce837d31d83757febbe84dd465fd")) + #expect(ref.oid == expectedOID) + } + } + + @Suite("==(Reference, Reference)") class Equality: FixturesSpec { + @Test("should be true with equal references") func equal() throws { + let repo = try fixtures.simpleRepository() + let ref1 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } + let ref2 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } + #expect(ref1 == ref2) + } + + @Test("should be false with unequal references") func notEqual() throws { + let repo = try fixtures.simpleRepository() + let ref1 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } + let ref2 = repo.withGitReference(named: "refs/heads/another-branch") { Reference($0) } + #expect(ref1 != ref2) + } + } + + @Suite("Reference.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal references") func equal() throws { + let repo = try fixtures.simpleRepository() + let ref1 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } + let ref2 = repo.withGitReference(named: "refs/heads/master") { Reference($0) } + #expect(ref1.hashValue == ref2.hashValue) + } + } } -class BranchSpec: QuickSpec { - override class func spec() { - describe("Branch(pointer)") { - it("should initialize its properties") { - let repo = Fixtures.mantleRepository - let branch = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } - expect(branch.longName).to(equal("refs/heads/master")) - expect(branch.name).to(equal("master")) - expect(branch.shortName).to(equal(branch.name)) - expect(branch.commit.oid).to(equal(OID(string: "f797bd4837b61d37847a4833024aab268599a681")!)) - expect(branch.oid).to(equal(branch.commit.oid)) - expect(branch.isLocal).to(beTrue()) - expect(branch.isRemote).to(beFalse()) - } - - it("should work with symoblic refs") { - let repo = Fixtures.mantleRepository - let branch = repo.withGitReference(named: "refs/remotes/origin/HEAD") { Branch($0)! } - expect(branch.longName).to(equal("refs/remotes/origin/HEAD")) - expect(branch.name).to(equal("origin/HEAD")) - expect(branch.shortName).to(equal(branch.name)) - expect(branch.commit.oid).to(equal(OID(string: "f797bd4837b61d37847a4833024aab268599a681")!)) - expect(branch.oid).to(equal(branch.commit.oid)) - expect(branch.isLocal).to(beFalse()) - expect(branch.isRemote).to(beTrue()) - } - } - - describe("==(Branch, Branch)") { - it("should be true with equal branches") { - let repo = Fixtures.simpleRepository - let branch1 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } - let branch2 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } - expect(branch1).to(equal(branch2)) - } - - it("should be false with unequal branches") { - let repo = Fixtures.simpleRepository - let branch1 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } - let branch2 = repo.withGitReference(named: "refs/heads/another-branch") { Branch($0)! } - expect(branch1).notTo(equal(branch2)) - } - } - - describe("Branch.hashValue") { - it("should be equal with equal references") { - let repo = Fixtures.simpleRepository - let branch1 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } - let branch2 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } - expect(branch1.hashValue).to(equal(branch2.hashValue)) - } - } - } +@Suite("Branch") class BranchSpec { + @Suite("Branch(pointer)") class Initializer: FixturesSpec { + @Test("should initialize its properties") func properties() throws { + let repo = try fixtures.mantleRepository() + let branch = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } + #expect(branch.longName == "refs/heads/master") + #expect(branch.name == "master") + #expect(branch.shortName == branch.name) + let expectedOID = try #require(OID(string: "f797bd4837b61d37847a4833024aab268599a681")) + #expect(branch.commit.oid == expectedOID) + #expect(branch.oid == branch.commit.oid) + #expect(branch.isLocal == true) + #expect(branch.isRemote == false) + } + + @Test("should work with symoblic refs") func symbolicRef() throws { + let repo = try fixtures.mantleRepository() + let branch = repo.withGitReference(named: "refs/remotes/origin/HEAD") { Branch($0)! } + #expect(branch.longName == "refs/remotes/origin/HEAD") + #expect(branch.name == "origin/HEAD") + #expect(branch.shortName == branch.name) + let expectedOID = try #require(OID(string: "f797bd4837b61d37847a4833024aab268599a681")) + #expect(branch.commit.oid == expectedOID) + #expect(branch.oid == branch.commit.oid) + #expect(branch.isLocal == false) + #expect(branch.isRemote == true) + } + } + + @Suite("==(Branch, Branch)") class Equality: FixturesSpec { + @Test("should be true with equal branches") func equal() throws { + let repo = try fixtures.simpleRepository() + let branch1 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } + let branch2 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } + #expect(branch1 == branch2) + } + + @Test("should be false with unequal branches") func notEqual() throws { + let repo = try fixtures.simpleRepository() + let branch1 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } + let branch2 = repo.withGitReference(named: "refs/heads/another-branch") { Branch($0)! } + #expect(branch1 != branch2) + } + } + + @Suite("Branch.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal references") func equal() throws { + let repo = try fixtures.simpleRepository() + let branch1 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } + let branch2 = repo.withGitReference(named: "refs/heads/master") { Branch($0)! } + #expect(branch1.hashValue == branch2.hashValue) + } + } } -class TagReferenceSpec: QuickSpec { - override class func spec() { - describe("TagReference(pointer)") { - it("should work with an annotated tag") { - let repo = Fixtures.simpleRepository - let tag = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } - expect(tag.longName).to(equal("refs/tags/tag-2")) - expect(tag.name).to(equal("tag-2")) - expect(tag.shortName).to(equal(tag.name)) - expect(tag.oid).to(equal(OID(string: "24e1e40ee77525d9e279f079f9906ad6d98c8940")!)) - } - - it("should work with a lightweight tag") { - let repo = Fixtures.mantleRepository - let tag = repo.withGitReference(named: "refs/tags/1.5.4") { TagReference($0)! } - expect(tag.longName).to(equal("refs/tags/1.5.4")) - expect(tag.name).to(equal("1.5.4")) - expect(tag.shortName).to(equal(tag.name)) - expect(tag.oid).to(equal(OID(string: "d9dc95002cfbf3929d2b70d2c8a77e6bf5b1b88a")!)) - } - - it("should return nil if not a tag") { - let repo = Fixtures.simpleRepository - let tag = repo.withGitReference(named: "refs/heads/master") { TagReference($0) } - expect(tag).to(beNil()) - } - } - - describe("==(TagReference, TagReference)") { - it("should be true with equal tag references") { - let repo = Fixtures.simpleRepository - let tag1 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } - let tag2 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } - expect(tag1).to(equal(tag2)) - } - - it("should be false with unequal tag references") { - let repo = Fixtures.simpleRepository - let tag1 = repo.withGitReference(named: "refs/tags/tag-1") { TagReference($0)! } - let tag2 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } - expect(tag1).notTo(equal(tag2)) - } - } - - describe("TagReference.hashValue") { - it("should be equal with equal references") { - let repo = Fixtures.simpleRepository - let tag1 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } - let tag2 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } - expect(tag1.hashValue).to(equal(tag2.hashValue)) - } - } - } +@Suite("TagReference") class TagReferenceSpec { + + @Suite("TagReference(pointer)") class InitializeWithPointer: FixturesSpec { + @Test("should work with an annotated tag") func annotatedTag() throws { + let repo = try fixtures.simpleRepository() + let tag = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } + #expect(tag.longName == "refs/tags/tag-2") + #expect(tag.name == "tag-2") + #expect(tag.shortName == tag.name) + let expectedOID = try #require(OID(string: "24e1e40ee77525d9e279f079f9906ad6d98c8940")) + #expect(tag.oid == expectedOID) + } + + @Test("should work with a lightweight tag") func lightweightTag() throws { + let repo = try fixtures.mantleRepository() + let tag = repo.withGitReference(named: "refs/tags/1.5.4") { TagReference($0)! } + #expect(tag.longName == "refs/tags/1.5.4") + #expect(tag.name == "1.5.4") + #expect(tag.shortName == tag.name) + let expectedOID = try #require(OID(string: "d9dc95002cfbf3929d2b70d2c8a77e6bf5b1b88a")) + #expect(tag.oid == expectedOID) + } + + @Test("should return nil if not a tag") func notATag() throws { + let repo = try fixtures.simpleRepository() + let tag = repo.withGitReference(named: "refs/heads/master") { TagReference($0) } + #expect(tag == nil) + } + } + + @Suite("==(TagReference, TagReference)") class Equality: FixturesSpec { + @Test("should be true with equal tag references") func equal() throws { + let repo = try fixtures.simpleRepository() + let tag1 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } + let tag2 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } + #expect(tag1 == tag2) + } + + @Test("should be false with unequal tag references") func notEqual() throws { + let repo = try fixtures.simpleRepository() + let tag1 = repo.withGitReference(named: "refs/tags/tag-1") { TagReference($0)! } + let tag2 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } + #expect(tag1 != tag2) + } + } + + @Suite("TagReference.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal references") func equal() throws { + let repo = try fixtures.simpleRepository() + let tag1 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } + let tag2 = repo.withGitReference(named: "refs/tags/tag-2") { TagReference($0)! } + #expect(tag1.hashValue == tag2.hashValue) + } + } } diff --git a/Tests/SwiftGit2Tests/RemotesSpec.swift b/Tests/SwiftGit2Tests/RemotesSpec.swift index 63a8cbcc..d82779fd 100644 --- a/Tests/SwiftGit2Tests/RemotesSpec.swift +++ b/Tests/SwiftGit2Tests/RemotesSpec.swift @@ -6,59 +6,57 @@ // Copyright (c) 2015 GitHub, Inc. All rights reserved. // +import Foundation +import Testing import SwiftGit2 -import Nimble -import Quick import Clibgit2 private extension Repository { - func withGitRemote(named name: String, transform: (OpaquePointer) -> T) -> T { - let repository = self.pointer + func withGitRemote(named name: String, transform: (OpaquePointer) -> T) -> T { + let repository = self.pointer - var pointer: OpaquePointer? = nil - git_remote_lookup(&pointer, repository, name) - let result = transform(pointer!) - git_remote_free(pointer) + var pointer: OpaquePointer? = nil + git_remote_lookup(&pointer, repository, name) + let result = transform(pointer!) + git_remote_free(pointer) - return result - } + return result + } } -class RemoteSpec: FixturesSpec { - override class func spec() { - describe("Remote(pointer)") { - it("should initialize its properties") { - let repo = Fixtures.mantleRepository - let remote = repo.withGitRemote(named: "upstream") { Remote($0) } - - expect(remote.name).to(equal("upstream")) - expect(remote.URL).to(equal("git@github.com:Mantle/Mantle.git")) - } - } - - describe("==(Remote, Remote)") { - it("should be true with equal objects") { - let repo = Fixtures.mantleRepository - let remote1 = repo.withGitRemote(named: "upstream") { Remote($0) } - let remote2 = remote1 - expect(remote1).to(equal(remote2)) - } - - it("should be false with unequal objcets") { - let repo = Fixtures.mantleRepository - let origin = repo.withGitRemote(named: "origin") { Remote($0) } - let upstream = repo.withGitRemote(named: "upstream") { Remote($0) } - expect(origin).notTo(equal(upstream)) - } - } - - describe("Remote.hashValue") { - it("should be equal with equal objcets") { - let repo = Fixtures.mantleRepository - let remote1 = repo.withGitRemote(named: "upstream") { Remote($0) } - let remote2 = remote1 - expect(remote1.hashValue).to(equal(remote2.hashValue)) - } - } - } +@Suite("Remote") class RemoteSpec { + @Suite("Remote(pointer)") class Initializer: FixturesSpec { + @Test("should initialize its properties") func initializer() throws { + let repo = try fixtures.mantleRepository() + let remote = repo.withGitRemote(named: "upstream") { Remote($0) } + + #expect(remote.name == "upstream") + #expect(remote.URL == "git@github.com:Mantle/Mantle.git") + } + } + + @Suite("==(Remote, Remote)") class Equality: FixturesSpec { + @Test("should be true with equal objects") func equal() throws { + let repo = try fixtures.mantleRepository() + let remote1 = repo.withGitRemote(named: "upstream") { Remote($0) } + let remote2 = remote1 + #expect(remote1 == remote2) + } + + @Test("should be false with unequal objcets") func unequal() throws { + let repo = try fixtures.mantleRepository() + let origin = repo.withGitRemote(named: "origin") { Remote($0) } + let upstream = repo.withGitRemote(named: "upstream") { Remote($0) } + #expect(origin != upstream) + } + } + + @Suite("Remote.hashValue") class HashValue: FixturesSpec { + @Test("should be equal with equal objcets") func equal() throws { + let repo = try fixtures.mantleRepository() + let remote1 = repo.withGitRemote(named: "upstream") { Remote($0) } + let remote2 = remote1 + #expect(remote1.hashValue == remote2.hashValue) + } + } } diff --git a/Tests/SwiftGit2Tests/RepositorySpec.swift b/Tests/SwiftGit2Tests/RepositorySpec.swift index bf02563f..0ba9be58 100644 --- a/Tests/SwiftGit2Tests/RepositorySpec.swift +++ b/Tests/SwiftGit2Tests/RepositorySpec.swift @@ -7,983 +7,976 @@ // import Foundation +import Testing import SwiftGit2 -import Nimble -import Quick // swiftlint:disable cyclomatic_complexity -class RepositorySpec: FixturesSpec { - override class func spec() { - describe("Repository.Type.at(_:)") { - it("should work if the repo exists") { - let repo = Fixtures.simpleRepository - expect(repo.directoryURL).notTo(beNil()) - } - - it("should fail if the repo doesn't exist") { - let url = URL(fileURLWithPath: "blah") - let result = Repository.at(url) - expect(result.error?.domain) == libGit2ErrorDomain - expect(result.error?.localizedDescription).to(match("failed to resolve path")) - } - } - - describe("Repository.Type.isValid(url:)") { - it("should return true if the repo exists") { - guard let repositoryURL = Fixtures.simpleRepository.directoryURL else { - fail("Fixture setup broken: Repository does not exist"); return - } - - let result = Repository.isValid(url: repositoryURL) - - expect(result.error).to(beNil()) - - if case .success(let isValid) = result { - expect(isValid).to(beTruthy()) - } - } - - it("should return false if the directory does not contain a repo") { - let tmpURL = URL(fileURLWithPath: "/dev/null") - let result = Repository.isValid(url: tmpURL) - - expect(result.error).to(beNil()) - - if case .success(let isValid) = result { - expect(isValid).to(beFalsy()) - } - } - - it("should return error if .git is not readable") { - let localURL = self.temporaryURL(forPurpose: "git-isValid-unreadable").appendingPathComponent(".git") - let nonReadablePermissions: [FileAttributeKey: Any] = [.posixPermissions: 0o077] - try! FileManager.default.createDirectory( - at: localURL, - withIntermediateDirectories: true, - attributes: nonReadablePermissions) - let result = Repository.isValid(url: localURL) - - expect(result.value).to(beNil()) - expect(result.error).notTo(beNil()) - } - } - - describe("Repository.Type.create(at:)") { - it("should create a new repo at the specified location") { - let localURL = self.temporaryURL(forPurpose: "local-create") - let result = Repository.create(at: localURL) - - expect(result.error).to(beNil()) - - if case .success(let clonedRepo) = result { - expect(clonedRepo.directoryURL).notTo(beNil()) - } - } - } - - describe("Repository.Type.clone(from:to:)") { - it("should handle local clones") { - let remoteRepo = Fixtures.simpleRepository - let localURL = self.temporaryURL(forPurpose: "local-clone") - let result = Repository.clone(from: remoteRepo.directoryURL!, to: localURL, localClone: true) - - expect(result.error).to(beNil()) - - if case .success(let clonedRepo) = result { - expect(clonedRepo.directoryURL).notTo(beNil()) - } - } - - it("should handle bare clones") { - let remoteRepo = Fixtures.simpleRepository - let localURL = self.temporaryURL(forPurpose: "bare-clone") - let result = Repository.clone(from: remoteRepo.directoryURL!, to: localURL, localClone: true, bare: true) - - expect(result.error).to(beNil()) - - if case .success(let clonedRepo) = result { - expect(clonedRepo.directoryURL).to(beNil()) - } - } - - it("should have set a valid remote url") { - let remoteRepo = Fixtures.simpleRepository - let localURL = self.temporaryURL(forPurpose: "valid-remote-clone") - let cloneResult = Repository.clone(from: remoteRepo.directoryURL!, to: localURL, localClone: true) - - expect(cloneResult.error).to(beNil()) - - if case .success(let clonedRepo) = cloneResult { - let remoteResult = clonedRepo.remote(named: "origin") - expect(remoteResult.error).to(beNil()) - - if case .success(let remote) = remoteResult { - expect(remote.URL).to(equal(remoteRepo.directoryURL?.absoluteString)) - } - } - } - - it("should be able to clone a remote repository") { - let remoteRepoURL = URL(string: "https://github.com/libgit2/TestGitRepository.git") - let localURL = self.temporaryURL(forPurpose: "public-remote-clone") - let cloneResult = Repository.clone(from: remoteRepoURL!, to: localURL) - - expect(cloneResult.error).to(beNil()) - - if case .success(let clonedRepo) = cloneResult { - let remoteResult = clonedRepo.remote(named: "origin") - expect(remoteResult.error).to(beNil()) - - if case .success(let remote) = remoteResult { - expect(remote.URL).to(equal(remoteRepoURL?.absoluteString)) - } - } - } - - let env = ProcessInfo.processInfo.environment - - if let privateRepo = env["SG2TestPrivateRepo"], - let gitUsername = env["SG2TestUsername"], - let publicKey = env["SG2TestPublicKey"], - let privateKey = env["SG2TestPrivateKey"], - let passphrase = env["SG2TestPassphrase"] { - - it("should be able to clone a remote repository requiring credentials") { - let remoteRepoURL = URL(string: privateRepo) - let localURL = self.temporaryURL(forPurpose: "private-remote-clone") - let credentials = Credentials.sshMemory(username: gitUsername, - publicKey: publicKey, - privateKey: privateKey, - passphrase: passphrase) - - let cloneResult = Repository.clone(from: remoteRepoURL!, to: localURL, credentials: credentials) - - expect(cloneResult.error).to(beNil()) - - if case .success(let clonedRepo) = cloneResult { - let remoteResult = clonedRepo.remote(named: "origin") - expect(remoteResult.error).to(beNil()) - - if case .success(let remote) = remoteResult { - expect(remote.URL).to(equal(remoteRepoURL?.absoluteString)) - } - } - } - } - } - - describe("Repository.blob(_:)") { - it("should return the commit if it exists") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - - let result = repo.blob(oid) - expect(result.map { $0.oid }.value) == oid - } - - it("should error if the blob doesn't exist") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")! - - let result = repo.blob(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - - it("should error if the oid doesn't point to a blob") { - let repo = Fixtures.simpleRepository - // This is a tree in the repository - let oid = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let result = repo.blob(oid) - expect(result.error).notTo(beNil()) - } - } - - describe("Repository.commit(_:)") { - it("should return the commit if it exists") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let result = repo.commit(oid) - expect(result.map { $0.oid }.value) == oid - } - - it("should error if the commit doesn't exist") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")! - - let result = repo.commit(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - - it("should error if the oid doesn't point to a commit") { - let repo = Fixtures.simpleRepository - // This is a tree in the repository - let oid = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let result = repo.commit(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.tag(_:)") { - it("should return the tag if it exists") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - - let result = repo.tag(oid) - expect(result.map { $0.oid }.value) == oid - } - - it("should error if the tag doesn't exist") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")! - - let result = repo.tag(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - - it("should error if the oid doesn't point to a tag") { - let repo = Fixtures.simpleRepository - // This is a commit in the repository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let result = repo.tag(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.tree(_:)") { - it("should return the tree if it exists") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let result = repo.tree(oid) - expect(result.map { $0.oid }.value) == oid - } - - it("should error if the tree doesn't exist") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")! - - let result = repo.tree(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - - it("should error if the oid doesn't point to a tree") { - let repo = Fixtures.simpleRepository - // This is a commit in the repository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let result = repo.tree(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.object(_:)") { - it("should work with a blob") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - let blob = repo.blob(oid).value - let result = repo.object(oid) - expect(result.map { $0 as! Blob }.value) == blob - } - - it("should work with a commit") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - let commit = repo.commit(oid).value - let result = repo.object(oid) - expect(result.map { $0 as! Commit }.value) == commit - } - - it("should work with a tag") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - let tag = repo.tag(oid).value - let result = repo.object(oid) - expect(result.map { $0 as! Tag }.value) == tag - } - - it("should work with a tree") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - let tree = repo.tree(oid).value - let result = repo.object(oid) - expect(result.map { $0 as! Tree }.value) == tree - } - - it("should error if there's no object with that oid") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")! - let result = repo.object(oid) - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.object(from: PointerTo)") { - it("should work with commits") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let pointer = PointerTo(oid) - let commit = repo.commit(oid).value! - expect(repo.object(from: pointer).value) == commit - } - - it("should work with trees") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let pointer = PointerTo(oid) - let tree = repo.tree(oid).value! - expect(repo.object(from: pointer).value) == tree - } - - it("should work with blobs") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - - let pointer = PointerTo(oid) - let blob = repo.blob(oid).value! - expect(repo.object(from: pointer).value) == blob - } - - it("should work with tags") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - - let pointer = PointerTo(oid) - let tag = repo.tag(oid).value! - expect(repo.object(from: pointer).value) == tag - } - } - - describe("Repository.object(from: Pointer)") { - it("should work with commits") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")! - - let pointer = Pointer.commit(oid) - let commit = repo.commit(oid).value! - let result = repo.object(from: pointer).map { $0 as! Commit } - expect(result.value) == commit - } - - it("should work with trees") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")! - - let pointer = Pointer.tree(oid) - let tree = repo.tree(oid).value! - let result = repo.object(from: pointer).map { $0 as! Tree } - expect(result.value) == tree - } - - it("should work with blobs") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")! - - let pointer = Pointer.blob(oid) - let blob = repo.blob(oid).value! - let result = repo.object(from: pointer).map { $0 as! Blob } - expect(result.value) == blob - } - - it("should work with tags") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "57943b8ee00348180ceeedc960451562750f6d33")! - - let pointer = Pointer.tag(oid) - let tag = repo.tag(oid).value! - let result = repo.object(from: pointer).map { $0 as! Tag } - expect(result.value) == tag - } - } - - describe("Repository.allRemotes()") { - it("should return an empty list if there are no remotes") { - let repo = Fixtures.simpleRepository - let result = repo.allRemotes() - expect(result.value) == [] - } - - it("should return all the remotes") { - let repo = Fixtures.mantleRepository - let remotes = repo.allRemotes() - let names = remotes.map { $0.map { $0.name } } - expect(remotes.map { $0.count }.value) == 2 - expect(names.value).to(contain("origin", "upstream")) - } - } - - describe("Repository.remote(named:)") { - it("should return the remote if it exists") { - let repo = Fixtures.mantleRepository - let result = repo.remote(named: "upstream") - expect(result.map { $0.name }.value) == "upstream" - } - - it("should error if the remote doesn't exist") { - let repo = Fixtures.simpleRepository - let result = repo.remote(named: "nonexistent") - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.reference(named:)") { - it("should return a local branch if it exists") { - let name = "refs/heads/master" - let result = Fixtures.simpleRepository.reference(named: name) - expect(result.map { $0.longName }.value) == name - expect(result.value as? Branch).notTo(beNil()) - } - - it("should return a remote branch if it exists") { - let name = "refs/remotes/upstream/master" - let result = Fixtures.mantleRepository.reference(named: name) - expect(result.map { $0.longName }.value) == name - expect(result.value as? Branch).notTo(beNil()) - } - - it("should return a tag if it exists") { - let name = "refs/tags/tag-2" - let result = Fixtures.simpleRepository.reference(named: name) - expect(result.value?.longName).to(equal(name)) - expect(result.value as? TagReference).notTo(beNil()) - } - - it("should return the reference if it exists") { - let name = "refs/other-ref" - let result = Fixtures.simpleRepository.reference(named: name) - expect(result.value?.longName).to(equal(name)) - } - - it("should error if the reference doesn't exist") { - let result = Fixtures.simpleRepository.reference(named: "refs/heads/nonexistent") - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.localBranches()") { - it("should return all the local branches") { - let repo = Fixtures.simpleRepository - let expected = [ - repo.localBranch(named: "another-branch").value!, - repo.localBranch(named: "master").value!, - repo.localBranch(named: "yet-another-branch").value!, - ] - expect(repo.localBranches().value).to(equal(expected)) - } - } - - describe("Repository.remoteBranches()") { - it("should return all the remote branches") { - let repo = Fixtures.mantleRepository - let expectedNames = [ - "origin/2.0-development", - "origin/HEAD", - "origin/bump-config", - "origin/bump-xcconfigs", - "origin/github-reversible-transformer", - "origin/master", - "origin/mtlmanagedobject", - "origin/reversible-transformer", - "origin/subclassing-notes", - "upstream/2.0-development", - "upstream/bump-config", - "upstream/bump-xcconfigs", - "upstream/github-reversible-transformer", - "upstream/master", - "upstream/mtlmanagedobject", - "upstream/reversible-transformer", - "upstream/subclassing-notes", - ] - let expected = expectedNames.map { repo.remoteBranch(named: $0).value! } - let actual = repo.remoteBranches().value!.sorted { - return $0.longName.lexicographicallyPrecedes($1.longName) - } - expect(actual).to(equal(expected)) - expect(actual.map { $0.name }).to(equal(expectedNames)) - } - } - - describe("Repository.localBranch(named:)") { - it("should return the branch if it exists") { - let result = Fixtures.simpleRepository.localBranch(named: "master") - expect(result.value?.longName).to(equal("refs/heads/master")) - } - - it("should error if the branch doesn't exists") { - let result = Fixtures.simpleRepository.localBranch(named: "nonexistent") - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.remoteBranch(named:)") { - it("should return the branch if it exists") { - let result = Fixtures.mantleRepository.remoteBranch(named: "upstream/master") - expect(result.value?.longName).to(equal("refs/remotes/upstream/master")) - } - - it("should error if the branch doesn't exists") { - let result = Fixtures.simpleRepository.remoteBranch(named: "origin/nonexistent") - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.fetch(_:)") { - it("should fetch the data") { - let repo = Fixtures.mantleRepository - let remote = repo.remote(named: "origin").value! - expect(repo.fetch(remote).value).toNot(beNil()) - } - } - - describe("Repository.allTags()") { - it("should return all the tags") { - let repo = Fixtures.simpleRepository - let expected = [ - repo.tag(named: "tag-1").value!, - repo.tag(named: "tag-2").value!, - ] - expect(repo.allTags().value).to(equal(expected)) - } - } - - describe("Repository.tag(named:)") { - it("should return the tag if it exists") { - let result = Fixtures.simpleRepository.tag(named: "tag-2") - expect(result.value?.longName).to(equal("refs/tags/tag-2")) - } - - it("should error if the branch doesn't exists") { - let result = Fixtures.simpleRepository.tag(named: "nonexistent") - expect(result.error?.domain) == libGit2ErrorDomain - } - } - - describe("Repository.HEAD()") { - it("should work when on a branch") { - let result = Fixtures.simpleRepository.HEAD() - expect(result.value?.longName).to(equal("refs/heads/master")) - expect(result.value?.shortName).to(equal("master")) - expect(result.value as? Branch).notTo(beNil()) - } - - it("should work when on a detached HEAD") { - let result = Fixtures.detachedHeadRepository.HEAD() - expect(result.value?.longName).to(equal("HEAD")) - expect(result.value?.shortName).to(beNil()) - expect(result.value?.oid).to(equal(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")!)) - expect(result.value as? Reference).notTo(beNil()) - } - } - - describe("Repository.setHEAD(OID)") { - it("should set HEAD to the OID") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")! - expect(repo.HEAD().value?.shortName).to(equal("master")) - - expect(repo.setHEAD(oid).error).to(beNil()) - let HEAD = repo.HEAD().value - expect(HEAD?.longName).to(equal("HEAD")) - expect(HEAD?.oid).to(equal(oid)) - - expect(repo.setHEAD(repo.localBranch(named: "master").value!).error).to(beNil()) - expect(repo.HEAD().value?.shortName).to(equal("master")) - } - } - - describe("Repository.setHEAD(ReferenceType)") { - it("should set HEAD to a branch") { - let repo = Fixtures.detachedHeadRepository - let oid = repo.HEAD().value!.oid - expect(repo.HEAD().value?.longName).to(equal("HEAD")) - - let branch = repo.localBranch(named: "another-branch").value! - expect(repo.setHEAD(branch).error).to(beNil()) - expect(repo.HEAD().value?.shortName).to(equal(branch.name)) - - expect(repo.setHEAD(oid).error).to(beNil()) - expect(repo.HEAD().value?.longName).to(equal("HEAD")) - } - } - - describe("Repository.checkout()") { - // We're not really equipped to test this yet. :( - } - - describe("Repository.checkout(OID)") { - it("should set HEAD") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")! - expect(repo.HEAD().value?.shortName).to(equal("master")) - - expect(repo.checkout(oid, strategy: CheckoutStrategy.None).error).to(beNil()) - let HEAD = repo.HEAD().value - expect(HEAD?.longName).to(equal("HEAD")) - expect(HEAD?.oid).to(equal(oid)) - - expect(repo.checkout(repo.localBranch(named: "master").value!, strategy: CheckoutStrategy.None).error).to(beNil()) - expect(repo.HEAD().value?.shortName).to(equal("master")) - } - - it("should call block on progress") { - let repo = Fixtures.simpleRepository - let oid = OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")! - expect(repo.HEAD().value?.shortName).to(equal("master")) - - expect(repo.checkout(oid, strategy: .None, progress: { (_, completedSteps, totalSteps) -> Void in - expect(completedSteps).to(beLessThanOrEqualTo(totalSteps)) - }).error).to(beNil()) - - let HEAD = repo.HEAD().value - expect(HEAD?.longName).to(equal("HEAD")) - expect(HEAD?.oid).to(equal(oid)) - } - } - - describe("Repository.checkout(ReferenceType)") { - it("should set HEAD") { - let repo = Fixtures.detachedHeadRepository - let oid = repo.HEAD().value!.oid - expect(repo.HEAD().value?.longName).to(equal("HEAD")) - - let branch = repo.localBranch(named: "another-branch").value! - expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error).to(beNil()) - expect(repo.HEAD().value?.shortName).to(equal(branch.name)) - - expect(repo.checkout(oid, strategy: CheckoutStrategy.None).error).to(beNil()) - expect(repo.HEAD().value?.longName).to(equal("HEAD")) - } - } - - describe("Repository.allCommits(in:)") { - it("should return all (9) commits") { - let repo = Fixtures.simpleRepository - let branches = repo.localBranches().value! - let expectedCount = 9 - let expectedMessages: [String] = [ - "List branches in README\n", - "Create a README\n", - "Merge branch 'alphabetize'\n", - "Alphabetize branches\n", - "List new branches\n", - "List branches in README\n", - "Create a README\n", - "List branches in README\n", - "Create a README\n", - ] - var commitMessages: [String] = [] - for branch in branches { - for commit in repo.commits(in: branch) { - commitMessages.append(commit.value!.message) - } - } - expect(commitMessages.count).to(equal(expectedCount)) - expect(commitMessages).to(equal(expectedMessages)) - } - } - - describe("Repository.add") { - it("Should add the modification under a path") { - let repo = Fixtures.simpleRepository - let branch = repo.localBranch(named: "master").value! - expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error).to(beNil()) - - // make a change to README - let readmeURL = repo.directoryURL!.appendingPathComponent("README.md") - let readmeData = try! Data(contentsOf: readmeURL) - defer { try! readmeData.write(to: readmeURL) } - - try! "different".data(using: .utf8)?.write(to: readmeURL) - - let status = repo.status() - expect(status.value?.count).to(equal(1)) - expect(status.value!.first!.status).to(equal(.workTreeModified)) - - expect(repo.add(path: "README.md").error).to(beNil()) - - let newStatus = repo.status() - expect(newStatus.value?.count).to(equal(1)) - expect(newStatus.value!.first!.status).to(equal(.indexModified)) - } - - it("Should add an untracked file under a path") { - let repo = Fixtures.simpleRepository - let branch = repo.localBranch(named: "master").value! - expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error).to(beNil()) - - // make a change to README - let untrackedURL = repo.directoryURL!.appendingPathComponent("untracked") - try! "different".data(using: .utf8)?.write(to: untrackedURL) - defer { try! FileManager.default.removeItem(at: untrackedURL) } - - expect(repo.add(path: ".").error).to(beNil()) - - let newStatus = repo.status() - expect(newStatus.value?.count).to(equal(1)) - expect(newStatus.value!.first!.status).to(equal(.indexNew)) - } - } - - describe("Repository.commit") { - it("Should perform a simple commit with specified signature") { - let repo = Fixtures.simpleRepository - let branch = repo.localBranch(named: "master").value! - expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error).to(beNil()) - - // make a change to README - let untrackedURL = repo.directoryURL!.appendingPathComponent("untrackedtest") - try! "different".data(using: .utf8)?.write(to: untrackedURL) - - expect(repo.add(path: ".").error).to(beNil()) - - let signature = Signature( - name: "swiftgit2", - email: "foobar@example.com", - time: Date(timeIntervalSince1970: 1525200858), - timeZone: TimeZone(secondsFromGMT: 3600)! - ) - let message = "Test Commit" - expect(repo.commit(message: message, signature: signature).error).to(beNil()) - let updatedBranch = repo.localBranch(named: "master").value! - expect(repo.commits(in: updatedBranch).next()?.value?.author).to(equal(signature)) - expect(repo.commits(in: updatedBranch).next()?.value?.committer).to(equal(signature)) - expect(repo.commits(in: updatedBranch).next()?.value?.message).to(equal("\(message)\n")) - expect(repo.commits(in: updatedBranch).next()?.value?.oid.description) - .to(equal("7d6b2d7492f29aee48022387f96dbfe996d435fe")) - - // should be clean now - let newStatus = repo.status() - expect(newStatus.value?.count).to(equal(0)) - } - } - - describe("Repository.status") { - it("Should accurately report status for repositories with no status") { - let expectedCount = 0 - - let repo = Fixtures.mantleRepository - let branch = repo.localBranch(named: "master").value! - expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error).to(beNil()) - - let status = repo.status() - - expect(status.value?.count).to(equal(expectedCount)) - } - - it("Should accurately report status for repositories with status") { - let expectedCount = 6 - let expectedNewFilePaths = [ - "stage-file-1", - "stage-file-2", - "stage-file-3", - "stage-file-4", - "stage-file-5", - ] - let expectedOldFilePaths = [ - "stage-file-1", - "stage-file-2", - "stage-file-3", - "stage-file-4", - "stage-file-5", - ] - let expectedUntrackedFiles = [ - "unstaged-file", - ] - - let repoWithStatus = Fixtures.sharedInstance.repository(named: "repository-with-status") - let branchWithStatus = repoWithStatus.localBranch(named: "master").value! - expect(repoWithStatus.checkout(branchWithStatus, strategy: CheckoutStrategy.None).error).to(beNil()) - - let statuses = repoWithStatus.status().value! - - var newFilePaths: [String] = [] - for status in statuses { - if let path = status.headToIndex?.newFile?.path { - newFilePaths.append(path) - } - } - var oldFilePaths: [String] = [] - for status in statuses { - if let path = status.headToIndex?.oldFile?.path { - oldFilePaths.append(path) - } - } - - var newUntrackedFilePaths: [String] = [] - for status in statuses { - if let path = status.indexToWorkDir?.newFile?.path { - newUntrackedFilePaths.append(path) - } - } - - expect(statuses.count).to(equal(expectedCount)) - expect(newFilePaths).to(equal(expectedNewFilePaths)) - expect(oldFilePaths).to(equal(expectedOldFilePaths)) - expect(newUntrackedFilePaths).to(equal(expectedUntrackedFiles)) - } - } - - describe("Repository.diff") { - it("Should have accurate delta information") { - let expectedCount = 13 - let expectedNewFilePaths = [ - ".gitmodules", - "Cartfile", - "Cartfile.lock", - "Cartfile.private", - "Cartfile.resolved", - "Carthage.checkout/Nimble", - "Carthage.checkout/Quick", - "Carthage.checkout/xcconfigs", - "Carthage/Checkouts/Nimble", - "Carthage/Checkouts/Quick", - "Carthage/Checkouts/xcconfigs", - "Mantle.xcodeproj/project.pbxproj", - "Mantle.xcworkspace/contents.xcworkspacedata", - ] - let expectedOldFilePaths = [ - ".gitmodules", - "Cartfile", - "Cartfile.lock", - "Cartfile.private", - "Cartfile.resolved", - "Carthage.checkout/Nimble", - "Carthage.checkout/Quick", - "Carthage.checkout/xcconfigs", - "Carthage/Checkouts/Nimble", - "Carthage/Checkouts/Quick", - "Carthage/Checkouts/xcconfigs", - "Mantle.xcodeproj/project.pbxproj", - "Mantle.xcworkspace/contents.xcworkspacedata", - ] - - let repo = Fixtures.mantleRepository - let branch = repo.localBranch(named: "master").value! - expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error).to(beNil()) - - let head = repo.HEAD().value! - let commit = repo.object(head.oid).value! as! Commit - let diff = repo.diff(for: commit).value! - - let newFilePaths = diff.deltas.map { $0.newFile!.path } - let oldFilePaths = diff.deltas.map { $0.oldFile!.path } - - expect(diff.deltas.count).to(equal(expectedCount)) - expect(newFilePaths).to(equal(expectedNewFilePaths)) - expect(oldFilePaths).to(equal(expectedOldFilePaths)) - } - - it("Should handle initial commit well") { - let expectedCount = 2 - let expectedNewFilePaths = [ - ".gitignore", - "README.md", - ] - let expectedOldFilePaths = [ - ".gitignore", - "README.md", - ] - - let repo = Fixtures.mantleRepository - expect(repo.checkout(OID(string: "047b931bd7f5478340cef5885a6fff713005f4d6")!, - strategy: CheckoutStrategy.None).error).to(beNil()) - let head = repo.HEAD().value! - let initalCommit = repo.object(head.oid).value! as! Commit - let diff = repo.diff(for: initalCommit).value! - - var newFilePaths: [String] = [] - for delta in diff.deltas { - newFilePaths.append((delta.newFile?.path)!) - } - var oldFilePaths: [String] = [] - for delta in diff.deltas { - oldFilePaths.append((delta.oldFile?.path)!) - } - - expect(diff.deltas.count).to(equal(expectedCount)) - expect(newFilePaths).to(equal(expectedNewFilePaths)) - expect(oldFilePaths).to(equal(expectedOldFilePaths)) - } - - it("Should handle merge commits well") { - let expectedCount = 20 - let expectedNewFilePaths = [ - "Mantle.xcodeproj/project.pbxproj", - "Mantle/MTLModel+NSCoding.m", - "Mantle/Mantle.h", - "Mantle/NSArray+MTLHigherOrderAdditions.h", - "Mantle/NSArray+MTLHigherOrderAdditions.m", - "Mantle/NSArray+MTLManipulationAdditions.m", - "Mantle/NSDictionary+MTLHigherOrderAdditions.h", - "Mantle/NSDictionary+MTLHigherOrderAdditions.m", - "Mantle/NSDictionary+MTLManipulationAdditions.m", - "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.h", - "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.m", - "Mantle/NSOrderedSet+MTLHigherOrderAdditions.h", - "Mantle/NSOrderedSet+MTLHigherOrderAdditions.m", - "Mantle/NSSet+MTLHigherOrderAdditions.h", - "Mantle/NSSet+MTLHigherOrderAdditions.m", - "Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m", - "MantleTests/MTLHigherOrderAdditionsSpec.m", - "MantleTests/MTLNotificationCenterAdditionsSpec.m", - "MantleTests/MTLPredefinedTransformerAdditionsSpec.m", - "README.md", - ] - let expectedOldFilePaths = [ - "Mantle.xcodeproj/project.pbxproj", - "Mantle/MTLModel+NSCoding.m", - "Mantle/Mantle.h", - "Mantle/NSArray+MTLHigherOrderAdditions.h", - "Mantle/NSArray+MTLHigherOrderAdditions.m", - "Mantle/NSArray+MTLManipulationAdditions.m", - "Mantle/NSDictionary+MTLHigherOrderAdditions.h", - "Mantle/NSDictionary+MTLHigherOrderAdditions.m", - "Mantle/NSDictionary+MTLManipulationAdditions.m", - "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.h", - "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.m", - "Mantle/NSOrderedSet+MTLHigherOrderAdditions.h", - "Mantle/NSOrderedSet+MTLHigherOrderAdditions.m", - "Mantle/NSSet+MTLHigherOrderAdditions.h", - "Mantle/NSSet+MTLHigherOrderAdditions.m", - "Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m", - "MantleTests/MTLHigherOrderAdditionsSpec.m", - "MantleTests/MTLNotificationCenterAdditionsSpec.m", - "MantleTests/MTLPredefinedTransformerAdditionsSpec.m", - "README.md", - ] - - let repo = Fixtures.mantleRepository - expect(repo.checkout(OID(string: "d0d9c13da5eb5f9e8cf2a9f1f6ca3bdbe975b57d")!, - strategy: CheckoutStrategy.None).error).to(beNil()) - let head = repo.HEAD().value! - let initalCommit = repo.object(head.oid).value! as! Commit - let diff = repo.diff(for: initalCommit).value! - - var newFilePaths: [String] = [] - for delta in diff.deltas { - newFilePaths.append((delta.newFile?.path)!) - } - var oldFilePaths: [String] = [] - for delta in diff.deltas { - oldFilePaths.append((delta.oldFile?.path)!) - } - - expect(diff.deltas.count).to(equal(expectedCount)) - expect(newFilePaths).to(equal(expectedNewFilePaths)) - expect(oldFilePaths).to(equal(expectedOldFilePaths)) - } - } - } - - static func temporaryURL(forPurpose purpose: String) -> URL { - let globallyUniqueString = ProcessInfo.processInfo.globallyUniqueString - let path = "\(NSTemporaryDirectory())\(globallyUniqueString)_\(purpose)" - return URL(fileURLWithPath: path) - } +@Suite("Repository") class RepositorySpec { + @Suite("Repository.Type.at(_:)") class At: FixturesSpec { + @Test("should work if the repo exists") func exists() throws { + let repo = try fixtures.simpleRepository() + #expect(repo.directoryURL != nil) + } + + @Test("should fail if the repo doesn't exist") func notExists() { + let url = URL(fileURLWithPath: "blah") + let result = Repository.at(url) + #expect(result.error?.domain == libGit2ErrorDomain) + #expect(result.error?.localizedDescription.starts(with: "failed to resolve path") == true) + } + } + + @Suite("Repository.Type.isValid(url:)") class IsValid: FixturesSpec { + @Test("should return true if the repo exists") func exists() throws { + let repositoryURL = try #require(fixtures.simpleRepository().directoryURL, + "Fixture setup broken: Repository does not exist") + + let result = Repository.isValid(url: repositoryURL) + + #expect(result.error == nil) + + let isValid = try result.get() + #expect(isValid == true) + } + + @Test("should return false if the directory does not contain a repo") func notExists() throws { + let tmpURL = URL(fileURLWithPath: "/dev/null") + let result = Repository.isValid(url: tmpURL) + + #expect(result.error == nil) + + let isValid = try result.get() + #expect(isValid == false) + } + + @Test("should return error if .git is not readable") func notReadable() throws { + let localURL = temporaryURL(forPurpose: "git-isValid-unreadable").appendingPathComponent(".git") + let nonReadablePermissions: [FileAttributeKey: Any] = [.posixPermissions: 0o077] + try FileManager.default.createDirectory( + at: localURL, + withIntermediateDirectories: true, + attributes: nonReadablePermissions) + let result = Repository.isValid(url: localURL) + + #expect(result.value == nil) + #expect(result.error != nil) + } + } + + @Suite("Repository.Type.create(at:)") class Create: FixturesSpec { + @Test("should create a new repo at the specified location") func success() throws { + let localURL = temporaryURL(forPurpose: "local-create") + let result = Repository.create(at: localURL) + + #expect(result.error == nil) + + let clonedRepo = try result.get() + #expect(clonedRepo.directoryURL != nil) + } + } + + @Suite("Repository.Type.clone(from:to:)") class Clone: FixturesSpec { + @Test("should handle local clones") func localClone() throws { + let remoteRepo = try fixtures.simpleRepository() + let localURL = temporaryURL(forPurpose: "local-clone") + let result = Repository.clone(from: try #require(remoteRepo.directoryURL), to: localURL, localClone: true) + + #expect(result.error == nil) + + let clonedRepo = try result.get() + #expect(clonedRepo.directoryURL != nil) + } + + @Test("should handle bare clones") func bareClone() throws { + let remoteRepo = try fixtures.simpleRepository() + let localURL = temporaryURL(forPurpose: "bare-clone") + let result = Repository.clone(from: remoteRepo.directoryURL!, to: localURL, localClone: true, bare: true) + + #expect(result.error == nil) + + let clonedRepo = try result.get() + #expect(clonedRepo.directoryURL == nil) + } + + @Test("should have set a valid remote url") func validRemoteURL() throws { + let remoteRepo = try fixtures.simpleRepository() + let localURL = temporaryURL(forPurpose: "valid-remote-clone") + let cloneResult = Repository.clone(from: remoteRepo.directoryURL!, to: localURL, localClone: true) + + #expect(cloneResult.error == nil) + + let clonedRepo = try cloneResult.get() + let remoteResult = clonedRepo.remote(named: "origin") + #expect(remoteResult.error == nil) + + let remote = try remoteResult.get() + #expect(remote.URL == remoteRepo.directoryURL?.absoluteString) + } + + @Test("should be able to clone a remote repository") func cloneRemoteRepository() throws { + let remoteRepoURL = try #require(URL(string: "https://github.com/libgit2/TestGitRepository.git")) + let localURL = temporaryURL(forPurpose: "public-remote-clone") + let cloneResult = Repository.clone(from: remoteRepoURL, to: localURL) + + #expect(cloneResult.error == nil) + + let clonedRepo = try cloneResult.get() + let remoteResult = clonedRepo.remote(named: "origin") + #expect(remoteResult.error == nil) + + let remote = try remoteResult.get() + #expect(remote.URL == remoteRepoURL.absoluteString) + } + +// let env = ProcessInfo.processInfo.environment +// +// if let privateRepo = env["SG2TestPrivateRepo"], +// let gitUsername = env["SG2TestUsername"], +// let publicKey = env["SG2TestPublicKey"], +// let privateKey = env["SG2TestPrivateKey"], +// let passphrase = env["SG2TestPassphrase"] { +// +// @Test("should be able to clone a remote repository requiring credentials") { +// let remoteRepoURL = URL(string: privateRepo) +// let localURL = temporaryURL(forPurpose: "private-remote-clone") +// let credentials = Credentials.sshMemory(username: gitUsername, +// publicKey: publicKey, +// privateKey: privateKey, +// passphrase: passphrase) +// +// let cloneResult = Repository.clone(from: remoteRepoURL!, to: localURL, credentials: credentials) +// +// #expect(cloneResult.error == nil) +// +// if case .success(let clonedRepo) = cloneResult { +// let remoteResult = clonedRepo.remote(named: "origin") +// #expect(remoteResult.error == nil) +// +// if case .success(let remote) = remoteResult { +// #expect(remote.URL == remoteRepoURL?.absoluteString) +// } +// } +// } +// } + } + + @Suite("Repository.blob(_:)") class BlobSpec: FixturesSpec { + @Test("should return the commit if it exists") func exists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + + let result = repo.blob(oid) + #expect(result.map { $0.oid }.value == oid) + } + + @Test("should error if the blob doesn't exist") func notExists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + let result = repo.blob(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + + @Test("should error if the oid doesn't point to a blob") func notABlob() throws { + let repo = try fixtures.simpleRepository() + // This is a tree in the repository + let oid = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let result = repo.blob(oid) + #expect(result.error != nil) + } + } + + @Suite("Repository.commit(_:)") class CommitSpec: FixturesSpec { + @Test("should return the commit if it exists") func exists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let result = repo.commit(oid) + #expect(result.map { $0.oid }.value == oid) + } + + @Test("should error if the commit doesn't exist") func notExists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + let result = repo.commit(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + + @Test("should error if the oid doesn't point to a commit") func notACommit() throws { + let repo = try fixtures.simpleRepository() + // This is a tree in the repository + let oid = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let result = repo.commit(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.tag(_:)") class TagSpec: FixturesSpec { + @Test("should return the tag if it exists") func exists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + + let result = repo.tag(oid) + #expect(result.map { $0.oid }.value == oid) + } + + @Test("should error if the tag doesn't exist") func notExists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + let result = repo.tag(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + + @Test("should error if the oid doesn't point to a tag") func notATag() throws { + let repo = try fixtures.simpleRepository() + // This is a commit in the repository + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let result = repo.tag(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.tree(_:)") class TreeSpec: FixturesSpec { + @Test("should return the tree if it exists") func exists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let result = repo.tree(oid) + #expect(result.map { $0.oid }.value == oid) + } + + @Test("should error if the tree doesn't exist") func notExists() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + + let result = repo.tree(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + + @Test("should error if the oid doesn't point to a tree") func notATree() throws { + let repo = try fixtures.simpleRepository() + // This is a commit in the repository + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let result = repo.tree(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.object(_:)") class ObjectSpec: FixturesSpec { + @Test("should work with a blob") func blob() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + let blob = repo.blob(oid).value + let result = repo.object(oid) + #expect(result.map { $0 as! Blob }.value == blob) + } + + @Test("should work with a commit") func commit() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + let commit = repo.commit(oid).value + let result = repo.object(oid) + #expect(result.map { $0 as! Commit }.value == commit) + } + +// TODO +// @Test("should work with a tag") func tag() throws { +// let repo = try fixtures.simpleRepository() +// let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) +// let tag = repo.tag(oid).value +// let result = repo.object(oid) +// #expect(result.map { $0 as! Tag }.value == tag) +// } + + @Test("should work with a tree") func tree() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + let tree = repo.tree(oid).value + let result = repo.object(oid) + #expect(result.map { $0 as! Tree }.value == tree) + } + + @Test("should error if there's no object with that oid") func nonExisting() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) + let result = repo.object(oid) + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.object(from: PointerTo)") class PointerToSpec: FixturesSpec { + @Test("should work with commits") func commit() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let pointer = PointerTo(oid) + let commit = try #require(repo.commit(oid).value) + #expect(repo.object(from: pointer).value == commit) + } + + @Test("should work with trees") func tree() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let pointer = PointerTo(oid) + let tree = try #require(repo.tree(oid).value) + #expect(repo.object(from: pointer).value == tree) + } + + @Test("should work with blobs") func blob() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + + let pointer = PointerTo(oid) + let blob = try #require(repo.blob(oid).value) + #expect(repo.object(from: pointer).value == blob) + } + +// TODO +// @Test("should work with tags") func tag() throws { +// let repo = try fixtures.simpleRepository() +// let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) +// +// let pointer = PointerTo(oid) +// let tag = try #require(repo.tag(oid).value) +// #expect(repo.object(from: pointer).value == tag) +// } + } + + @Suite("Repository.object(from: Pointer)") class FromPointer: FixturesSpec { + @Test("should work with commits") func commit() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "dc220a3f0c22920dab86d4a8d3a3cb7e69d6205a")) + + let pointer = Pointer.commit(oid) + let commit = try #require(repo.commit(oid).value) + let result = repo.object(from: pointer).map { $0 as! Commit } + #expect(result.value == commit) + } + + @Test("should work with trees") func tree() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "f93e3a1a1525fb5b91020da86e44810c87a2d7bc")) + + let pointer = Pointer.tree(oid) + let tree = try #require(repo.tree(oid).value) + let result = repo.object(from: pointer).map { $0 as! Tree } + #expect(result.value == tree) + } + + @Test("should work with blobs") func blob() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "41078396f5187daed5f673e4a13b185bbad71fba")) + + let pointer = Pointer.blob(oid) + let blob = try #require(repo.blob(oid).value) + let result = repo.object(from: pointer).map { $0 as! Blob } + #expect(result.value == blob) + } + +// TODO +// @Test("should work with tags") func tag() throws { +// let repo = try fixtures.simpleRepository() +// let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) +// +// let pointer = Pointer.tag(oid) +// let tag = try #require(repo.tag(oid).value) +// let result = repo.object(from: pointer).map { $0 as! Tag } +// #expect(result.value == tag) +// } + } + + @Suite("Repository.allRemotes()") class AllRemotes: FixturesSpec { + @Test("should return an empty list if there are no remotes") func empty() throws { + let repo = try fixtures.simpleRepository() + let result = repo.allRemotes() + #expect(result.value == []) + } + + @Test("should return all the remotes") func all() throws { + let repo = try fixtures.mantleRepository() + let remotes = repo.allRemotes() + let names = remotes.map { $0.map { $0.name } } + #expect(remotes.map { $0.count }.value == 2) + #expect(names.value == ["origin", "upstream"]) + } + } + + @Suite("Repository.remote(named:)") class NamedRemote: FixturesSpec { + @Test("should return the remote if it exists") func exists() throws { + let repo = try fixtures.mantleRepository() + let result = repo.remote(named: "upstream") + #expect(result.map { $0.name }.value == "upstream") + } + + @Test("should error if the remote doesn't exist") func notExists() throws { + let repo = try fixtures.simpleRepository() + let result = repo.remote(named: "nonexistent") + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.reference(named:)") class NamedReference: FixturesSpec { + @Test("should return a local branch if it exists") func localBranch() throws { + let name = "refs/heads/master" + let result = try fixtures.simpleRepository().reference(named: name) + #expect(result.map { $0.longName }.value == name) + #expect(result.value as? Branch != nil) + } + + @Test("should return a remote branch if it exists") func remoteBranch() throws { + let name = "refs/remotes/upstream/master" + let result = try fixtures.mantleRepository().reference(named: name) + #expect(result.map { $0.longName }.value == name) + #expect(result.value as? Branch != nil) + } + + @Test("should return a tag if it exists") func tag() throws { + let name = "refs/tags/tag-2" + let result = try fixtures.simpleRepository().reference(named: name) + #expect(result.value?.longName == name) + #expect(result.value as? TagReference != nil) + } + + @Test("should return the reference if it exists") func reference() throws { + let name = "refs/other-ref" + let result = try fixtures.simpleRepository().reference(named: name) + #expect(result.value?.longName == name) + } + + @Test("should error if the reference doesn't exist") func notExists() throws { + let result = try fixtures.simpleRepository().reference(named: "refs/heads/nonexistent") + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.localBranches()") class LocalBranches: FixturesSpec { + @Test("should return all the local branches") func localBranches() throws { + let repo = try fixtures.simpleRepository() + let expected = [ + try #require(repo.localBranch(named: "another-branch").value), + try #require(repo.localBranch(named: "master").value), + try #require(repo.localBranch(named: "yet-another-branch").value), + ] + #expect(repo.localBranches().value == expected) + } + } + + @Suite("Repository.remoteBranches()") class RemoteBranches: FixturesSpec { + @Test("should return all the remote branches") func remoteBranches() throws { + let repo = try fixtures.mantleRepository() + let expectedNames = [ + "origin/2.0-development", + "origin/HEAD", + "origin/bump-config", + "origin/bump-xcconfigs", + "origin/github-reversible-transformer", + "origin/master", + "origin/mtlmanagedobject", + "origin/reversible-transformer", + "origin/subclassing-notes", + "upstream/2.0-development", + "upstream/bump-config", + "upstream/bump-xcconfigs", + "upstream/github-reversible-transformer", + "upstream/master", + "upstream/mtlmanagedobject", + "upstream/reversible-transformer", + "upstream/subclassing-notes", + ] + let expected = try expectedNames.map { try #require(repo.remoteBranch(named: $0).value) } + let remoteBranches = try #require(repo.remoteBranches().value) + let actual = remoteBranches.sorted { + return $0.longName.lexicographicallyPrecedes($1.longName) + } + #expect(actual == expected) + #expect(actual.map { $0.name } == expectedNames) + } + } + + @Suite("Repository.localBranch(named:)") class LocalBranch: FixturesSpec { + @Test("should return the branch if it exists") func exists() throws { + let result = try fixtures.simpleRepository().localBranch(named: "master") + #expect(result.value?.longName == "refs/heads/master") + } + + @Test("should error if the branch doesn't exists") func notExists() throws { + let result = try fixtures.simpleRepository().localBranch(named: "nonexistent") + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.remoteBranch(named:)") class RemoteBranch: FixturesSpec { + @Test("should return the branch if it exists") func exists() throws { + let result = try fixtures.mantleRepository().remoteBranch(named: "upstream/master") + #expect(result.value?.longName == "refs/remotes/upstream/master") + } + + @Test("should error if the branch doesn't exists") func notExists() throws { + let result = try fixtures.simpleRepository().remoteBranch(named: "origin/nonexistent") + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.fetch(_:)") class Fetch: FixturesSpec { + @Test("should fetch the data") func fetch() throws { + let repo = try fixtures.mantleRepository() + let remote = try #require(repo.remote(named: "origin").value) + #expect(repo.fetch(remote).value != nil) + } + } + + @Suite("Repository.allTags()") class AllTags: FixturesSpec { + @Test("should return all the tags") func allTags() throws { + let repo = try fixtures.simpleRepository() + let expected = [ + try #require(repo.tag(named: "tag-1").value), + try #require(repo.tag(named: "tag-2").value), + ] + #expect(repo.allTags().value == expected) + } + } + + @Suite("Repository.tag(named:)") class NamedTag: FixturesSpec { + @Test("should return the tag if it exists") func exists() throws { + let result = try fixtures.simpleRepository().tag(named: "tag-2") + #expect(result.value?.longName == "refs/tags/tag-2") + } + + @Test("should error if the branch doesn't exists") func notExists() throws { + let result = try fixtures.simpleRepository().tag(named: "nonexistent") + #expect(result.error?.domain == libGit2ErrorDomain) + } + } + + @Suite("Repository.HEAD()") class Head: FixturesSpec { + @Test("should work when on a branch") func onBranch() throws { + let result = try fixtures.simpleRepository().HEAD() + #expect(result.value?.longName == "refs/heads/master") + #expect(result.value?.shortName == "master") + #expect(result.value as? Branch != nil) + } + + @Test("should work when on a detached HEAD") func detached() throws { + let result = try fixtures.detachedHeadRepository().HEAD() + #expect(result.value?.longName == "HEAD") + #expect(result.value?.shortName == nil) + let expectedOID = try #require(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")) + #expect(result.value?.oid == expectedOID) + #expect(result.value as? Reference != nil) + } + } + + @Suite("Repository.setHEAD(OID)") class SetHeadToOID: FixturesSpec { + @Test("should set HEAD to the OID") func setHeadToOID() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")) + #expect(repo.HEAD().value?.shortName == "master") + + #expect(repo.setHEAD(oid).error == nil) + let HEAD = repo.HEAD().value + #expect(HEAD?.longName == "HEAD") + #expect(HEAD?.oid == oid) + + let branch = try #require(repo.localBranch(named: "master").value) + #expect(repo.setHEAD(branch).error == nil) + #expect(repo.HEAD().value?.shortName == "master") + } + } + + @Suite("Repository.setHEAD(ReferenceType)") class SetHeadToReferenceType: FixturesSpec { + @Test("should set HEAD to a branch") func setHeadToBranch() throws { + let repo = try fixtures.detachedHeadRepository() + let oid = try #require(repo.HEAD().value?.oid) + #expect(repo.HEAD().value?.longName == "HEAD") + + let branch = try #require(repo.localBranch(named: "another-branch").value) + #expect(repo.setHEAD(branch).error == nil) + #expect(repo.HEAD().value?.shortName == branch.name) + + #expect(repo.setHEAD(oid).error == nil) + #expect(repo.HEAD().value?.longName == "HEAD") + } + } + + @Suite("Repository.checkout()") class Checkout { + // We're not really equipped to test this yet. :( + } + + @Suite("Repository.checkout(OID)") class CheckoutOID: FixturesSpec { + @Test("should set HEAD") func setHEAD() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")) + #expect(repo.HEAD().value?.shortName == "master") + + #expect(repo.checkout(oid, strategy: CheckoutStrategy.None).error == nil) + let HEAD = repo.HEAD().value + #expect(HEAD?.longName == "HEAD") + #expect(HEAD?.oid == oid) + + let branch = try #require(repo.localBranch(named: "master").value) + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + #expect(repo.HEAD().value?.shortName == "master") + } + + @Test("should call block on progress") func progressCallback() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "315b3f344221db91ddc54b269f3c9af422da0f2e")) + #expect(repo.HEAD().value?.shortName == "master") + + let result = repo.checkout(oid, strategy: .None, progress: { (_, completedSteps, totalSteps) -> Void in + #expect(completedSteps <= totalSteps) + }) + #expect(result.error == nil) + + let HEAD = repo.HEAD().value + #expect(HEAD?.longName == "HEAD") + #expect(HEAD?.oid == oid) + } + } + + @Suite("Repository.checkout(ReferenceType)") class CheckoutReferenceType: FixturesSpec { + @Test("should set HEAD") func setHEAD() throws { + let repo = try fixtures.detachedHeadRepository() + let oid = try #require(repo.HEAD().value?.oid) + #expect(repo.HEAD().value?.longName == "HEAD") + + let branch = try #require(repo.localBranch(named: "another-branch").value) + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + #expect(repo.HEAD().value?.shortName == branch.name) + + #expect(repo.checkout(oid, strategy: CheckoutStrategy.None).error == nil) + #expect(repo.HEAD().value?.longName == "HEAD") + } + } + + @Suite("Repository.allCommits(in:)") class AllCommits: FixturesSpec { + @Test("should return all (9) commits") func allCommits() throws { + let repo = try fixtures.simpleRepository() + let branches = try #require(repo.localBranches().value) + let expectedCount = 9 + let expectedMessages: [String] = [ + "List branches in README\n", + "Create a README\n", + "Merge branch 'alphabetize'\n", + "Alphabetize branches\n", + "List new branches\n", + "List branches in README\n", + "Create a README\n", + "List branches in README\n", + "Create a README\n", + ] + let commitMessages: [String] = branches.flatMap { branch in + repo.commits(in: branch).compactMap { commit in + commit.value?.message + } + } + #expect(commitMessages.count == expectedCount) + #expect(commitMessages == expectedMessages) + } + } + + @Suite("Repository.add") class Add: FixturesSpec { + @Test("Should add the modification under a path") func addPath() throws { + let repo = try fixtures.simpleRepository() + let branch = try #require(repo.localBranch(named: "master").value) + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + + // make a change to README + let readmeURL = try #require(repo.directoryURL?.appendingPathComponent("README.md")) + let data = try #require("different".data(using: .utf8)) + try data.write(to: readmeURL) + + let status = try #require(repo.status().value) + #expect(status.count == 1) + #expect(status.first?.status == .workTreeModified) + + #expect(repo.add(path: "README.md").error == nil) + + let newStatus = try #require(repo.status().value) + #expect(newStatus.count == 1) + #expect(newStatus.first?.status == .indexModified) + } + + @Test("Should add an untracked file under a path") func addUntrackedPath() throws { + let repo = try fixtures.simpleRepository() + let branch = try #require(repo.localBranch(named: "master").value) + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + + // make a change to README + let untrackedURL = try #require(repo.directoryURL?.appendingPathComponent("untracked")) + let data = try #require("different".data(using: .utf8)) + try data.write(to: untrackedURL) + + #expect(repo.add(path: ".").error == nil) + + let newStatus = try #require(repo.status().value) + #expect(newStatus.count == 1) + #expect(newStatus.first?.status == .indexNew) + } + + deinit { + let repo = try? fixtures.simpleRepository() + + if let untrackedURL = repo?.directoryURL?.appendingPathComponent("untracked") { + try? FileManager.default.removeItem(at: untrackedURL) + } + + if let readmeURL = repo?.directoryURL?.appendingPathComponent("README.md") { + try? FileManager.default.removeItem(at: readmeURL) + } + } + } + + @Suite("Repository.commit") class RepositoryCommit: FixturesSpec { + @Test("Should perform a simple commit with specified signature") func commit() throws { + let repo = try fixtures.simpleRepository() + let branch = repo.localBranch(named: "master").value! + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + + // make a change to README + let untrackedURL = try #require(repo.directoryURL?.appendingPathComponent("untrackedtest")) + let data = try #require("different".data(using: .utf8)) + try data.write(to: untrackedURL) + + #expect(repo.add(path: ".").error == nil) + + let signature = Signature( + name: "swiftgit2", + email: "foobar@example.com", + time: Date(timeIntervalSince1970: 1525200858), + timeZone: TimeZone(secondsFromGMT: 3600)! + ) + let message = "Test Commit" + #expect(repo.commit(message: message, signature: signature).error == nil) + let updatedBranch = repo.localBranch(named: "master").value! + #expect(repo.commits(in: updatedBranch).next()?.value?.author == signature) + #expect(repo.commits(in: updatedBranch).next()?.value?.committer == signature) + #expect(repo.commits(in: updatedBranch).next()?.value?.message == "\(message)\n") + #expect(repo.commits(in: updatedBranch).next()?.value?.oid.description == + "7d6b2d7492f29aee48022387f96dbfe996d435fe") + + // should be clean now + let newStatus = try #require(repo.status().value) + #expect(newStatus.count == 0) + } + } + + @Suite("Repository.status") class RepositoryStatus: FixturesSpec { + @Test("Should accurately report status for repositories with no status") func noStatus() throws { + let expectedCount = 0 + + let repo = try fixtures.mantleRepository() + let branch = try #require(repo.localBranch(named: "master").value) + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + + let status = repo.status() + + #expect(status.value?.count == expectedCount) + } + + @Test("Should accurately report status for repositories with status") func withStatus() throws { + let expectedCount = 6 + let expectedNewFilePaths = [ + "stage-file-1", + "stage-file-2", + "stage-file-3", + "stage-file-4", + "stage-file-5", + ] + let expectedOldFilePaths = [ + "stage-file-1", + "stage-file-2", + "stage-file-3", + "stage-file-4", + "stage-file-5", + ] + let expectedUntrackedFiles = [ + "unstaged-file", + ] + + let repoWithStatus = try fixtures.repository(named: "repository-with-status") + let branchWithStatus = try #require(repoWithStatus.localBranch(named: "master").value) + #expect(repoWithStatus.checkout(branchWithStatus, strategy: CheckoutStrategy.None).error == nil) + + let statuses = repoWithStatus.status().value! + + let newFilePaths: [String] = statuses.compactMap { status in + status.headToIndex?.newFile?.path + } + let oldFilePaths: [String] = statuses.compactMap { status in + status.headToIndex?.oldFile?.path + } + let newUntrackedFilePaths: [String] = statuses.compactMap { status in + status.indexToWorkDir?.newFile?.path + } + + #expect(statuses.count == expectedCount) + #expect(newFilePaths == expectedNewFilePaths) + #expect(oldFilePaths == expectedOldFilePaths) + #expect(newUntrackedFilePaths == expectedUntrackedFiles) + } + } + + @Suite("Repository.diff") class RepositoryDiff: FixturesSpec { + @Test("Should have accurate delta information") func deltas() throws { + let expectedCount = 13 + let expectedNewFilePaths = [ + ".gitmodules", + "Cartfile", + "Cartfile.lock", + "Cartfile.private", + "Cartfile.resolved", + "Carthage.checkout/Nimble", + "Carthage.checkout/Quick", + "Carthage.checkout/xcconfigs", + "Carthage/Checkouts/Nimble", + "Carthage/Checkouts/Quick", + "Carthage/Checkouts/xcconfigs", + "Mantle.xcodeproj/project.pbxproj", + "Mantle.xcworkspace/contents.xcworkspacedata", + ] + let expectedOldFilePaths = [ + ".gitmodules", + "Cartfile", + "Cartfile.lock", + "Cartfile.private", + "Cartfile.resolved", + "Carthage.checkout/Nimble", + "Carthage.checkout/Quick", + "Carthage.checkout/xcconfigs", + "Carthage/Checkouts/Nimble", + "Carthage/Checkouts/Quick", + "Carthage/Checkouts/xcconfigs", + "Mantle.xcodeproj/project.pbxproj", + "Mantle.xcworkspace/contents.xcworkspacedata", + ] + + let repo = try fixtures.mantleRepository() + let branch = repo.localBranch(named: "master").value! + #expect(repo.checkout(branch, strategy: CheckoutStrategy.None).error == nil) + + let head = repo.HEAD().value! + let commit = repo.object(head.oid).value! as! Commit + let diff = repo.diff(for: commit).value! + + let newFilePaths = diff.deltas.map { $0.newFile!.path } + let oldFilePaths = diff.deltas.map { $0.oldFile!.path } + + #expect(diff.deltas.count == expectedCount) + #expect(newFilePaths == expectedNewFilePaths) + #expect(oldFilePaths == expectedOldFilePaths) + } + + @Test("Should handle initial commit well") func initialCommit() throws { + let expectedCount = 2 + let expectedNewFilePaths = [ + ".gitignore", + "README.md", + ] + let expectedOldFilePaths = [ + ".gitignore", + "README.md", + ] + + let repo = try fixtures.mantleRepository() + #expect(repo.checkout(try #require(OID(string: "047b931bd7f5478340cef5885a6fff713005f4d6")), + strategy: CheckoutStrategy.None).error == nil) + let head = repo.HEAD().value! + let initalCommit = repo.object(head.oid).value! as! Commit + let diff = repo.diff(for: initalCommit).value! + + var newFilePaths: [String] = [] + for delta in diff.deltas { + newFilePaths.append((delta.newFile?.path)!) + } + var oldFilePaths: [String] = [] + for delta in diff.deltas { + oldFilePaths.append((delta.oldFile?.path)!) + } + + #expect(diff.deltas.count == expectedCount) + #expect(newFilePaths == expectedNewFilePaths) + #expect(oldFilePaths == expectedOldFilePaths) + } + + @Test("Should handle merge commits well") func mergeCommit() throws { + let expectedCount = 20 + let expectedNewFilePaths = [ + "Mantle.xcodeproj/project.pbxproj", + "Mantle/MTLModel+NSCoding.m", + "Mantle/Mantle.h", + "Mantle/NSArray+MTLHigherOrderAdditions.h", + "Mantle/NSArray+MTLHigherOrderAdditions.m", + "Mantle/NSArray+MTLManipulationAdditions.m", + "Mantle/NSDictionary+MTLHigherOrderAdditions.h", + "Mantle/NSDictionary+MTLHigherOrderAdditions.m", + "Mantle/NSDictionary+MTLManipulationAdditions.m", + "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.h", + "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.m", + "Mantle/NSOrderedSet+MTLHigherOrderAdditions.h", + "Mantle/NSOrderedSet+MTLHigherOrderAdditions.m", + "Mantle/NSSet+MTLHigherOrderAdditions.h", + "Mantle/NSSet+MTLHigherOrderAdditions.m", + "Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m", + "MantleTests/MTLHigherOrderAdditionsSpec.m", + "MantleTests/MTLNotificationCenterAdditionsSpec.m", + "MantleTests/MTLPredefinedTransformerAdditionsSpec.m", + "README.md", + ] + let expectedOldFilePaths = [ + "Mantle.xcodeproj/project.pbxproj", + "Mantle/MTLModel+NSCoding.m", + "Mantle/Mantle.h", + "Mantle/NSArray+MTLHigherOrderAdditions.h", + "Mantle/NSArray+MTLHigherOrderAdditions.m", + "Mantle/NSArray+MTLManipulationAdditions.m", + "Mantle/NSDictionary+MTLHigherOrderAdditions.h", + "Mantle/NSDictionary+MTLHigherOrderAdditions.m", + "Mantle/NSDictionary+MTLManipulationAdditions.m", + "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.h", + "Mantle/NSNotificationCenter+MTLWeakReferenceAdditions.m", + "Mantle/NSOrderedSet+MTLHigherOrderAdditions.h", + "Mantle/NSOrderedSet+MTLHigherOrderAdditions.m", + "Mantle/NSSet+MTLHigherOrderAdditions.h", + "Mantle/NSSet+MTLHigherOrderAdditions.m", + "Mantle/NSValueTransformer+MTLPredefinedTransformerAdditions.m", + "MantleTests/MTLHigherOrderAdditionsSpec.m", + "MantleTests/MTLNotificationCenterAdditionsSpec.m", + "MantleTests/MTLPredefinedTransformerAdditionsSpec.m", + "README.md", + ] + + let repo = try fixtures.mantleRepository() + #expect(repo.checkout(try #require(OID(string: "d0d9c13da5eb5f9e8cf2a9f1f6ca3bdbe975b57d")), + strategy: CheckoutStrategy.None).error == nil) + let head = try #require(repo.HEAD().value) + let initalCommit = try #require(repo.object(head.oid).value as? Commit) + let diff = repo.diff(for: initalCommit).value! + + let newFilePaths: [String] = diff.deltas.compactMap { delta in + delta.newFile?.path + } + let oldFilePaths: [String] = diff.deltas.compactMap { delta in + delta.oldFile?.path + } + + #expect(diff.deltas.count == expectedCount) + #expect(newFilePaths == expectedNewFilePaths) + #expect(oldFilePaths == expectedOldFilePaths) + } + } +} + +private func temporaryURL(forPurpose purpose: String) -> URL { + let globallyUniqueString = ProcessInfo.processInfo.globallyUniqueString + let path = "\(NSTemporaryDirectory())\(globallyUniqueString)_\(purpose)" + return URL(fileURLWithPath: path) } diff --git a/Tests/SwiftGit2Tests/ResultShims.swift b/Tests/SwiftGit2Tests/ResultShims.swift index 4095a7a4..cf57d0b3 100644 --- a/Tests/SwiftGit2Tests/ResultShims.swift +++ b/Tests/SwiftGit2Tests/ResultShims.swift @@ -1,3 +1,5 @@ +import Foundation + // Once Nimble adds matchers for the Result type, remove these shims and refactor the tests that use them. extension Result { var value: Success? { From 03e2bbd9c5f346a2b4426caed0ef75f9ae7fbbb4 Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Fri, 21 Feb 2025 20:07:40 +0100 Subject: [PATCH 2/8] Silence clang integer conversion warning --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index ee18b35d..bcdd1a03 100644 --- a/Package.swift +++ b/Package.swift @@ -74,7 +74,7 @@ let package = Package( // Disable -fmodules flag. Clang finds (`struct entry`) in a different file (`search.h`). "-fno-modules", // Disable warning: "implicit conversion loses integer precision" - "-Wno-single-bit-bitfield-constant-conversion", + "-Wno-single-bit-bitfield-constant-conversion", "-Wno-conversion", // Disable warning: "a function definition without a prototype is deprecated" "-Wno-deprecated-non-prototype", ]), From 8ced2453966caaf36162860dea06d2803cf86d3e Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Fri, 21 Feb 2025 20:13:10 +0100 Subject: [PATCH 3/8] Lazy initialization of fixtures to speed up tests --- Tests/SwiftGit2Tests/Fixtures.swift | 22 +++++++++++++--------- Tests/SwiftGit2Tests/RepositorySpec.swift | 12 ------------ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/Tests/SwiftGit2Tests/Fixtures.swift b/Tests/SwiftGit2Tests/Fixtures.swift index ddb92225..f467b4e6 100644 --- a/Tests/SwiftGit2Tests/Fixtures.swift +++ b/Tests/SwiftGit2Tests/Fixtures.swift @@ -15,9 +15,10 @@ final class Fixtures { // MARK: - Setup and Teardown - init() throws{ + init() throws { directoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) .appendingPathComponent("org.libgit2.SwiftGit2") + // Each instance of `Fixtures` gets a unique namespace .appendingPathComponent(UUID().uuidString) try setUp() } @@ -31,13 +32,9 @@ final class Fixtures { } private func setUp() throws { - try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) - - let zipURLs = Bundle.module.urls(forResourcesWithExtension: "zip", subdirectory: "Fixtures")! - - for URL in zipURLs { - SSZipArchive.unzipFile(atPath: URL.path, toDestination: directoryURL.path) - } + // Create target directory for this set of fixtures + try FileManager.default.createDirectory(at: directoryURL, + withIntermediateDirectories: true, attributes: nil) } private func tearDown() throws { @@ -47,7 +44,14 @@ final class Fixtures { // MARK: - Helpers func repository(named name: String) throws -> Repository { - let url = directoryURL.appendingPathComponent(name, isDirectory: true) + let url = directoryURL.appendingPathComponent(name, isDirectory: true) + + // Lazily initialize the fixture repositories as they are requested + if !FileManager.default.fileExists(atPath: url.path) { + let zipPath = Bundle.module.path(forResource: name, ofType: "zip", inDirectory: "Fixtures")! + SSZipArchive.unzipFile(atPath: zipPath, toDestination: directoryURL.path) + } + return try Repository.at(url).get() } diff --git a/Tests/SwiftGit2Tests/RepositorySpec.swift b/Tests/SwiftGit2Tests/RepositorySpec.swift index 0ba9be58..2f58a5a8 100644 --- a/Tests/SwiftGit2Tests/RepositorySpec.swift +++ b/Tests/SwiftGit2Tests/RepositorySpec.swift @@ -721,18 +721,6 @@ import SwiftGit2 #expect(newStatus.count == 1) #expect(newStatus.first?.status == .indexNew) } - - deinit { - let repo = try? fixtures.simpleRepository() - - if let untrackedURL = repo?.directoryURL?.appendingPathComponent("untracked") { - try? FileManager.default.removeItem(at: untrackedURL) - } - - if let readmeURL = repo?.directoryURL?.appendingPathComponent("README.md") { - try? FileManager.default.removeItem(at: readmeURL) - } - } } @Suite("Repository.commit") class RepositoryCommit: FixturesSpec { From b650db2f5f84a44d095945444939468f51bc090f Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Wed, 5 Mar 2025 09:29:51 +0100 Subject: [PATCH 4/8] Enable Tag-related tests --- Tests/SwiftGit2Tests/RepositorySpec.swift | 114 +++++++++++----------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/Tests/SwiftGit2Tests/RepositorySpec.swift b/Tests/SwiftGit2Tests/RepositorySpec.swift index 2f58a5a8..46db5f32 100644 --- a/Tests/SwiftGit2Tests/RepositorySpec.swift +++ b/Tests/SwiftGit2Tests/RepositorySpec.swift @@ -129,36 +129,39 @@ import SwiftGit2 #expect(remote.URL == remoteRepoURL.absoluteString) } -// let env = ProcessInfo.processInfo.environment -// -// if let privateRepo = env["SG2TestPrivateRepo"], -// let gitUsername = env["SG2TestUsername"], -// let publicKey = env["SG2TestPublicKey"], -// let privateKey = env["SG2TestPrivateKey"], -// let passphrase = env["SG2TestPassphrase"] { -// -// @Test("should be able to clone a remote repository requiring credentials") { -// let remoteRepoURL = URL(string: privateRepo) -// let localURL = temporaryURL(forPurpose: "private-remote-clone") -// let credentials = Credentials.sshMemory(username: gitUsername, -// publicKey: publicKey, -// privateKey: privateKey, -// passphrase: passphrase) -// -// let cloneResult = Repository.clone(from: remoteRepoURL!, to: localURL, credentials: credentials) -// -// #expect(cloneResult.error == nil) -// -// if case .success(let clonedRepo) = cloneResult { -// let remoteResult = clonedRepo.remote(named: "origin") -// #expect(remoteResult.error == nil) -// -// if case .success(let remote) = remoteResult { -// #expect(remote.URL == remoteRepoURL?.absoluteString) -// } -// } -// } -// } + @Test( + "should be able to clone a remote repository requiring credentials", + .enabled(if: ProcessInfo.processInfo.environment["SG2TestPrivateRepo"] != nil) + ) + func cloneRemoteRepositoryWithCredentials() throws { + let env = ProcessInfo.processInfo.environment + guard let privateRepo = env["SG2TestPrivateRepo"], + let gitUsername = env["SG2TestUsername"], + let publicKey = env["SG2TestPublicKey"], + let privateKey = env["SG2TestPrivateKey"], + let passphrase = env["SG2TestPassphrase"] + else { return } + + let remoteRepoURL = URL(string: privateRepo) + let localURL = temporaryURL(forPurpose: "private-remote-clone") + let credentials = Credentials.sshMemory(username: gitUsername, + publicKey: publicKey, + privateKey: privateKey, + passphrase: passphrase) + + let cloneResult = Repository.clone(from: remoteRepoURL!, to: localURL, credentials: credentials) + + #expect(cloneResult.error == nil) + + if case .success(let clonedRepo) = cloneResult { + let remoteResult = clonedRepo.remote(named: "origin") + #expect(remoteResult.error == nil) + + if case .success(let remote) = remoteResult { + #expect(remote.URL == remoteRepoURL?.absoluteString) + } + } + } } @Suite("Repository.blob(_:)") class BlobSpec: FixturesSpec { @@ -286,14 +289,13 @@ import SwiftGit2 #expect(result.map { $0 as! Commit }.value == commit) } -// TODO -// @Test("should work with a tag") func tag() throws { -// let repo = try fixtures.simpleRepository() -// let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) -// let tag = repo.tag(oid).value -// let result = repo.object(oid) -// #expect(result.map { $0 as! Tag }.value == tag) -// } + @Test("should work with a tag") func tag() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + let tag = repo.tag(oid).value + let result = repo.object(oid) + #expect(result.map { $0 as! SwiftGit2.Tag }.value == tag) + } @Test("should work with a tree") func tree() throws { let repo = try fixtures.simpleRepository() @@ -339,15 +341,14 @@ import SwiftGit2 #expect(repo.object(from: pointer).value == blob) } -// TODO -// @Test("should work with tags") func tag() throws { -// let repo = try fixtures.simpleRepository() -// let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) -// -// let pointer = PointerTo(oid) -// let tag = try #require(repo.tag(oid).value) -// #expect(repo.object(from: pointer).value == tag) -// } + @Test("should work with tags") func tag() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + + let pointer = PointerTo(oid) + let tag = try #require(repo.tag(oid).value) + #expect(repo.object(from: pointer).value == tag) + } } @Suite("Repository.object(from: Pointer)") class FromPointer: FixturesSpec { @@ -381,16 +382,15 @@ import SwiftGit2 #expect(result.value == blob) } -// TODO -// @Test("should work with tags") func tag() throws { -// let repo = try fixtures.simpleRepository() -// let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) -// -// let pointer = Pointer.tag(oid) -// let tag = try #require(repo.tag(oid).value) -// let result = repo.object(from: pointer).map { $0 as! Tag } -// #expect(result.value == tag) -// } + @Test("should work with tags") func tag() throws { + let repo = try fixtures.simpleRepository() + let oid = try #require(OID(string: "57943b8ee00348180ceeedc960451562750f6d33")) + + let pointer = Pointer.tag(oid) + let tag = try #require(repo.tag(oid).value) + let result = repo.object(from: pointer).map { $0 as! SwiftGit2.Tag } + #expect(result.value == tag) + } } @Suite("Repository.allRemotes()") class AllRemotes: FixturesSpec { From 1005716f9e68d753fda1441a76cc68455ca939e9 Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Wed, 5 Mar 2025 11:06:26 +0100 Subject: [PATCH 5/8] Parameterize test: clone remote repository --- Tests/SwiftGit2Tests/RepositorySpec.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Tests/SwiftGit2Tests/RepositorySpec.swift b/Tests/SwiftGit2Tests/RepositorySpec.swift index 46db5f32..d7abae9f 100644 --- a/Tests/SwiftGit2Tests/RepositorySpec.swift +++ b/Tests/SwiftGit2Tests/RepositorySpec.swift @@ -114,8 +114,12 @@ import SwiftGit2 #expect(remote.URL == remoteRepo.directoryURL?.absoluteString) } - @Test("should be able to clone a remote repository") func cloneRemoteRepository() throws { - let remoteRepoURL = try #require(URL(string: "https://github.com/libgit2/TestGitRepository.git")) + @Test("should be able to clone a remote repository", arguments: [ + URL(string: "https://github.com/libgit2/TestGitRepository.git"), + URL(string: "git@github.com:libgit2/TestGitRepository.git"), + ]) + func cloneRemoteRepository(url: URL?) throws { + let remoteRepoURL = try #require(url) let localURL = temporaryURL(forPurpose: "public-remote-clone") let cloneResult = Repository.clone(from: remoteRepoURL, to: localURL) From f8cf6e1d4ec36e64c983d9b086f8fb95bce341de Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Sun, 12 Oct 2025 11:05:39 +0200 Subject: [PATCH 6/8] Update libgit2 to v1.9.1 --- libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgit2 b/libgit2 index 2fa13adf..0060d9cf 160000 --- a/libgit2 +++ b/libgit2 @@ -1 +1 @@ -Subproject commit 2fa13adf09c8dd1f334f57023390043ea3f71cb2 +Subproject commit 0060d9cf5666f015b1067129bd874c6cc4c9c7ac From 31b18612f3fce416cb915568c3116ff0f1373904 Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Sun, 12 Oct 2025 11:18:45 +0200 Subject: [PATCH 7/8] Disable SSH clone test --- Tests/SwiftGit2Tests/RepositorySpec.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/SwiftGit2Tests/RepositorySpec.swift b/Tests/SwiftGit2Tests/RepositorySpec.swift index d7abae9f..7299b7d1 100644 --- a/Tests/SwiftGit2Tests/RepositorySpec.swift +++ b/Tests/SwiftGit2Tests/RepositorySpec.swift @@ -116,7 +116,8 @@ import SwiftGit2 @Test("should be able to clone a remote repository", arguments: [ URL(string: "https://github.com/libgit2/TestGitRepository.git"), - URL(string: "git@github.com:libgit2/TestGitRepository.git"), + // Disabled: not implemented yet on iOS. + // URL(string: "git@github.com:libgit2/TestGitRepository.git"), ]) func cloneRemoteRepository(url: URL?) throws { let remoteRepoURL = try #require(url) From d7c6cec5d5b6a199a53351c80ce6e0152017940b Mon Sep 17 00:00:00 2001 From: Mathijs Bernson Date: Sun, 12 Oct 2025 12:02:31 +0200 Subject: [PATCH 8/8] CI workflow: Remove Xcode 15, add iOS/macOS 26 --- .github/workflows/BuildPR.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/BuildPR.yml b/.github/workflows/BuildPR.yml index 3892fdef..d922cae7 100644 --- a/.github/workflows/BuildPR.yml +++ b/.github/workflows/BuildPR.yml @@ -14,11 +14,11 @@ jobs: matrix: xcode: # The '^' stands for a semantic version range - - ^15 - ^16 os: - macos-14 - macos-15 + - macos-26 steps: - name: Checkout uses: actions/checkout@v4 @@ -39,12 +39,13 @@ jobs: xcode: - ^16 os: - - macos-15 + - macos-26 platform: - iOS platform-version: # The '^' stands for a semantic version range - ^18 + - ^26 steps: - name: Checkout uses: actions/checkout@v4