From ae8a4efbea86e4e8c2d93e3af913a12f3fd9630f Mon Sep 17 00:00:00 2001 From: Steffan Andrews Date: Fri, 25 Oct 2024 17:40:46 -0700 Subject: [PATCH] Migrated from XCTest unit tests to Swift Testing --- .../AnnotationsTests.swift | 95 ++++++------- .../AudioOnlyTests.swift | 49 +++---- .../BasicMarkersEmptyIDTests.swift | 78 ++++++----- .../BasicMarkersOutOfClipBoundsTests.swift | 27 ++-- .../BasicMarkersSubframesTests.swift | 17 +-- .../BasicMarkersTests.swift | 125 +++++++++--------- .../CompoundClipTests.swift | 17 +-- .../MarkerRolesTests.swift | 87 ++++++------ .../MarkersExtractorTests.swift | 43 +++--- .../MarkersExtractorTests/ProgressTests.swift | 35 ++--- .../Test Utilities.swift | 41 +++--- .../TestResource/TestResource.swift | 67 +--------- 12 files changed, 318 insertions(+), 363 deletions(-) diff --git a/Tests/MarkersExtractorTests/AnnotationsTests.swift b/Tests/MarkersExtractorTests/AnnotationsTests.swift index edd14ef..cdbc6e9 100644 --- a/Tests/MarkersExtractorTests/AnnotationsTests.swift +++ b/Tests/MarkersExtractorTests/AnnotationsTests.swift @@ -4,15 +4,16 @@ // Licensed under MIT License // -@testable import MarkersExtractor import OTCore import TimecodeKitCore -import XCTest +import Testing +import TestingExtensions +@testable import MarkersExtractor -final class AnnotationsTests: XCTestCase { +@Suite struct AnnotationsTests { // TODO: add test for filtering disabled captions once that's implemented /// Test importing captions - func testAnnotations_CaptionsOnly() async throws { + @Test func annotations_CaptionsOnly() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -27,31 +28,31 @@ final class AnnotationsTests: XCTestCase { let markers = try await extractor.extractMarkers().markers - XCTAssertEqual(markers.count, 2) + #expect(markers.count == 2) let fr: TimecodeFrameRate = .fps25 - let marker0 = try XCTUnwrap(markers[safe: 0]) - XCTAssertEqual(marker0.type, .caption) - XCTAssertEqual(marker0.name, "caption1") - XCTAssertEqual(marker0.notes, "") - XCTAssertEqual(marker0.roles.audio?.map(\.rawValue), ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) - XCTAssertEqual(marker0.roles.video?.rawValue, nil) - XCTAssertEqual(marker0.roles.caption?.rawValue, "iTT?captionFormat=ITT.en") - XCTAssertEqual(marker0.position, tc("01:00:03:00", at: fr)) + let marker0 = try #require(markers[safe: 0]) + #expect(marker0.type == .caption) + #expect(marker0.name == "caption1") + #expect(marker0.notes == "") + #expect(marker0.roles.audio?.map(\.rawValue) == ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) + #expect(marker0.roles.video?.rawValue == nil) + #expect(marker0.roles.caption?.rawValue == "iTT?captionFormat=ITT.en") + #expect(marker0.position == tc("01:00:03:00", at: fr)) - let marker1 = try XCTUnwrap(markers[safe: 1]) - XCTAssertEqual(marker1.type, .caption) - XCTAssertEqual(marker1.name, "caption2") - XCTAssertEqual(marker1.notes, "") - XCTAssertEqual(marker1.roles.audio?.map(\.rawValue), ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) - XCTAssertEqual(marker1.roles.video?.rawValue, nil) - XCTAssertEqual(marker1.roles.caption?.rawValue, "iTT?captionFormat=ITT.en") - XCTAssertEqual(marker1.position, tc("01:00:09:10", at: fr)) + let marker1 = try #require(markers[safe: 1]) + #expect(marker1.type == .caption) + #expect(marker1.name == "caption2") + #expect(marker1.notes == "") + #expect(marker1.roles.audio?.map(\.rawValue) == ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) + #expect(marker1.roles.video?.rawValue == nil) + #expect(marker1.roles.caption?.rawValue == "iTT?captionFormat=ITT.en") + #expect(marker1.position == tc("01:00:09:10", at: fr)) } /// Test importing captions - func testAnnotations_MarkersAndCaptions() async throws { + @Test func annotations_MarkersAndCaptions() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -66,36 +67,36 @@ final class AnnotationsTests: XCTestCase { let markers = try await extractor.extractMarkers().markers - XCTAssertEqual(markers.count, 3) + #expect(markers.count == 3) let fr: TimecodeFrameRate = .fps25 - let marker0 = try XCTUnwrap(markers[safe: 0]) - XCTAssertEqual(marker0.type, .caption) - XCTAssertEqual(marker0.name, "caption1") - XCTAssertEqual(marker0.notes, "") - XCTAssertEqual(marker0.roles.audio?.map(\.rawValue), ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) - XCTAssertEqual(marker0.roles.video?.rawValue, nil) - XCTAssertEqual(marker0.roles.caption?.rawValue, "iTT?captionFormat=ITT.en") - XCTAssertEqual(marker0.position, tc("01:00:03:00", at: fr)) + let marker0 = try #require(markers[safe: 0]) + #expect(marker0.type == .caption) + #expect(marker0.name == "caption1") + #expect(marker0.notes == "") + #expect(marker0.roles.audio?.map(\.rawValue) == ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) + #expect(marker0.roles.video?.rawValue == nil) + #expect(marker0.roles.caption?.rawValue == "iTT?captionFormat=ITT.en") + #expect(marker0.position == tc("01:00:03:00", at: fr)) - let marker1 = try XCTUnwrap(markers[safe: 1]) - XCTAssertEqual(marker1.type, .caption) - XCTAssertEqual(marker1.name, "caption2") - XCTAssertEqual(marker1.notes, "") - XCTAssertEqual(marker1.roles.audio?.map(\.rawValue), ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) - XCTAssertEqual(marker1.roles.video?.rawValue, nil) - XCTAssertEqual(marker1.roles.caption?.rawValue, "iTT?captionFormat=ITT.en") - XCTAssertEqual(marker1.position, tc("01:00:09:10", at: fr)) + let marker1 = try #require(markers[safe: 1]) + #expect(marker1.type == .caption) + #expect(marker1.name == "caption2") + #expect(marker1.notes == "") + #expect(marker1.roles.audio?.map(\.rawValue) == ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) + #expect(marker1.roles.video?.rawValue == nil) + #expect(marker1.roles.caption?.rawValue == "iTT?captionFormat=ITT.en") + #expect(marker1.position == tc("01:00:09:10", at: fr)) - let marker2 = try XCTUnwrap(markers[safe: 2]) - XCTAssertEqual(marker2.type, .marker(.standard)) - XCTAssertEqual(marker2.name, "marker1") - XCTAssertEqual(marker2.notes, "m1 notes") - XCTAssertEqual(marker2.roles.audio?.map(\.rawValue), ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) - XCTAssertEqual(marker2.roles.video?.rawValue, nil) - XCTAssertEqual(marker2.roles.caption?.rawValue, nil) - XCTAssertEqual(marker2.position, tc("01:00:27:10", at: fr)) + let marker2 = try #require(markers[safe: 2]) + #expect(marker2.type == .marker(.standard)) + #expect(marker2.name == "marker1") + #expect(marker2.notes == "m1 notes") + #expect(marker2.roles.audio?.map(\.rawValue) == ["Dialogue"]) // inherited from clip it's anchored on (TODO: ?) + #expect(marker2.roles.video?.rawValue == nil) + #expect(marker2.roles.caption?.rawValue == nil) + #expect(marker2.position == tc("01:00:27:10", at: fr)) } } diff --git a/Tests/MarkersExtractorTests/AudioOnlyTests.swift b/Tests/MarkersExtractorTests/AudioOnlyTests.swift index efbaef9..edf5aea 100644 --- a/Tests/MarkersExtractorTests/AudioOnlyTests.swift +++ b/Tests/MarkersExtractorTests/AudioOnlyTests.swift @@ -4,14 +4,15 @@ // Licensed under MIT License // -@testable import MarkersExtractor +import DAWFileKit import OTCore +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest -import DAWFileKit +@testable import MarkersExtractor -final class AudioOnlyTests: XCTestCase { - func testAudioOnly() async throws { +@Suite struct AudioOnlyTests { + @Test func audioOnly() async throws { let outputDir = FileManager.default .temporaryDirectory .appendingPathComponent(UUID().uuidString) @@ -27,36 +28,36 @@ final class AudioOnlyTests: XCTestCase { let markers = try await extractor.extractMarkers().markers - XCTAssertEqual(markers.count, 1) + #expect(markers.count == 1) let fr: TimecodeFrameRate = .fps24 - let marker0 = try XCTUnwrap(markers[safe: 0]) - XCTAssertEqual(marker0.name, "Marker 1") - XCTAssertEqual(marker0.position, tc("00:00:02:00", at: fr)) + let marker0 = try #require(markers[safe: 0]) + #expect(marker0.name == "Marker 1") + #expect(marker0.position == tc("00:00:02:00", at: fr)) - XCTAssertEqual(marker0.roles.audio, [FinalCutPro.FCPXML.AudioRole(role: "Dialogue")]) - XCTAssertEqual(marker0.roles.isAudioDefault, false) // TODO: Dialogue isn't a builtin/default role?? - XCTAssertEqual(marker0.roles.isAudioEmpty, false) - XCTAssertEqual(marker0.roles.isAudioDefined, true) // exists in the XML + #expect(marker0.roles.audio == [FinalCutPro.FCPXML.AudioRole(role: "Dialogue")]) + #expect(marker0.roles.isAudioDefault == false) // TODO: Dialogue isn't a builtin/default role?? + #expect(marker0.roles.isAudioEmpty == false) + #expect(marker0.roles.isAudioDefined == true) // exists in the XML - XCTAssertEqual(marker0.roles.video, nil) - XCTAssertEqual(marker0.roles.isVideoDefault, false) - XCTAssertEqual(marker0.roles.isVideoEmpty, true) - XCTAssertEqual(marker0.roles.isVideoDefined, false) // was default, not defined + #expect(marker0.roles.video == nil) + #expect(marker0.roles.isVideoDefault == false) + #expect(marker0.roles.isVideoEmpty == true) + #expect(marker0.roles.isVideoDefined == false) // was default, not defined - XCTAssertEqual(marker0.roles.caption, nil) + #expect(marker0.roles.caption == nil) } /// Ensure that a placeholder thumbnail image is used for an audio-only clip. - func testAudioOnly_WithMedia_PNG() async throws { + @Test func audioOnly_WithMedia_PNG() async throws { let tempDir = FileManager.default .temporaryDirectory .appendingPathComponent(UUID().uuidString) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: false) - let dummyMediaData = try XCTUnwrap(EmbeddedResource.empty_mov.data) + let dummyMediaData = try #require(EmbeddedResource.empty_mov.data) let dummyMediaURL = tempDir.appendingPathComponent("AudioOnly.mov") try dummyMediaData.write(to: dummyMediaURL) @@ -86,18 +87,18 @@ final class AudioOnlyTests: XCTestCase { exportFilenames.forEach { print(" - " + $0) } // ensure that a placeholder thumbnail was used for the audio-only clip - XCTAssertTrue(exportFilenames.contains("marker-placeholder.png")) + #expect(exportFilenames.contains("marker-placeholder.png")) } /// Ensure that a placeholder thumbnail image is used for an audio-only clip. - func testAudioOnly_WithMedia_GIF() async throws { + @Test func audioOnly_WithMedia_GIF() async throws { let tempDir = FileManager.default .temporaryDirectory .appendingPathComponent(UUID().uuidString) try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: false) - let dummyMediaData = try XCTUnwrap(EmbeddedResource.empty_mov.data) + let dummyMediaData = try #require(EmbeddedResource.empty_mov.data) let dummyMediaURL = tempDir.appendingPathComponent("AudioOnly.mov") try dummyMediaData.write(to: dummyMediaURL) @@ -127,7 +128,7 @@ final class AudioOnlyTests: XCTestCase { exportFilenames.forEach { print(" - " + $0) } // ensure that a placeholder thumbnail was used for the audio-only clip - XCTAssertTrue(exportFilenames.contains("marker-placeholder.gif")) + #expect(exportFilenames.contains("marker-placeholder.gif")) } } diff --git a/Tests/MarkersExtractorTests/BasicMarkersEmptyIDTests.swift b/Tests/MarkersExtractorTests/BasicMarkersEmptyIDTests.swift index b1ae01c..55fbd6a 100644 --- a/Tests/MarkersExtractorTests/BasicMarkersEmptyIDTests.swift +++ b/Tests/MarkersExtractorTests/BasicMarkersEmptyIDTests.swift @@ -4,49 +4,55 @@ // Licensed under MIT License // -@testable import MarkersExtractor +import Foundation +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class BasicMarkersEmptyIDTests: XCTestCase { - /// Ensure that empty marker ID strings cause an error and abort the conversion process. - func testBasicMarkers_extractMarkers_nonEmptyMarkerIDs() async throws { - var settings = try MarkersExtractor.Settings( +@Suite struct BasicMarkersEmptyIDTests { + var settings: MarkersExtractor.Settings + + init() throws { + settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory ) + } + + /// Ensure that empty marker ID strings cause an error and abort the conversion process. + @Test(arguments: MarkerIDMode.allCases) + mutating func testBasicMarkers_extractMarkers_nonEmptyMarkerIDs(idMode: MarkerIDMode) async throws { + settings.idNamingMode = idMode - for idMode in MarkerIDMode.allCases { - settings.idNamingMode = idMode - - let extractor = MarkersExtractor(settings: settings) + let extractor = MarkersExtractor(settings: settings) + + // attempt to extract markers. + switch idMode { + case .timelineNameAndTimecode: + // no way case an error since timecode will always be a non-empty string. + // so just test that no error is thrown here. - // attempt to extract markers. - switch idMode { - case .timelineNameAndTimecode: - // no way case an error since timecode will always be a non-empty string. - // so just test that no error is thrown here. - do { - _ = try await extractor.extractMarkers() - } catch { - XCTFail() - } - case .name: - // expect an error here - 3rd marker has an empty Name - do { - _ = try await extractor.extractMarkers() - XCTFail("Expected error to be thrown.") - } catch { - // we want an error - } - case .notes: - // expect an error here - 2nd marker has an empty Name - do { - _ = try await extractor.extractMarkers() - XCTFail("Expected error to be thrown.") - } catch { - // we want an error - } + do { + _ = try await extractor.extractMarkers() + } catch { + #fail + } + case .name: + // expect an error here - 3rd marker has an empty Name + do { + _ = try await extractor.extractMarkers() + #fail("Expected error to be thrown.") + } catch { + // we want an error + } + case .notes: + // expect an error here - 2nd marker has an empty Name + do { + _ = try await extractor.extractMarkers() + #fail("Expected error to be thrown.") + } catch { + // we want an error } } } diff --git a/Tests/MarkersExtractorTests/BasicMarkersOutOfClipBoundsTests.swift b/Tests/MarkersExtractorTests/BasicMarkersOutOfClipBoundsTests.swift index 6132c78..286d6ab 100644 --- a/Tests/MarkersExtractorTests/BasicMarkersOutOfClipBoundsTests.swift +++ b/Tests/MarkersExtractorTests/BasicMarkersOutOfClipBoundsTests.swift @@ -5,14 +5,15 @@ // import DAWFileKit -@testable import MarkersExtractor +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class BasicMarkersOutOfClipBoundsTests: XCTestCase { +@Suite struct BasicMarkersOutOfClipBoundsTests { /// Ensure that markers that are out of bounds of clips are not included in extraction. /// Also tests to make sure marker parent clip information is correct. - func testOutOfClipBounds() async throws { + @Test func outOfClipBounds() async throws { let settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -55,7 +56,7 @@ final class BasicMarkersOutOfClipBoundsTests: XCTestCase { // check markers - XCTAssertEqual(markers.count, 2) + #expect(markers.count == 2) // if the clip is the first clip on the timeline (it starts at 00:00:00:00) and // it had been resized from its left edge to result in an out-of-boundary marker prior to @@ -63,17 +64,17 @@ final class BasicMarkersOutOfClipBoundsTests: XCTestCase { // clip 1 - let marker0 = try XCTUnwrap(markers[safe: 0]) - XCTAssertEqual(marker0.name, "Marker 2") - XCTAssertEqual(marker0.position, tc("00:00:07:23", at: fr)) - XCTAssertEqual(marker0.parentInfo, clip1ParentInfo) + let marker0 = try #require(markers[safe: 0]) + #expect(marker0.name == "Marker 2") + #expect(marker0.position == tc("00:00:07:23", at: fr)) + #expect(marker0.parentInfo == clip1ParentInfo) // clip 2 - let marker1 = try XCTUnwrap(markers[safe: 1]) - XCTAssertEqual(marker1.name, "Marker 5") - XCTAssertEqual(marker1.position, tc("00:00:28:18", at: fr)) - XCTAssertEqual(marker1.parentInfo, clip2ParentInfo) + let marker1 = try #require(markers[safe: 1]) + #expect(marker1.name == "Marker 5") + #expect(marker1.position == tc("00:00:28:18", at: fr)) + #expect(marker1.parentInfo == clip2ParentInfo) } } diff --git a/Tests/MarkersExtractorTests/BasicMarkersSubframesTests.swift b/Tests/MarkersExtractorTests/BasicMarkersSubframesTests.swift index 4693947..82d4a60 100644 --- a/Tests/MarkersExtractorTests/BasicMarkersSubframesTests.swift +++ b/Tests/MarkersExtractorTests/BasicMarkersSubframesTests.swift @@ -4,13 +4,14 @@ // Licensed under MIT License // -@testable import MarkersExtractor +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class BasicMarkersSubframesTests: XCTestCase { +@Suite struct BasicMarkersSubframesTests { /// Test that fraction time values that have subframes correctly convert to Timecode. - func testBasicMarkers_extractMarkers_TimecodeSubframes() async throws { + @Test func basicMarkers_extractMarkers_TimecodeSubframes() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -23,11 +24,11 @@ final class BasicMarkersSubframesTests: XCTestCase { // 24 total markers. // 6 markers are ignored because they are within compound clips (the 2 instances of the // `ref-clip` which contains 3 markers). - XCTAssertEqual(markers.count, 18) + #expect(markers.count == 18) - let lastMarker = try XCTUnwrap(markers.last) - XCTAssertEqual( - lastMarker.positionTimeString(format: .timecode(stringFormat: [.showSubFrames])), + let lastMarker = try #require(markers.last) + #expect( + lastMarker.positionTimeString(format: .timecode(stringFormat: [.showSubFrames])) == "00:00:28:19.25" ) } diff --git a/Tests/MarkersExtractorTests/BasicMarkersTests.swift b/Tests/MarkersExtractorTests/BasicMarkersTests.swift index 3401245..fb65ed6 100644 --- a/Tests/MarkersExtractorTests/BasicMarkersTests.swift +++ b/Tests/MarkersExtractorTests/BasicMarkersTests.swift @@ -5,16 +5,17 @@ // import DAWFileKit -@testable import MarkersExtractor +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class BasicMarkersTests: XCTestCase { +@Suite struct BasicMarkersTests { /// Basic test to check `MarkersExtractor.extractMarkers()` parses data correctly. /// /// Note that two markers share the same marker ID. This test also checks the default behavior /// of non-unique IDs. - func testBasicMarkers_extractMarkers() async throws { + @Test func basicMarkers_extractMarkers() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -27,7 +28,7 @@ final class BasicMarkersTests: XCTestCase { let markers = try await extractor.extractMarkers().markers - XCTAssertEqual(markers.count, 4) + #expect(markers.count == 4) let fr: TimecodeFrameRate = .fps29_97 @@ -44,53 +45,53 @@ final class BasicMarkersTests: XCTestCase { timelineStartTime: tc("00:00:00:00", at: fr) ) - let marker0 = try XCTUnwrap(markers[safe: 0]) - XCTAssertEqual(marker0.type, .marker(.standard)) - XCTAssertEqual(marker0.name, "Marker 1") - XCTAssertEqual(marker0.notes, "some notes here") - XCTAssertEqual( - marker0.roles, + let marker0 = try #require(markers[safe: 0]) + #expect(marker0.type == .marker(.standard)) + #expect(marker0.name == "Marker 1") + #expect(marker0.notes == "some notes here") + #expect( + marker0.roles == .init(video: "Titles", isVideoDefault: true, audio: nil, isAudioDefault: false) ) - XCTAssertEqual(marker0.position, tc("00:00:29:14", at: fr)) - XCTAssertEqual(marker0.parentInfo, parentInfo) + #expect(marker0.position == tc("00:00:29:14", at: fr)) + #expect(marker0.parentInfo == parentInfo) - let marker1 = try XCTUnwrap(markers[safe: 1]) - XCTAssertEqual(marker1.type, .marker(.toDo(completed: false))) - XCTAssertEqual(marker1.name, "Marker 1") - XCTAssertEqual(marker1.notes, "more notes here") - XCTAssertEqual( - marker1.roles, + let marker1 = try #require(markers[safe: 1]) + #expect(marker1.type == .marker(.toDo(completed: false))) + #expect(marker1.name == "Marker 1") + #expect(marker1.notes == "more notes here") + #expect( + marker1.roles == .init(video: "Titles", isVideoDefault: true, audio: nil, isAudioDefault: false) ) - XCTAssertEqual(marker1.position, tc("00:00:29:15", at: fr)) - XCTAssertEqual(marker1.parentInfo, parentInfo) + #expect(marker1.position == tc("00:00:29:15", at: fr)) + #expect(marker1.parentInfo == parentInfo) - let marker2 = try XCTUnwrap(markers[safe: 2]) - XCTAssertEqual(marker2.type, .marker(.toDo(completed: true))) - XCTAssertEqual(marker2.name, "Marker 2") - XCTAssertEqual(marker2.notes, "notes yay") - XCTAssertEqual( - marker2.roles, + let marker2 = try #require(markers[safe: 2]) + #expect(marker2.type == .marker(.toDo(completed: true))) + #expect(marker2.name == "Marker 2") + #expect(marker2.notes == "notes yay") + #expect( + marker2.roles == .init(video: "Titles", isVideoDefault: true, audio: nil, isAudioDefault: false) ) - XCTAssertEqual(marker2.position, tc("00:00:29:15", at: fr)) - XCTAssertEqual(marker2.parentInfo, parentInfo) + #expect(marker2.position == tc("00:00:29:15", at: fr)) + #expect(marker2.parentInfo == parentInfo) - let marker3 = try XCTUnwrap(markers[safe: 3]) - XCTAssertEqual(marker3.type, .marker(.chapter(posterOffset: Fraction(11, 30)))) - XCTAssertEqual(marker3.name, "Marker 3") - XCTAssertEqual(marker3.notes, "more notes here") - XCTAssertEqual( - marker3.roles, + let marker3 = try #require(markers[safe: 3]) + #expect(marker3.type == .marker(.chapter(posterOffset: Fraction(11, 30)))) + #expect(marker3.name == "Marker 3") + #expect(marker3.notes == "more notes here") + #expect( + marker3.roles == .init(video: "Titles", isVideoDefault: true, audio: nil, isAudioDefault: false) ) - XCTAssertEqual(marker3.position, tc("00:00:29:17", at: fr)) - XCTAssertEqual(marker3.parentInfo, parentInfo) + #expect(marker3.position == tc("00:00:29:17", at: fr)) + #expect(marker3.parentInfo == parentInfo) } /// Ensure that duplicate marker ID uniquing works correctly for all marker ID naming modes. - func testBasicMarkers_extractMarkers_uniquing() async throws { + @Test func basicMarkers_extractMarkers_uniquing() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -108,90 +109,90 @@ final class BasicMarkersTests: XCTestCase { // verify correct IDs switch idMode { case .timelineNameAndTimecode: - XCTAssertEqual( + #expect( markers[safe: 0]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Test Project_00:00:29:14" ) - XCTAssertEqual( + #expect( markers[safe: 1]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Test Project_00:00:29:15-1" ) - XCTAssertEqual( + #expect( markers[safe: 2]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Test Project_00:00:29:15-2" ) - XCTAssertEqual( + #expect( markers[safe: 3]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Test Project_00:00:29:17" ) case .name: - XCTAssertEqual( + #expect( markers[safe: 0]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Marker 1-1" ) - XCTAssertEqual( + #expect( markers[safe: 1]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Marker 1-2" ) - XCTAssertEqual( + #expect( markers[safe: 2]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Marker 2" ) - XCTAssertEqual( + #expect( markers[safe: 3]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "Marker 3" ) case .notes: - XCTAssertEqual( + #expect( markers[safe: 0]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "some notes here" ) - XCTAssertEqual( + #expect( markers[safe: 1]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "more notes here-1" ) - XCTAssertEqual( + #expect( markers[safe: 2]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "notes yay" ) - XCTAssertEqual( + #expect( markers[safe: 3]?.id( settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat - ), + ) == "more notes here-2" ) } diff --git a/Tests/MarkersExtractorTests/CompoundClipTests.swift b/Tests/MarkersExtractorTests/CompoundClipTests.swift index 41edece..c5f0ad2 100644 --- a/Tests/MarkersExtractorTests/CompoundClipTests.swift +++ b/Tests/MarkersExtractorTests/CompoundClipTests.swift @@ -4,15 +4,16 @@ // Licensed under MIT License // -@testable import MarkersExtractor import OTCore +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class CompoundClipTests: XCTestCase { +@Suite struct CompoundClipTests { /// Ensure that markers directly attached to compound clips (`ref-clip`s) on the main timeline /// are preserved, while all markers within compound clips are discarded. - func testCompoundClips() async throws { + @Test func compoundClips() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: fcpxmlTestData), outputDir: FileManager.default.temporaryDirectory @@ -25,14 +26,14 @@ final class CompoundClipTests: XCTestCase { let markers = try await extractor.extractMarkers().markers - XCTAssertEqual(markers.count, 1) + #expect(markers.count == 1) let fr: TimecodeFrameRate = .fps25 // just test basic marker info to identify the marker - let marker0 = try XCTUnwrap(markers[safe: 0]) - XCTAssertEqual(marker0.name, "Marker On Compound Clip in Main Timeline") - XCTAssertEqual(marker0.position, tc("01:00:04:00", at: fr)) + let marker0 = try #require(markers[safe: 0]) + #expect(marker0.name == "Marker On Compound Clip in Main Timeline") + #expect(marker0.position == tc("01:00:04:00", at: fr)) } } diff --git a/Tests/MarkersExtractorTests/MarkerRolesTests.swift b/Tests/MarkersExtractorTests/MarkerRolesTests.swift index 84290b8..8b39c7c 100644 --- a/Tests/MarkersExtractorTests/MarkerRolesTests.swift +++ b/Tests/MarkersExtractorTests/MarkerRolesTests.swift @@ -4,11 +4,12 @@ // Licensed under MIT License // +import Testing +import TestingExtensions @testable import MarkersExtractor -import XCTest -final class MarkerRolesTests: XCTestCase { - func testVerbatim() { +@Suite struct MarkerRolesTests { + @Test func verbatim() async { let markerRoles = MarkerRoles( video: "My Video Role.My Video Role-1", isVideoDefault: false, @@ -19,13 +20,13 @@ final class MarkerRolesTests: XCTestCase { collapseSubroles: false ) - XCTAssertEqual(markerRoles.videoFormatted(), "My Video Role.My Video Role-1") - XCTAssertEqual(markerRoles.audioFormatted(multipleRoleSeparator: ",").flat, "My Audio Role.My Audio Role-1") - XCTAssertEqual(markerRoles.audioFormatted(multipleRoleSeparator: ",").array, ["My Audio Role.My Audio Role-1"]) - XCTAssertEqual(markerRoles.captionFormatted(), "My Caption Role") + #expect(markerRoles.videoFormatted() == "My Video Role.My Video Role-1") + #expect(markerRoles.audioFormatted(multipleRoleSeparator: ",").flat == "My Audio Role.My Audio Role-1") + #expect(markerRoles.audioFormatted(multipleRoleSeparator: ",").array == ["My Audio Role.My Audio Role-1"]) + #expect(markerRoles.captionFormatted() == "My Caption Role") } - func testCollapsedSubRole() { + @Test func collapsedSubRole() async { let markerRoles = MarkerRoles( video: "My Video Role.My Video Role-1", isVideoDefault: false, @@ -36,58 +37,58 @@ final class MarkerRolesTests: XCTestCase { collapseSubroles: true ) - XCTAssertEqual(markerRoles.videoFormatted(), "My Video Role") - XCTAssertEqual(markerRoles.audioFormatted(multipleRoleSeparator: ",").flat, "My Audio Role") - XCTAssertEqual(markerRoles.audioFormatted(multipleRoleSeparator: ",").array, ["My Audio Role"]) - XCTAssertEqual(markerRoles.captionFormatted(), "My Caption Role") + #expect(markerRoles.videoFormatted() == "My Video Role") + #expect(markerRoles.audioFormatted(multipleRoleSeparator: ",").flat == "My Audio Role") + #expect(markerRoles.audioFormatted(multipleRoleSeparator: ",").array == ["My Audio Role"]) + #expect(markerRoles.captionFormatted() == "My Caption Role") } - func testIsDefault() { - XCTAssertEqual(MarkerRoles(video: "Video", isVideoDefault: true).isVideoDefault, true) - XCTAssertEqual(MarkerRoles(video: "Video", isVideoDefault: false).isVideoDefault, false) + @Test func isDefault() async { + #expect(MarkerRoles(video: "Video", isVideoDefault: true).isVideoDefault) + #expect(!MarkerRoles(video: "Video", isVideoDefault: false).isVideoDefault) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"], isAudioDefault: true).isAudioDefault, true) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"], isAudioDefault: false).isAudioDefault, false) + #expect(MarkerRoles(audio: ["Dialogue"], isAudioDefault: true).isAudioDefault) + #expect(!MarkerRoles(audio: ["Dialogue"], isAudioDefault: false).isAudioDefault) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"], isAudioDefault: true).isAudioDefault, true) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"], isAudioDefault: false).isAudioDefault, false) + #expect(MarkerRoles(audio: ["Dialogue"], isAudioDefault: true).isAudioDefault) + #expect(!MarkerRoles(audio: ["Dialogue"], isAudioDefault: false).isAudioDefault) - XCTAssertEqual(MarkerRoles(video: "Video", isVideoDefault: true).isAudioDefault, false) - XCTAssertEqual(MarkerRoles(video: "Video", isVideoDefault: false).isVideoDefault, false) - XCTAssertEqual(MarkerRoles(video: "Video", isVideoDefault: false).isCaptionDefault, false) + #expect(!MarkerRoles(video: "Video", isVideoDefault: true).isAudioDefault) + #expect(!MarkerRoles(video: "Video", isVideoDefault: false).isVideoDefault) + #expect(!MarkerRoles(video: "Video", isVideoDefault: false).isCaptionDefault) } - func testIsEmpty() { - XCTAssertEqual(MarkerRoles(video: nil).isVideoEmpty, true) - XCTAssertEqual(MarkerRoles(video: "").isVideoEmpty, true) - XCTAssertEqual(MarkerRoles(video: "Video").isVideoEmpty, false) + @Test func isEmpty() async { + #expect(MarkerRoles(video: nil).isVideoEmpty) + #expect(MarkerRoles(video: "").isVideoEmpty) + #expect(!MarkerRoles(video: "Video").isVideoEmpty) - XCTAssertEqual(MarkerRoles(audio: nil).isAudioEmpty, true) - XCTAssertEqual(MarkerRoles(audio: [""]).isAudioEmpty, true) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"]).isAudioEmpty, false) + #expect(MarkerRoles(audio: nil).isAudioEmpty) + #expect(MarkerRoles(audio: [""]).isAudioEmpty) + #expect(!MarkerRoles(audio: ["Dialogue"]).isAudioEmpty) } - func testIsDefined() { - XCTAssertEqual(MarkerRoles(video: nil).isVideoDefined, false) - XCTAssertEqual(MarkerRoles(video: "").isVideoDefined, false) - XCTAssertEqual(MarkerRoles(video: "Video").isVideoDefined, true) - XCTAssertEqual(MarkerRoles(video: "Video", isVideoDefault: true).isVideoDefined, false) + @Test func isDefined() async { + #expect(!MarkerRoles(video: nil).isVideoDefined) + #expect(!MarkerRoles(video: "").isVideoDefined) + #expect(MarkerRoles(video: "Video").isVideoDefined) + #expect(!MarkerRoles(video: "Video", isVideoDefault: true).isVideoDefined) - XCTAssertEqual(MarkerRoles(audio: nil).isAudioDefined, false) - XCTAssertEqual(MarkerRoles(audio: [""]).isAudioDefined, false) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"]).isAudioDefined, true) - XCTAssertEqual(MarkerRoles(audio: ["Dialogue"], isAudioDefault: true).isAudioDefined, false) + #expect(!MarkerRoles(audio: nil).isAudioDefined) + #expect(!MarkerRoles(audio: [""]).isAudioDefined) + #expect(MarkerRoles(audio: ["Dialogue"]).isAudioDefined) + #expect(!MarkerRoles(audio: ["Dialogue"], isAudioDefault: true).isAudioDefined) } - func testMultipleAudio() { - XCTAssertEqual( + @Test func multipleAudio() async { + #expect( MarkerRoles(audio: ["Dialogue.MixL", "Dialogue.MixR"]) - .audioFormatted(multipleRoleSeparator: ",").flat, + .audioFormatted(multipleRoleSeparator: ",").flat == "Dialogue.MixL,Dialogue.MixR" ) - XCTAssertEqual( + #expect( MarkerRoles(audio: ["Dialogue.MixL", "Dialogue.MixR"]) - .audioFormatted(multipleRoleSeparator: ",").array, + .audioFormatted(multipleRoleSeparator: ",").array == ["Dialogue.MixL", "Dialogue.MixR"] ) } diff --git a/Tests/MarkersExtractorTests/MarkersExtractorTests.swift b/Tests/MarkersExtractorTests/MarkersExtractorTests.swift index 1c93566..49e9f5c 100644 --- a/Tests/MarkersExtractorTests/MarkersExtractorTests.swift +++ b/Tests/MarkersExtractorTests/MarkersExtractorTests.swift @@ -5,12 +5,13 @@ // import DAWFileKit -@testable import MarkersExtractor +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class MarkersExtractorTests: XCTestCase { - func testFindDuplicateIDs_inMarkers() throws { +@Suite struct MarkersExtractorTests { + @Test func findDuplicateIDs_inMarkers() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: ""), outputDir: FileManager.default.temporaryDirectory @@ -49,30 +50,30 @@ final class MarkersExtractorTests: XCTestCase { let marker1 = makeMarker("marker1", position: .init(f: 1)) let marker2 = makeMarker("marker2", position: .init(f: 2)) - XCTAssertEqual( - extractor.findDuplicateIDs(in: []), [] + #expect( + extractor.findDuplicateIDs(in: []) == [] ) - XCTAssertEqual( - extractor.findDuplicateIDs(in: [marker1]), [] + #expect( + extractor.findDuplicateIDs(in: [marker1]) == [] ) - XCTAssertEqual( - extractor.findDuplicateIDs(in: [marker1, marker2]), [] + #expect( + extractor.findDuplicateIDs(in: [marker1, marker2]) == [] ) - XCTAssertEqual( - extractor.findDuplicateIDs(in: [marker1, marker1]), + #expect( + extractor.findDuplicateIDs(in: [marker1, marker1]) == [marker1.id(settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat)] ) - XCTAssertEqual( - extractor.findDuplicateIDs(in: [marker2, marker1, marker2]), + #expect( + extractor.findDuplicateIDs(in: [marker2, marker1, marker2]) == [marker2.id(settings.idNamingMode, tcStringFormat: extractor.timecodeStringFormat)] ) } - func testIsAllUniqueIDNonEmpty_inMarkers() throws { + @Test func isAllUniqueIDNonEmpty_inMarkers() async throws { var settings = try MarkersExtractor.Settings( fcpxml: FCPXMLFile(fileContents: ""), outputDir: FileManager.default.temporaryDirectory @@ -111,20 +112,20 @@ final class MarkersExtractorTests: XCTestCase { let marker1 = makeMarker("marker1", position: .init(f: 1)) let marker2 = makeMarker("", position: .init(f: 2)) - XCTAssertTrue( + #expect( extractor.isAllUniqueIDNonEmpty(in: []) ) - XCTAssertTrue( + #expect( extractor.isAllUniqueIDNonEmpty(in: [marker1]) ) - XCTAssertFalse( - extractor.isAllUniqueIDNonEmpty(in: [marker1, marker2]) + #expect( + !extractor.isAllUniqueIDNonEmpty(in: [marker1, marker2]) ) - XCTAssertFalse( - extractor.isAllUniqueIDNonEmpty(in: [marker2]) + #expect( + !extractor.isAllUniqueIDNonEmpty(in: [marker2]) ) } } diff --git a/Tests/MarkersExtractorTests/ProgressTests.swift b/Tests/MarkersExtractorTests/ProgressTests.swift index f8d8a3e..47e9e89 100644 --- a/Tests/MarkersExtractorTests/ProgressTests.swift +++ b/Tests/MarkersExtractorTests/ProgressTests.swift @@ -4,12 +4,13 @@ // Licensed under MIT License // -@testable import MarkersExtractor +import Testing +import TestingExtensions import TimecodeKitCore -import XCTest +@testable import MarkersExtractor -final class ProgressTests: XCTestCase { - func testFCPXMLMarkerExtractor() async throws { +@Suite struct ProgressTests { + @Test func fcpxmlMarkerExtractor() async throws { var file = FCPXMLFile(fileContents: fcpxmlTestString) let extractor = try FCPXMLMarkerExtractor( fcpxml: &file, @@ -21,15 +22,15 @@ final class ProgressTests: XCTestCase { logger: nil ) - XCTAssertEqual(extractor.progress.fractionCompleted, 0.0) - let context = try XCTUnwrap(extractor.extractTimelineContext(defaultTimelineName: "Timeline")) + #expect(extractor.progress.fractionCompleted == 0.0) + let context = try #require(extractor.extractTimelineContext(defaultTimelineName: "Timeline")) _ = await extractor.extractMarkers(context: context) // NOTE: this may randomly fail because NSProgress is garbage - XCTAssert(extractor.progress.fractionCompleted == 1.0 || extractor.progress.isFinished) + #expect(extractor.progress.fractionCompleted == 1.0 || extractor.progress.isFinished) } - func testAnimatedImageExtractor() async throws { + @Test func animatedImageExtractor() async throws { let videoData = try TestResource.videoTrack_29_97_Start_00_00_00_00.data() let videoPlaceholder = try TemporaryMediaFile(withData: videoData) let range = tc("00:00:00:00", at: .fps24) ... tc("00:00:00:10", at: .fps24) @@ -57,11 +58,11 @@ final class ProgressTests: XCTestCase { imageLabelProperties: .default() ) - XCTAssertEqual(writer.progress.fractionCompleted, 0.0) + #expect(writer.progress.fractionCompleted == 0.0) try await writer.write() // NOTE: this may randomly fail because NSProgress is garbage - XCTAssert(writer.progress.fractionCompleted == 1.0 || writer.progress.isFinished) + #expect(writer.progress.fractionCompleted == 1.0 || writer.progress.isFinished) // MARK: - AnimatedImageExtractor @@ -77,14 +78,14 @@ final class ProgressTests: XCTestCase { ) ) - XCTAssertEqual(extractor.progress.fractionCompleted, 0.0) + #expect(extractor.progress.fractionCompleted == 0.0) let _ = try await extractor.convert() // NOTE: this may randomly fail because NSProgress is garbage - XCTAssert(extractor.progress.fractionCompleted == 1.0 || extractor.progress.isFinished) + #expect(extractor.progress.fractionCompleted == 1.0 || extractor.progress.isFinished) } - func testStillImageBatchExtractor() async throws { + @Test func stillImageBatchExtractor() async throws { let videoData = try TestResource.videoTrack_29_97_Start_00_00_00_00.data() let videoPlaceholder = try TemporaryMediaFile(withData: videoData) let range = tc("00:00:00:00", at: .fps24) ... tc("00:00:00:10", at: .fps24) @@ -110,11 +111,11 @@ final class ProgressTests: XCTestCase { imageLabelProperties: .default() ) - XCTAssertEqual(writer.progress.fractionCompleted, 0.0) + #expect(writer.progress.fractionCompleted == 0.0) try await writer.write() // NOTE: this may randomly fail because NSProgress is garbage - XCTAssert(writer.progress.fractionCompleted == 1.0 || writer.progress.isFinished) + #expect(writer.progress.fractionCompleted == 1.0 || writer.progress.isFinished) // MARK: - StillImageBatchExtractor @@ -130,11 +131,11 @@ final class ProgressTests: XCTestCase { ) ) - XCTAssertEqual(extractor.progress.fractionCompleted, 0.0) + #expect(extractor.progress.fractionCompleted == 0.0) let _ = try await extractor.convert() // NOTE: this may randomly fail because NSProgress is garbage - XCTAssert(extractor.progress.fractionCompleted == 1.0 || extractor.progress.isFinished) + #expect(extractor.progress.fractionCompleted == 1.0 || extractor.progress.isFinished) } } diff --git a/Tests/MarkersExtractorTests/Test Utilities.swift b/Tests/MarkersExtractorTests/Test Utilities.swift index cce4644..6e5b8cd 100644 --- a/Tests/MarkersExtractorTests/Test Utilities.swift +++ b/Tests/MarkersExtractorTests/Test Utilities.swift @@ -6,26 +6,25 @@ @testable import MarkersExtractor import TimecodeKitCore -import XCTest -extension XCTestCase { - /// Convenience timecode constructor. Final Cut Pro always uses 80 subframes base. - func tc(_ string: String, at frameRate: TimecodeFrameRate) -> Timecode { - try! Timecode( - .string(string), - at: frameRate, - base: .max80SubFrames, - limit: .max24Hours - ) - } - - /// Convenience timecode constructor. Final Cut Pro always uses 80 subframes base. - func tc(_ components: Timecode.Components, at frameRate: TimecodeFrameRate) -> Timecode { - try! Timecode( - .components(components), - at: frameRate, - base: .max80SubFrames, - limit: .max24Hours - ) - } +/// Convenience timecode constructor. +/// Final Cut Pro always uses 80 subframes base. +func tc(_ string: String, at frameRate: TimecodeFrameRate) -> Timecode { + try! Timecode( + .string(string), + at: frameRate, + base: .max80SubFrames, + limit: .max24Hours + ) +} + +/// Convenience timecode constructor. +/// Final Cut Pro always uses 80 subframes base. +func tc(_ components: Timecode.Components, at frameRate: TimecodeFrameRate) -> Timecode { + try! Timecode( + .components(components), + at: frameRate, + base: .max80SubFrames, + limit: .max24Hours + ) } diff --git a/Tests/MarkersExtractorTests/TestResource/TestResource.swift b/Tests/MarkersExtractorTests/TestResource/TestResource.swift index 714709f..0a6f4cb 100644 --- a/Tests/MarkersExtractorTests/TestResource/TestResource.swift +++ b/Tests/MarkersExtractorTests/TestResource/TestResource.swift @@ -5,73 +5,14 @@ // import Foundation -import XCTest +import Testing +import TestingExtensions -// NOTE: DO NOT name any folders "Resources". Xcode will fail to build iOS targets. - -// MARK: - Constants +// NOTE: DO NOT name any folders "Resources". Xcode may fail to build targets. /// Resources files on disk used for unit testing. -enum TestResource: CaseIterable { +extension TestResource { static let videoTrack_29_97_Start_00_00_00_00 = TestResource.File( name: "VideoTrack_29_97_Start-00-00-00-00", ext: "mp4", subFolder: "Media Files" ) } - -// MARK: - Utilities - -extension TestResource { - struct File: Equatable, Hashable { - let name: String - let ext: String - let subFolder: String? - - var fileName: String { name + "." + ext } - } -} - -extension TestResource.File { - func url( - _ message: @autoclosure () -> String = "", - file: StaticString = #filePath, - line: UInt = #line - ) throws -> URL { - // Bundle.module is synthesized when the package target has `resources: [...]` - guard let url = Bundle.module.url( - forResource: name, - withExtension: ext, - subdirectory: subFolder - ) - else { - var msg = message() - msg = msg.isEmpty ? "" : ": \(msg)" - XCTFail( - "Could not form URL, possibly could not find file.\(msg)", - file: file, - line: line - ) - throw XCTSkip() - } - return url - } - - func data( - _ message: @autoclosure () -> String = "", - file: StaticString = #filePath, - line: UInt = #line - ) throws -> Data { - let url = try url() - guard let data = try? Data(contentsOf: url) - else { - var msg = message() - msg = msg.isEmpty ? "" : ": \(msg)" - XCTFail( - "Could not read file at URL: \(url.absoluteString).", - file: file, - line: line - ) - throw XCTSkip("Aborting test.") - } - return data - } -}