diff --git a/concepts/for-loops/.meta/config.json b/concepts/for-loops/.meta/config.json index d5cb6a257..1d255acef 100644 --- a/concepts/for-loops/.meta/config.json +++ b/concepts/for-loops/.meta/config.json @@ -1,7 +1,8 @@ { "blurb": "For loops can be used to iterate over a sequence of values.", "authors": [ - "wneumann" + "wneumann", + "meatball133" ], "contributors": [] } diff --git a/concepts/for-loops/about.md b/concepts/for-loops/about.md index 32c347589..b09ef265f 100644 --- a/concepts/for-loops/about.md +++ b/concepts/for-loops/about.md @@ -1,84 +1,121 @@ -# About +# For loops -[For-in loops][for-in-loops] are used to iterate over a sequence of values, taking each element in turn, binding it to a variable or constant name of the developer's choosing, then executes a block of code that may refer to the element. When every element of the sequence has been iterated over, the loop exits and execution begins with the first line following the body of the loop. +Looping is a fundamental concept in programming that allows you to execute a block of code multiple times. +In Swift, there are two types of loops: [`for-in` loops][for-loops] and `while` loops. +In this chapter, you'll learn about `for-in` loops. + +For loops allows you to iterate over a sequence of values, taking each element in turn, binding it to a variable of your choosing. +Swift allows you to iterate over a variety of sequences, such as ranges, arrays, and strings (and more types which will be covered later). +When every element of the sequence has been iterated over, the loop exits. + +For loops are declared by using the `for` keyword, followed by a variable name, the `in` keyword, and a sequence of values to iterate over. +The variable given in the `for-in` loop is inmutable, meaning you can't change its value inside the loop. +Here's an example of a `for-in` loop that iterates over an array of numbers: ```swift let numbers = [3, 10, 7, 11] -let word = "Supercalifragilisticexpialidocious" for number in numbers { - print("\(number) / 2 = \(number / 2)") + print(number) } print("Done with numbers") // prints: -// 3 / 2 = 1 -// 10 / 2 = 5 -// 7 / 2 = 3 -// 11 / 2 = 5 +// 3 +// 10 +// 7 +// 11 // Done with numbers +``` +~~~~exercism/note +The `number` variable is declared in the `for-in` loop and is only available within the loop's scope. -for char in word { - if "aeiou".contains(char) { - print(char, terminator: "") - } -} -print(" - those are all the vowels") - -// prints: -// ueaiaiiieiaioiou - those are all the vowels +```swift +let numbers = [3, 10, 7, 11] +for number in numbers { + number + 1 +} +number + 1 +// Error: Use of unresolved identifier 'number' ``` +~~~~ + +## Iterating over a range -If one needs to mutate the current element of the iteration, it can be declared as a variable in the for-in loop: +You can also iterate over a range of numbers using a `for-in` loop. +This allows you to execute a block of code a specific number of times, for example, the range `1...5` will iterate over the numbers 1, 2, 3, 4, and 5, so the loop will execute 5 times. +Sometimes you might want to iterate over indexes, in a datastructure like an array, then you can use a `0.. 0 { - print(x % 10) - x /= 10 - } - print() +let numbers = [3, 10, 7, 11] + +for i in 0.. Int { + var count = 0 + for day in birdsPerDay { + count += day + } + return count +} + +func birdsInWeek(_ birdsPerDay: [Int], weekNumber: Int) -> Int { + var count = 0 + let startOfWeek = (weekNumber - 1) * 7 + let endOfWeek = startOfWeek + 7 + for i in startOfWeek.. [Int] { + var result = [Int](birdsPerDay) + for i in stride(from: 0, through: birdsPerDay.count - 1, by: 2) { + result[i] += 1 + } + return result +} \ No newline at end of file diff --git a/exercises/concept/bird-watcher/.meta/config.json b/exercises/concept/bird-watcher/.meta/config.json new file mode 100644 index 000000000..bcf1eff1b --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "meatball133" + ], + "files": { + "solution": [ + "Sources/BirdWatcher/BirdWatcher.swift" + ], + "test": [ + "Tests/BirdWatcherTests/BirdWatcherTests.swift" + ], + "exemplar": [ + ".meta/Sources/BirdWatcher/BirdWatcherExemplar.swift" + ] + }, + "forked_from": [ + "go/bird-watcher" + ], + "blurb": "Count the birds in your garden with for loops." +} diff --git a/exercises/concept/bird-watcher/.meta/design.md b/exercises/concept/bird-watcher/.meta/design.md new file mode 100644 index 000000000..07f490d2f --- /dev/null +++ b/exercises/concept/bird-watcher/.meta/design.md @@ -0,0 +1,31 @@ +# Design + +This concept exercise should convey a basic understanding of how to work with closures in Swift. + +## Learning objectives + +- Defining closures +- Assigning closures to names +- Capturing the environment +- Shorthand argument names +- Trailing closure syntax + +## Out of scope + +- Capture lists +- Weak/Unowned +- Escaping closures +- Autoclosures + +## Concepts + +- `closures`: Know how to define closures; know how to assign closures to names +- `capturing`: know about environment capture +- `trailing-closures`: know about trailing closure syntax +- `shorthand-arguments`: know about shorthand closure syntax + +## Prerequisites + +- `functions` +- `higher-order-functions` +- `loops` diff --git a/exercises/concept/bird-watcher/Package.swift b/exercises/concept/bird-watcher/Package.swift new file mode 100644 index 000000000..25472ce29 --- /dev/null +++ b/exercises/concept/bird-watcher/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version:6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "BirdWatcher", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "BirdWatcher", + targets: ["BirdWatcher"]) + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "BirdWatcher", + dependencies: []), + .testTarget( + name: "BirdWatcherTests", + dependencies: ["BirdWatcher"]), + ] +) diff --git a/exercises/concept/bird-watcher/Sources/BirdWatcher/BirdWatcher.swift b/exercises/concept/bird-watcher/Sources/BirdWatcher/BirdWatcher.swift new file mode 100644 index 000000000..e84c68d84 --- /dev/null +++ b/exercises/concept/bird-watcher/Sources/BirdWatcher/BirdWatcher.swift @@ -0,0 +1,11 @@ +func totalBirdCount(_ birdsPerDay: [Int]) -> Int { + fatalError("Please implement the totalBirdCount(_:) function") +} + +func birdsInWeek(_ birdsPerDay: [Int], weekNumber: Int) -> Int { + fatalError("Please implement the birdsInWeek(_:weekNumber:) function") +} + +func fixBirdCountLog(_ birdsPerDay: [Int]) -> [Int] { + fatalError("Please implement the birdsInWeek(_:) function") +} diff --git a/exercises/concept/bird-watcher/Tests/BirdWatcherTests/BirdWatcherTests.swift b/exercises/concept/bird-watcher/Tests/BirdWatcherTests/BirdWatcherTests.swift new file mode 100644 index 000000000..aa2b68f08 --- /dev/null +++ b/exercises/concept/bird-watcher/Tests/BirdWatcherTests/BirdWatcherTests.swift @@ -0,0 +1,82 @@ +import Foundation +import Testing + +@testable import BirdWatcher + +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct BirdWatcherTests { + @Test("calculates the correct total number of birds") + func testTotalBirdCount() { + let birdCount = [9, 0, 8, 4, 5, 1, 3] + let expected = 30 + #expect(totalBirdCount(birdCount) == expected) + } + + @Test("works for a short bird count list", .enabled(if: RUNALL)) + func testTotalBirdCountShort() { + let birdCount = [2] + let expected = 2 + #expect(totalBirdCount(birdCount) == expected) + } + + @Test("works for a long bird count list", .enabled(if: RUNALL)) + func testTotalBirdCountLong() { + let birdCount = [2, 8, 4, 1, 3, 5, 0, 4, 1, 6, 0, 3, 0, 1, 5, 4, 1, 1, 2, 6] + let expected = 57 + #expect(totalBirdCount(birdCount) == expected) + } + + @Test("calculates the number of birds in the first week", .enabled(if: RUNALL)) + func testBirdsInFirstWeek() { + let birdCount = [3, 0, 5, 1, 0, 4, 1, 0, 3, 4, 3, 0, 8, 0] + let weekNumber = 1 + let expected = 14 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("calculates the number of birds for a week in the middle of the log", .enabled(if: RUNALL)) + func testBirdsInMiddleWeek() { + let birdCount = [4, 7, 3, 2, 1, 1, 2, 0, 2, 3, 2, 7, 1, 3, 0, 6, 5, 3, 7, 2, 3] + let weekNumber = 2 + let expected = 18 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("calculates the number of birds for a week in the end of the log", .enabled(if: RUNALL)) + func testBirdsInLastWeek() { + let birdCount = [4, 7, 3, 2, 1, 1, 2, 0, 2, 3, 2, 7, 1, 3, 0, 6, 5, 3, 7, 2, 3] + let weekNumber = 3 + let expected = 26 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("works when there is only one week", .enabled(if: RUNALL)) + func testBirdsInOneWeek() { + let birdCount = [3, 0, 3, 3, 2, 1, 0] + let weekNumber = 1 + let expected = 12 + #expect(birdsInWeek(birdCount, weekNumber: weekNumber) == expected) + } + + @Test("returns a bird count list with the corrected values", .enabled(if: RUNALL)) + func testFixBirdCountLog() { + var birdCount = [3, 0, 5, 1, 0, 4, 1, 0, 3, 4, 3, 0] + let expected = [4, 0, 6, 1, 1, 4, 2, 0, 4, 4, 4, 0] + #expect(fixBirdCountLog(birdCount) == expected) + } + + @Test("works for a short bird count list", .enabled(if: RUNALL)) + func testFixBirdCountLogShort() { + var birdCount = [4, 2] + let expected = [5, 2] + #expect(fixBirdCountLog(birdCount) == expected) + } + + @Test("works for a long bird count list", .enabled(if: RUNALL)) + func testFixBirdCountLogLong() { + var birdCount = [2, 8, 4, 1, 3, 5, 0, 4, 1, 6, 0, 3, 0, 1, 5, 4, 1, 1, 2, 6] + let expected = [3, 8, 5, 1, 4, 5, 1, 4, 2, 6, 1, 3, 1, 1, 6, 4, 2, 1, 3, 6] + #expect(fixBirdCountLog(birdCount) == expected) + } +} diff --git a/exercises/practice/space-age/.meta/template.swift b/exercises/practice/space-age/.meta/template.swift index ca6622e6c..76023a291 100644 --- a/exercises/practice/space-age/.meta/template.swift +++ b/exercises/practice/space-age/.meta/template.swift @@ -1,17 +1,20 @@ -import XCTest +import Testing +import Foundation +import Numerics @testable import {{exercise|camelCase}} -class {{exercise|camelCase}}Tests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct {{exercise|camelCase}}Tests { {% for case in cases %} {% if forloop.first -%} - func test{{case.description |camelCase }}() { + @Test("{{case.description}}") {% else -%} - func test{{case.description |camelCase }}() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("{{case.description}}", .enabled(if: RUNALL)) {% endif -%} + func test{{case.description |camelCase }}() { let age = SpaceAge({{case.input.seconds}}) - XCTAssertEqual(age.on{{case.input.planet |camelCase}}, {{case.expected | round:2 }}, accuracy: 0.02) + #expect(age.on{{case.input.planet |camelCase}}.isApproximatelyEqual(to: {{case.expected | round:2 }}, relativeTolerance: 0.03)) } {% endfor -%} } diff --git a/exercises/practice/space-age/Package.swift b/exercises/practice/space-age/Package.swift index 1c6b780b8..c1ae8b424 100644 --- a/exercises/practice/space-age/Package.swift +++ b/exercises/practice/space-age/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:6.0 import PackageDescription @@ -9,13 +9,15 @@ let package = Package( name: "SpaceAge", targets: ["SpaceAge"]) ], - dependencies: [], + dependencies: [ + .package(url: "https://github.com/apple/swift-numerics", from: "1.0.2"), + ], targets: [ .target( name: "SpaceAge", - dependencies: []), + dependencies: [.product(name: "Numerics", package: "swift-numerics")]), .testTarget( name: "SpaceAgeTests", - dependencies: ["SpaceAge"]), + dependencies: [.product(name: "Numerics", package: "swift-numerics"), "SpaceAge"]), ] ) diff --git a/exercises/practice/space-age/Sources/SpaceAge/SpaceAge.swift b/exercises/practice/space-age/Sources/SpaceAge/SpaceAge.swift index f8a8f6fc2..475232072 100644 --- a/exercises/practice/space-age/Sources/SpaceAge/SpaceAge.swift +++ b/exercises/practice/space-age/Sources/SpaceAge/SpaceAge.swift @@ -1,3 +1,16 @@ class SpaceAge { - // Write your code for the 'SpaceAge' exercise in this file. + var seconds: Float = 0 + + var onMercury: Float { return ((seconds / 7_600_530.24) * 100).rounded() / 100 } + var onVenus: Float { return ((seconds / 19_413_907.2) * 100).rounded() / 100 } + var onEarth: Float { return ((seconds / 31_558_149.76) * 100).rounded() / 100 } + var onMars: Float { return ((seconds / 59_354_294.4) * 100).rounded() / 100 } + var onJupiter: Float { return ((seconds / 374_335_776.0) * 100).rounded() / 100 } + var onSaturn: Float { return ((seconds / 929_596_608.0) * 100).rounded() / 100 } + var onUranus: Float { return ((seconds / 2_661_041_808.0) * 100).rounded() / 100 } + var onNeptune: Float { return ((seconds / 5_200_418_592.0) * 100).rounded() / 100 } + + init(_ input: Float) { + self.seconds = input + } } diff --git a/exercises/practice/space-age/Tests/SpaceAgeTests/SpaceAgeTests.swift b/exercises/practice/space-age/Tests/SpaceAgeTests/SpaceAgeTests.swift index 1366966d2..580537923 100644 --- a/exercises/practice/space-age/Tests/SpaceAgeTests/SpaceAgeTests.swift +++ b/exercises/practice/space-age/Tests/SpaceAgeTests/SpaceAgeTests.swift @@ -1,54 +1,58 @@ -import XCTest +import Foundation +import Numerics +import Testing @testable import SpaceAge -class SpaceAgeTests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +@Suite struct SpaceAgeTests { + + @Test("age on Earth") func testAgeOnEarth() { let age = SpaceAge(1_000_000_000) - XCTAssertEqual(age.onEarth, 31.69, accuracy: 0.02) + #expect(age.onEarth.isApproximatelyEqual(to: 31.69, relativeTolerance: 0.03)) } - func testAgeOnMercury() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Mercury", .enabled(if: RUNALL)) + func testAgeOnMercury() { let age = SpaceAge(2_134_835_688) - XCTAssertEqual(age.onMercury, 280.88, accuracy: 0.02) + #expect(age.onMercury.isApproximatelyEqual(to: 280.88, relativeTolerance: 0.03)) } - func testAgeOnVenus() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Venus", .enabled(if: RUNALL)) + func testAgeOnVenus() { let age = SpaceAge(189_839_836) - XCTAssertEqual(age.onVenus, 9.78, accuracy: 0.02) + #expect(age.onVenus.isApproximatelyEqual(to: 9.78, relativeTolerance: 0.03)) } - func testAgeOnMars() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Mars", .enabled(if: RUNALL)) + func testAgeOnMars() { let age = SpaceAge(2_129_871_239) - XCTAssertEqual(age.onMars, 35.88, accuracy: 0.02) + #expect(age.onMars.isApproximatelyEqual(to: 35.88, relativeTolerance: 0.03)) } - func testAgeOnJupiter() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Jupiter", .enabled(if: RUNALL)) + func testAgeOnJupiter() { let age = SpaceAge(901_876_382) - XCTAssertEqual(age.onJupiter, 2.41, accuracy: 0.02) + #expect(age.onJupiter.isApproximatelyEqual(to: 2.41, relativeTolerance: 0.03)) } - func testAgeOnSaturn() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Saturn", .enabled(if: RUNALL)) + func testAgeOnSaturn() { let age = SpaceAge(2_000_000_000) - XCTAssertEqual(age.onSaturn, 2.15, accuracy: 0.02) + #expect(age.onSaturn.isApproximatelyEqual(to: 2.15, relativeTolerance: 0.03)) } - func testAgeOnUranus() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Uranus", .enabled(if: RUNALL)) + func testAgeOnUranus() { let age = SpaceAge(1_210_123_456) - XCTAssertEqual(age.onUranus, 0.46, accuracy: 0.02) + #expect(age.onUranus.isApproximatelyEqual(to: 0.46, relativeTolerance: 0.03)) } - func testAgeOnNeptune() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("age on Neptune", .enabled(if: RUNALL)) + func testAgeOnNeptune() { let age = SpaceAge(1_821_023_456) - XCTAssertEqual(age.onNeptune, 0.35, accuracy: 0.02) + #expect(age.onNeptune.isApproximatelyEqual(to: 0.35, relativeTolerance: 0.03)) } } diff --git a/exercises/practice/strain/.meta/template.swift b/exercises/practice/strain/.meta/template.swift index 181fe63a7..e49bf8608 100644 --- a/exercises/practice/strain/.meta/template.swift +++ b/exercises/practice/strain/.meta/template.swift @@ -1,15 +1,17 @@ -import XCTest +import Testing +import Foundation @testable import {{exercise|camelCase}} -class {{exercise|camelCase}}Tests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct {{exercise|camelCase}}Tests { {% for case in cases %} {% if forloop.first -%} - func test{{case.description |camelCase }}() { + @Test("{{case.description}}") {% else -%} - func test{{case.description |camelCase }}() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("{{case.description}}", .enabled(if: RUNALL)) {% endif -%} + func test{{case.description |camelCase }}() { {%- if case.input.list.count == 0 -%} let input : [Int] = [] {%- else -%} @@ -20,7 +22,7 @@ class {{exercise|camelCase}}Tests: XCTestCase { {%- else %} let expected = {{case.expected | toStringArray}} {%- endif %} - XCTAssertEqual(input.{{case.property}} {{case.input.predicate | strain}}, expected) + #expect(input.{{case.property}} {{case.input.predicate | strain}} == expected) } {% endfor -%} } diff --git a/exercises/practice/strain/Package.swift b/exercises/practice/strain/Package.swift index 778628ef4..3944cabcf 100644 --- a/exercises/practice/strain/Package.swift +++ b/exercises/practice/strain/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:6.0 import PackageDescription diff --git a/exercises/practice/strain/Tests/StrainTests/StrainTests.swift b/exercises/practice/strain/Tests/StrainTests/StrainTests.swift index 0adf43202..1a97c910c 100644 --- a/exercises/practice/strain/Tests/StrainTests/StrainTests.swift +++ b/exercises/practice/strain/Tests/StrainTests/StrainTests.swift @@ -1,108 +1,111 @@ -import XCTest +import Foundation +import Testing @testable import Strain -class StrainTests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +@Suite struct StrainTests { + + @Test("keep on empty list returns empty list") func testKeepOnEmptyListReturnsEmptyList() { let input: [Int] = [] let expected: [Int] = [] - XCTAssertEqual(input.keep { x in true }, expected) + #expect(input.keep { x in true } == expected) } - func testKeepsEverything() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("keeps everything", .enabled(if: RUNALL)) + func testKeepsEverything() { let input = [1, 3, 5] let expected = [1, 3, 5] - XCTAssertEqual(input.keep { x in true }, expected) + #expect(input.keep { x in true } == expected) } - func testKeepsNothing() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("keeps nothing", .enabled(if: RUNALL)) + func testKeepsNothing() { let input = [1, 3, 5] let expected: [Int] = [] - XCTAssertEqual(input.keep { x in false }, expected) + #expect(input.keep { x in false } == expected) } - func testKeepsFirstAndLast() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("keeps first and last", .enabled(if: RUNALL)) + func testKeepsFirstAndLast() { let input = [1, 2, 3] let expected = [1, 3] - XCTAssertEqual(input.keep { x in x % 2 == 1 }, expected) + #expect(input.keep { x in x % 2 == 1 } == expected) } - func testKeepsNeitherFirstNorLast() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("keeps neither first nor last", .enabled(if: RUNALL)) + func testKeepsNeitherFirstNorLast() { let input = [1, 2, 3] let expected = [2] - XCTAssertEqual(input.keep { x in x % 2 == 0 }, expected) + #expect(input.keep { x in x % 2 == 0 } == expected) } - func testKeepsStrings() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("keeps strings", .enabled(if: RUNALL)) + func testKeepsStrings() { let input = ["apple", "zebra", "banana", "zombies", "cherimoya", "zealot"] let expected = ["zebra", "zombies", "zealot"] - XCTAssertEqual(input.keep { x in x.starts(with: "z") }, expected) + #expect(input.keep { x in x.starts(with: "z") } == expected) } - func testKeepsLists() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("keeps lists", .enabled(if: RUNALL)) + func testKeepsLists() { let input = [ [1, 2, 3], [5, 5, 5], [5, 1, 2], [2, 1, 2], [1, 5, 2], [2, 2, 1], [1, 2, 5], ] let expected = [[5, 5, 5], [5, 1, 2], [1, 5, 2], [1, 2, 5]] - XCTAssertEqual(input.keep { x in x.contains(5) }, expected) + #expect(input.keep { x in x.contains(5) } == expected) } - func testDiscardOnEmptyListReturnsEmptyList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discard on empty list returns empty list", .enabled(if: RUNALL)) + func testDiscardOnEmptyListReturnsEmptyList() { let input: [Int] = [] let expected: [Int] = [] - XCTAssertEqual(input.discard { x in true }, expected) + #expect(input.discard { x in true } == expected) } - func testDiscardsEverything() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discards everything", .enabled(if: RUNALL)) + func testDiscardsEverything() { let input = [1, 3, 5] let expected: [Int] = [] - XCTAssertEqual(input.discard { x in true }, expected) + #expect(input.discard { x in true } == expected) } - func testDiscardsNothing() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discards nothing", .enabled(if: RUNALL)) + func testDiscardsNothing() { let input = [1, 3, 5] let expected = [1, 3, 5] - XCTAssertEqual(input.discard { x in false }, expected) + #expect(input.discard { x in false } == expected) } - func testDiscardsFirstAndLast() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discards first and last", .enabled(if: RUNALL)) + func testDiscardsFirstAndLast() { let input = [1, 2, 3] let expected = [2] - XCTAssertEqual(input.discard { x in x % 2 == 1 }, expected) + #expect(input.discard { x in x % 2 == 1 } == expected) } - func testDiscardsNeitherFirstNorLast() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discards neither first nor last", .enabled(if: RUNALL)) + func testDiscardsNeitherFirstNorLast() { let input = [1, 2, 3] let expected = [1, 3] - XCTAssertEqual(input.discard { x in x % 2 == 0 }, expected) + #expect(input.discard { x in x % 2 == 0 } == expected) } - func testDiscardsStrings() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discards strings", .enabled(if: RUNALL)) + func testDiscardsStrings() { let input = ["apple", "zebra", "banana", "zombies", "cherimoya", "zealot"] let expected = ["apple", "banana", "cherimoya"] - XCTAssertEqual(input.discard { x in x.starts(with: "z") }, expected) + #expect(input.discard { x in x.starts(with: "z") } == expected) } - func testDiscardsLists() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("discards lists", .enabled(if: RUNALL)) + func testDiscardsLists() { let input = [ [1, 2, 3], [5, 5, 5], [5, 1, 2], [2, 1, 2], [1, 5, 2], [2, 2, 1], [1, 2, 5], ] let expected = [[1, 2, 3], [2, 1, 2], [2, 2, 1]] - XCTAssertEqual(input.discard { x in x.contains(5) }, expected) + #expect(input.discard { x in x.contains(5) } == expected) } } diff --git a/exercises/practice/sublist/.docs/instructions.md b/exercises/practice/sublist/.docs/instructions.md index 7535931af..8228edc6c 100644 --- a/exercises/practice/sublist/.docs/instructions.md +++ b/exercises/practice/sublist/.docs/instructions.md @@ -8,8 +8,8 @@ Given any two lists `A` and `B`, determine if: - None of the above is true, thus lists `A` and `B` are unequal Specifically, list `A` is equal to list `B` if both lists have the same values in the same order. -List `A` is a superlist of `B` if `A` contains a sub-sequence of values equal to `B`. -List `A` is a sublist of `B` if `B` contains a sub-sequence of values equal to `A`. +List `A` is a superlist of `B` if `A` contains a contiguous sub-sequence of values equal to `B`. +List `A` is a sublist of `B` if `B` contains a contiguous sub-sequence of values equal to `A`. Examples: diff --git a/exercises/practice/sublist/.meta/template.swift b/exercises/practice/sublist/.meta/template.swift index 09b3e1852..bbfe98726 100644 --- a/exercises/practice/sublist/.meta/template.swift +++ b/exercises/practice/sublist/.meta/template.swift @@ -1,16 +1,18 @@ -import XCTest +import Testing +import Foundation @testable import {{exercise|camelCase}} -class {{exercise|camelCase}}Tests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct {{exercise|camelCase}}Tests { {% for case in cases %} {% if forloop.first -%} - func test{{case.description |camelCase }}() { + @Test("{{case.description}}") {% else -%} - func test{{case.description |camelCase }}() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("{{case.description}}", .enabled(if: RUNALL)) {% endif -%} - XCTAssertEqual(.{{case.expected}}, classifier(listOne: {{case.input.listOne}}, listTwo: {{case.input.listTwo}})) + func test{{case.description |camelCase }}() { + #expect(.{{case.expected}} == classifier(listOne: {{case.input.listOne}}, listTwo: {{case.input.listTwo}})) } {% endfor -%} } diff --git a/exercises/practice/sublist/Package.swift b/exercises/practice/sublist/Package.swift index f76b5bd24..879055d7f 100644 --- a/exercises/practice/sublist/Package.swift +++ b/exercises/practice/sublist/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:6.0 import PackageDescription diff --git a/exercises/practice/sublist/Tests/SublistTests/SublistTests.swift b/exercises/practice/sublist/Tests/SublistTests/SublistTests.swift index 5e46e5977..53b3fc334 100644 --- a/exercises/practice/sublist/Tests/SublistTests/SublistTests.swift +++ b/exercises/practice/sublist/Tests/SublistTests/SublistTests.swift @@ -1,96 +1,99 @@ -import XCTest +import Foundation +import Testing @testable import Sublist -class SublistTests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +@Suite struct SublistTests { + + @Test("empty lists") func testEmptyLists() { - XCTAssertEqual(.equal, classifier(listOne: [], listTwo: [])) + #expect(.equal == classifier(listOne: [], listTwo: [])) } - func testEmptyListWithinNonEmptyList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.sublist, classifier(listOne: [], listTwo: [1, 2, 3])) + @Test("empty list within non empty list", .enabled(if: RUNALL)) + func testEmptyListWithinNonEmptyList() { + #expect(.sublist == classifier(listOne: [], listTwo: [1, 2, 3])) } - func testNonEmptyListContainsEmptyList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.superlist, classifier(listOne: [1, 2, 3], listTwo: [])) + @Test("non empty list contains empty list", .enabled(if: RUNALL)) + func testNonEmptyListContainsEmptyList() { + #expect(.superlist == classifier(listOne: [1, 2, 3], listTwo: [])) } - func testListEqualsItself() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.equal, classifier(listOne: [1, 2, 3], listTwo: [1, 2, 3])) + @Test("list equals itself", .enabled(if: RUNALL)) + func testListEqualsItself() { + #expect(.equal == classifier(listOne: [1, 2, 3], listTwo: [1, 2, 3])) } - func testDifferentLists() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.unequal, classifier(listOne: [1, 2, 3], listTwo: [2, 3, 4])) + @Test("different lists", .enabled(if: RUNALL)) + func testDifferentLists() { + #expect(.unequal == classifier(listOne: [1, 2, 3], listTwo: [2, 3, 4])) } - func testFalseStart() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.sublist, classifier(listOne: [1, 2, 5], listTwo: [0, 1, 2, 3, 1, 2, 5, 6])) + @Test("false start", .enabled(if: RUNALL)) + func testFalseStart() { + #expect(.sublist == classifier(listOne: [1, 2, 5], listTwo: [0, 1, 2, 3, 1, 2, 5, 6])) } - func testConsecutive() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.sublist, classifier(listOne: [1, 1, 2], listTwo: [0, 1, 1, 1, 2, 1, 2])) + @Test("consecutive", .enabled(if: RUNALL)) + func testConsecutive() { + #expect(.sublist == classifier(listOne: [1, 1, 2], listTwo: [0, 1, 1, 1, 2, 1, 2])) } - func testSublistAtStart() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.sublist, classifier(listOne: [0, 1, 2], listTwo: [0, 1, 2, 3, 4, 5])) + @Test("sublist at start", .enabled(if: RUNALL)) + func testSublistAtStart() { + #expect(.sublist == classifier(listOne: [0, 1, 2], listTwo: [0, 1, 2, 3, 4, 5])) } - func testSublistInMiddle() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.sublist, classifier(listOne: [2, 3, 4], listTwo: [0, 1, 2, 3, 4, 5])) + @Test("sublist in middle", .enabled(if: RUNALL)) + func testSublistInMiddle() { + #expect(.sublist == classifier(listOne: [2, 3, 4], listTwo: [0, 1, 2, 3, 4, 5])) } - func testSublistAtEnd() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.sublist, classifier(listOne: [3, 4, 5], listTwo: [0, 1, 2, 3, 4, 5])) + @Test("sublist at end", .enabled(if: RUNALL)) + func testSublistAtEnd() { + #expect(.sublist == classifier(listOne: [3, 4, 5], listTwo: [0, 1, 2, 3, 4, 5])) } - func testAtStartOfSuperlist() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.superlist, classifier(listOne: [0, 1, 2, 3, 4, 5], listTwo: [0, 1, 2])) + @Test("at start of superlist", .enabled(if: RUNALL)) + func testAtStartOfSuperlist() { + #expect(.superlist == classifier(listOne: [0, 1, 2, 3, 4, 5], listTwo: [0, 1, 2])) } - func testInMiddleOfSuperlist() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.superlist, classifier(listOne: [0, 1, 2, 3, 4, 5], listTwo: [2, 3])) + @Test("in middle of superlist", .enabled(if: RUNALL)) + func testInMiddleOfSuperlist() { + #expect(.superlist == classifier(listOne: [0, 1, 2, 3, 4, 5], listTwo: [2, 3])) } - func testAtEndOfSuperlist() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.superlist, classifier(listOne: [0, 1, 2, 3, 4, 5], listTwo: [3, 4, 5])) + @Test("at end of superlist", .enabled(if: RUNALL)) + func testAtEndOfSuperlist() { + #expect(.superlist == classifier(listOne: [0, 1, 2, 3, 4, 5], listTwo: [3, 4, 5])) } - func testFirstListMissingElementFromSecondList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.unequal, classifier(listOne: [1, 3], listTwo: [1, 2, 3])) + @Test("first list missing element from second list", .enabled(if: RUNALL)) + func testFirstListMissingElementFromSecondList() { + #expect(.unequal == classifier(listOne: [1, 3], listTwo: [1, 2, 3])) } - func testSecondListMissingElementFromFirstList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.unequal, classifier(listOne: [1, 2, 3], listTwo: [1, 3])) + @Test("second list missing element from first list", .enabled(if: RUNALL)) + func testSecondListMissingElementFromFirstList() { + #expect(.unequal == classifier(listOne: [1, 2, 3], listTwo: [1, 3])) } - func testFirstListMissingAdditionalDigitsFromSecondList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.unequal, classifier(listOne: [1, 2], listTwo: [1, 22])) + @Test("first list missing additional digits from second list", .enabled(if: RUNALL)) + func testFirstListMissingAdditionalDigitsFromSecondList() { + #expect(.unequal == classifier(listOne: [1, 2], listTwo: [1, 22])) } - func testOrderMattersToAList() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.unequal, classifier(listOne: [1, 2, 3], listTwo: [3, 2, 1])) + @Test("order matters to a list", .enabled(if: RUNALL)) + func testOrderMattersToAList() { + #expect(.unequal == classifier(listOne: [1, 2, 3], listTwo: [3, 2, 1])) } - func testSameDigitsButDifferentNumbers() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(.unequal, classifier(listOne: [1, 0, 1], listTwo: [10, 1])) + @Test("same digits but different numbers", .enabled(if: RUNALL)) + func testSameDigitsButDifferentNumbers() { + #expect(.unequal == classifier(listOne: [1, 0, 1], listTwo: [10, 1])) } } diff --git a/exercises/practice/sum-of-multiples/.meta/template.swift b/exercises/practice/sum-of-multiples/.meta/template.swift index 11a4feb6a..09aa4754c 100644 --- a/exercises/practice/sum-of-multiples/.meta/template.swift +++ b/exercises/practice/sum-of-multiples/.meta/template.swift @@ -1,16 +1,18 @@ -import XCTest +import Testing +import Foundation @testable import {{exercise|camelCase}} -class {{exercise|camelCase}}Tests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false + +@Suite struct {{exercise|camelCase}}Tests { {% for case in cases %} {% if forloop.first -%} - func test{{case.description |camelCase }}() { + @Test("{{case.description}}") {% else -%} - func test{{case.description |camelCase }}() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test + @Test("{{case.description}}", .enabled(if: RUNALL)) {% endif -%} - XCTAssertEqual(toLimit({{case.input.limit}}, inMultiples: {{case.input.factors}}), {{case.expected}}) + func test{{case.description |camelCase }}() { + #expect(toLimit({{case.input.limit}}, inMultiples: {{case.input.factors}}) == {{case.expected}}) } {% endfor -%} } diff --git a/exercises/practice/sum-of-multiples/Package.swift b/exercises/practice/sum-of-multiples/Package.swift index d94b6886a..ed4af850d 100644 --- a/exercises/practice/sum-of-multiples/Package.swift +++ b/exercises/practice/sum-of-multiples/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:6.0 import PackageDescription diff --git a/exercises/practice/sum-of-multiples/Tests/SumOfMultiplesTests/SumOfMultiplesTests.swift b/exercises/practice/sum-of-multiples/Tests/SumOfMultiplesTests/SumOfMultiplesTests.swift index 4119106d0..c5ed774b5 100644 --- a/exercises/practice/sum-of-multiples/Tests/SumOfMultiplesTests/SumOfMultiplesTests.swift +++ b/exercises/practice/sum-of-multiples/Tests/SumOfMultiplesTests/SumOfMultiplesTests.swift @@ -1,86 +1,91 @@ -import XCTest +import Foundation +import Testing @testable import SumOfMultiples -class SumOfMultiplesTests: XCTestCase { - let runAll = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +let RUNALL = Bool(ProcessInfo.processInfo.environment["RUNALL", default: "false"]) ?? false +@Suite struct SumOfMultiplesTests { + + @Test("no multiples within limit") func testNoMultiplesWithinLimit() { - XCTAssertEqual(toLimit(1, inMultiples: [3, 5]), 0) + #expect(toLimit(1, inMultiples: [3, 5]) == 0) } - func testOneFactorHasMultiplesWithinLimit() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(4, inMultiples: [3, 5]), 3) + @Test("one factor has multiples within limit", .enabled(if: RUNALL)) + func testOneFactorHasMultiplesWithinLimit() { + #expect(toLimit(4, inMultiples: [3, 5]) == 3) } - func testMoreThanOneMultipleWithinLimit() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(7, inMultiples: [3]), 9) + @Test("more than one multiple within limit", .enabled(if: RUNALL)) + func testMoreThanOneMultipleWithinLimit() { + #expect(toLimit(7, inMultiples: [3]) == 9) } - func testMoreThanOneFactorWithMultiplesWithinLimit() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(10, inMultiples: [3, 5]), 23) + @Test("more than one factor with multiples within limit", .enabled(if: RUNALL)) + func testMoreThanOneFactorWithMultiplesWithinLimit() { + #expect(toLimit(10, inMultiples: [3, 5]) == 23) } - func testEachMultipleIsOnlyCountedOnce() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(100, inMultiples: [3, 5]), 2318) + @Test("each multiple is only counted once", .enabled(if: RUNALL)) + func testEachMultipleIsOnlyCountedOnce() { + #expect(toLimit(100, inMultiples: [3, 5]) == 2318) } - func testAMuchLargerLimit() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(1000, inMultiples: [3, 5]), 233168) + @Test("a much larger limit", .enabled(if: RUNALL)) + func testAMuchLargerLimit() { + #expect(toLimit(1000, inMultiples: [3, 5]) == 233168) } - func testThreeFactors() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(20, inMultiples: [7, 13, 17]), 51) + @Test("three factors", .enabled(if: RUNALL)) + func testThreeFactors() { + #expect(toLimit(20, inMultiples: [7, 13, 17]) == 51) } - func testFactorsNotRelativelyPrime() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(15, inMultiples: [4, 6]), 30) + @Test("factors not relatively prime", .enabled(if: RUNALL)) + func testFactorsNotRelativelyPrime() { + #expect(toLimit(15, inMultiples: [4, 6]) == 30) } - func testSomePairsOfFactorsRelativelyPrimeAndSomeNot() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(150, inMultiples: [5, 6, 8]), 4419) + @Test("some pairs of factors relatively prime and some not", .enabled(if: RUNALL)) + func testSomePairsOfFactorsRelativelyPrimeAndSomeNot() { + #expect(toLimit(150, inMultiples: [5, 6, 8]) == 4419) } - func testOneFactorIsAMultipleOfAnother() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(51, inMultiples: [5, 25]), 275) + @Test("one factor is a multiple of another", .enabled(if: RUNALL)) + func testOneFactorIsAMultipleOfAnother() { + #expect(toLimit(51, inMultiples: [5, 25]) == 275) } - func testMuchLargerFactors() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(10000, inMultiples: [43, 47]), 2_203_160) + @Test("much larger factors", .enabled(if: RUNALL)) + func testMuchLargerFactors() { + #expect(toLimit(10000, inMultiples: [43, 47]) == 2_203_160) } - func testAllNumbersAreMultiplesOf1() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(100, inMultiples: [1]), 4950) + @Test("all numbers are multiples of 1", .enabled(if: RUNALL)) + func testAllNumbersAreMultiplesOf1() { + #expect(toLimit(100, inMultiples: [1]) == 4950) } - func testNoFactorsMeansAnEmptySum() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(10000, inMultiples: []), 0) + @Test("no factors means an empty sum", .enabled(if: RUNALL)) + func testNoFactorsMeansAnEmptySum() { + #expect(toLimit(10000, inMultiples: []) == 0) } - func testTheOnlyMultipleOf0Is0() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(1, inMultiples: [0]), 0) + @Test("the only multiple of 0 is 0", .enabled(if: RUNALL)) + func testTheOnlyMultipleOf0Is0() { + #expect(toLimit(1, inMultiples: [0]) == 0) } - func testTheFactor0DoesNotAffectTheSumOfMultiplesOfOtherFactors() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(4, inMultiples: [3, 0]), 3) + @Test("the factor 0 does not affect the sum of multiples of other factors", .enabled(if: RUNALL)) + func testTheFactor0DoesNotAffectTheSumOfMultiplesOfOtherFactors() { + #expect(toLimit(4, inMultiples: [3, 0]) == 3) } - func testSolutionsUsingIncludeExcludeMustExtendToCardinalityGreaterThan3() throws { - try XCTSkipIf(true && !runAll) // change true to false to run this test - XCTAssertEqual(toLimit(10000, inMultiples: [2, 3, 5, 7, 11]), 39_614_537) + @Test( + "solutions using include-exclude must extend to cardinality greater than 3", + .enabled(if: RUNALL)) + func testSolutionsUsingIncludeExcludeMustExtendToCardinalityGreaterThan3() { + #expect(toLimit(10000, inMultiples: [2, 3, 5, 7, 11]) == 39_614_537) } }