From 771edaf5faec6364da5908411db7245a00083ae5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:39:03 +0800 Subject: [PATCH 01/67] Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#2909) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.5.0 to 4.6.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/6f51ac03b9356f520e9adb1b1b7802705f340c2b...65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index c125970af..a2c38bb16 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -47,7 +47,7 @@ jobs: - name: Test all exercises using java-test-runner run: bin/test-with-test-runner - name: Archive test results - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 with: name: test-results path: exercises/**/build/results.json From 89fb0774c7a8d182ad62a6db06d76790afab34cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:39:35 +0800 Subject: [PATCH 02/67] Bump actions/setup-java from 4.6.0 to 4.7.0 (#2910) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.6.0 to 4.7.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/7a6d8a8234af8eb26422e24e3006232cccaa061b...3a4f6e1af504cf6a31855fa899c6aa5355ba6c12) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/java.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index a2c38bb16..d442a7481 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 - uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 with: java-version: 17 distribution: "temurin" @@ -31,7 +31,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 - uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b + uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 with: java-version: 17 distribution: "temurin" From 3b72eac00b9ee3b8abf2c14b53605045cd16714f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:40:02 +0800 Subject: [PATCH 03/67] Bump DavidAnson/markdownlint-cli2-action from 19.0.0 to 19.1.0 (#2911) Bumps [DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action) from 19.0.0 to 19.1.0. - [Release notes](https://github.com/davidanson/markdownlint-cli2-action/releases) - [Commits](https://github.com/davidanson/markdownlint-cli2-action/compare/a23dae216ce3fee4db69da41fed90d2a4af801cf...05f32210e84442804257b2a6f20b273450ec8265) --- updated-dependencies: - dependency-name: DavidAnson/markdownlint-cli2-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/markdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index 5ab372415..9bcd9dc76 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -19,4 +19,4 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Lint markdown - uses: DavidAnson/markdownlint-cli2-action@a23dae216ce3fee4db69da41fed90d2a4af801cf + uses: DavidAnson/markdownlint-cli2-action@05f32210e84442804257b2a6f20b273450ec8265 From 3ec59e814bafbf6700999b99e4fe1906253dba21 Mon Sep 17 00:00:00 2001 From: jagdish-15 Date: Sat, 8 Feb 2025 17:48:32 +0530 Subject: [PATCH 04/67] Add method based if approach for Bob (#2861) Co-authored-by: Kah Goh --- .../practice/bob/.approaches/config.json | 21 +++- .../practice/bob/.approaches/introduction.md | 86 +++++++++++----- .../method-based-if-statements/content.md | 99 +++++++++++++++++++ .../method-based-if-statements/snippet.txt | 8 ++ .../content.md | 2 +- .../snippet.txt | 0 6 files changed, 191 insertions(+), 25 deletions(-) create mode 100644 exercises/practice/bob/.approaches/method-based-if-statements/content.md create mode 100644 exercises/practice/bob/.approaches/method-based-if-statements/snippet.txt rename exercises/practice/bob/.approaches/{if-statements => variable-based-if-statements}/content.md (99%) rename exercises/practice/bob/.approaches/{if-statements => variable-based-if-statements}/snippet.txt (100%) diff --git a/exercises/practice/bob/.approaches/config.json b/exercises/practice/bob/.approaches/config.json index d7a8ba83b..14e2d8764 100644 --- a/exercises/practice/bob/.approaches/config.json +++ b/exercises/practice/bob/.approaches/config.json @@ -2,13 +2,30 @@ "introduction": { "authors": [ "bobahop" + ], + "contributors": [ + "jagdish-15", + "kahgoh" ] }, "approaches": [ + { + "uuid": "6ca5c7c0-f8f1-49b2-b137-951fa39f89eb", + "slug": "method-based-if-statements", + "title": "method-based if statements", + "blurb": "Use if statements to return the answer with the help of methods.", + "authors": [ + "jagdish-15" + ], + "contributors": [ + "BenjaminGale", + "kahgoh" + ] + }, { "uuid": "323eb230-7f27-4301-88ea-19c39d3eb5b6", - "slug": "if-statements", - "title": "if statements", + "slug": "variable-based-if-statements", + "title": "variable-based if statements", "blurb": "Use if statements to return the answer.", "authors": [ "bobahop" diff --git a/exercises/practice/bob/.approaches/introduction.md b/exercises/practice/bob/.approaches/introduction.md index f7b7389b2..96575876e 100644 --- a/exercises/practice/bob/.approaches/introduction.md +++ b/exercises/practice/bob/.approaches/introduction.md @@ -1,30 +1,65 @@ # Introduction -There are various idiomatic approaches to solve Bob. -A basic approach can use a series of `if` statements to test the conditions. -An array can contain answers from which the right response is selected by an index calculated from scores given to the conditions. +In this exercise, we’re working on a program to determine Bob’s responses based on the tone and style of given messages. +Bob responds differently depending on whether a message is a question, a shout, both, or silence. +Various approaches can be used to implement this logic efficiently and cleanly, ensuring the code remains readable and easy to maintain. ## General guidance -Regardless of the approach used, some things you could look out for include +When implementing your solution, consider the following tips to keep your code optimized and idiomatic: -- If the input is trimmed, [`trim()`][trim] only once. +- **Trim the Input Once**: Use [`trim()`][trim] only once at the start to remove any unnecessary whitespace. +- **Use Built-in Methods**: For checking if a message is a question, prefer [`endsWith("?")`][endswith] instead of manually checking the last character. +- **Single Determinations**: Use variables for `questioning` and `shouting` rather than calling these checks multiple times to improve efficiency. +- **DRY Code**: Avoid duplicating code by combining the logic for determining a shout and a question when handling shouted questions. Following the [DRY][dry] principle helps maintain clear and maintainable code. +- **Return Statements**: An early return in an `if` statement eliminates the need for additional `else` blocks, making the code more readable. +- **Curly Braces**: While optional for single-line statements, some teams may require them for readability and consistency. -- Use the [`endsWith()`][endswith] `String` method instead of checking the last character by index for `?`. +## Approach: method-based `if` statements -- Don't copy/paste the logic for determining a shout and for determining a question into determining a shouted question. - Combine the two determinations instead of copying them. - Not duplicating the code will keep the code [DRY][dry]. +```java +class Bob { + String hey(String input) { + var inputTrimmed = input.trim(); + + if (isSilent(inputTrimmed)) { + return "Fine. Be that way!"; + } + if (isShouting(inputTrimmed) && isQuestioning(inputTrimmed)) { + return "Calm down, I know what I'm doing!"; + } + if (isShouting(inputTrimmed)) { + return "Whoa, chill out!"; + } + if (isQuestioning(inputTrimmed)) { + return "Sure."; + } + + return "Whatever."; + } -- Perhaps consider making `questioning` and `shouting` values set once instead of functions that are possibly called twice. + private boolean isShouting(String input) { + return input.chars() + .anyMatch(Character::isLetter) && + input.chars() + .filter(Character::isLetter) + .allMatch(Character::isUpperCase); + } -- If an `if` statement can return, then an `else if` or `else` is not needed. - Execution will either return or will continue to the next statement anyway. + private boolean isQuestioning(String input) { + return input.endsWith("?"); + } -- If the body of an `if` statement is only one line, curly braces aren't needed. - Some teams may still require them in their style guidelines, though. + private boolean isSilent(String input) { + return input.length() == 0; + } +} +``` + +This approach defines helper methods for each type of message—silent, shouting, and questioning—to keep each condition clean and easily testable. +For more details, refer to the [method-based `if` Statements Approach][approach-method-if]. -## Approach: `if` statements +## Approach: variable-based `if` statements ```java import java.util.function.Predicate; @@ -56,7 +91,8 @@ class Bob { } ``` -For more information, check the [`if` statements approach][approach-if]. +This approach uses variables to avoid rechecking whether Bob is silent, shouting or questioning. +For more details, refer to the [variable-based `if` Statements Approach][approach-variable-if]. ## Approach: answer array @@ -86,16 +122,22 @@ class Bob { } ``` -For more information, check the [Answer array approach][approach-answer-array]. +This approach uses an array of answers and calculates the appropriate index based on flags for shouting and questioning. +For more details, refer to the [Answer Array Approach][approach-answer-array]. + +## Which Approach to Use? + +The choice between the **Method-Based `if` Statements Approach**, **Variable-Based `if` Statements Approach**, and the **Answer Array Approach** depends on readability, maintainability, and efficiency: -## Which approach to use? +- **Method-Based `if` Statements Approach**: This is clear and easy to follow but checks conditions multiple times, potentially affecting performance. Storing results in variables like `questioning` and `shouting` can improve efficiency but may reduce clarity slightly. +- **Variable-Based `if` Statements Approach**: This approach can be more efficient by avoiding redundant checks, but its nested structure can reduce readability and maintainability. +- **Answer Array Approach**: Efficient and compact, this method uses an array of responses based on flags for questioning and shouting. However, it may be less intuitive and harder to modify if more responses are needed. -Since benchmarking with the [Java Microbenchmark Harness][jmh] is currently outside the scope of this document, -the choice between `if` statements and answers array can be made by perceived readability. +Each approach offers a balance between readability and performance, with trade-offs in flexibility and clarity. [trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() [endswith]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#endsWith(java.lang.String) [dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself -[approach-if]: https://exercism.org/tracks/java/exercises/bob/approaches/if-statements +[approach-method-if]: https://exercism.org/tracks/java/exercises/bob/approaches/method-based-if-statements +[approach-variable-if]: https://exercism.org/tracks/java/exercises/bob/approaches/variable-based-if-statements [approach-answer-array]: https://exercism.org/tracks/java/exercises/bob/approaches/answer-array -[jmh]: https://github.com/openjdk/jmh diff --git a/exercises/practice/bob/.approaches/method-based-if-statements/content.md b/exercises/practice/bob/.approaches/method-based-if-statements/content.md new file mode 100644 index 000000000..93c95160b --- /dev/null +++ b/exercises/practice/bob/.approaches/method-based-if-statements/content.md @@ -0,0 +1,99 @@ +# Method-Based `if` statements + +```java +class Bob { + String hey(String input) { + var inputTrimmed = input.trim(); + + if (isSilent(inputTrimmed)) { + return "Fine. Be that way!"; + } + if (isShouting(inputTrimmed) && isQuestioning(inputTrimmed)) { + return "Calm down, I know what I'm doing!"; + } + if (isShouting(inputTrimmed)) { + return "Whoa, chill out!"; + } + if (isQuestioning(inputTrimmed)) { + return "Sure."; + } + + return "Whatever."; + } + + private boolean isShouting(String input) { + return input.chars() + .anyMatch(Character::isLetter) && + input.chars() + .filter(Character::isLetter) + .allMatch(Character::isUpperCase); + } + + private boolean isQuestioning(String input) { + return input.endsWith("?"); + } + + private boolean isSilent(String input) { + return input.length() == 0; + } +} +``` + +In this approach, the different conditions for Bob’s responses are separated into dedicated private methods within the `Bob` class. +This method-based approach improves readability and modularity by organizing each condition check into its own method, making the main response method easier to understand and maintain. + +## Explanation + +This approach simplifies the main method `hey` by breaking down each response condition into helper methods: + +### Trimming the Input + +The `input` is trimmed using the `String` [`trim()`][trim] method to remove any leading or trailing whitespace. +This helps to accurately detect if the input is empty and should prompt a `"Fine. Be that way!"` response. + +~~~~exercism/caution +Note that a `null` `string` would be different from a `String` of all whitespace. +A `null` `String` would throw a `NullPointerException` if `trim()` were applied to it. +~~~~ + +### Delegating to Helper Methods + +Each condition is evaluated using the following helper methods: + +1. **`isSilent`**: Checks if the trimmed input has no characters. +2. **`isShouting`**: Checks if the input is all uppercase and contains at least one alphabetic character, indicating shouting. +3. **`isQuestioning`**: Verifies if the trimmed input ends with a question mark. + +This modular approach keeps each condition encapsulated, enhancing code clarity. + +### Order of Checks + +The order of checks within `hey` is important: + +1. Silence is evaluated first, as it requires an immediate response. +2. Shouted questions take precedence over individual checks for shouting and questioning. +3. Shouting comes next, requiring its response if not combined with a question. +4. Questioning (a non-shouted question) is checked afterward. + +This ordering ensures that Bob’s response matches the expected behavior without redundancy. + +## Shortening + +When the body of an `if` statement is a single line, both the test expression and the body _could_ be put on the same line, like so: + +```java +if (isSilent(inputTrimmed)) return "Fine. Be that way!"; +``` + +or the body _could_ be put on a separate line without curly braces: + +```java +if (isSilent(inputTrimmed)) + return "Fine. Be that way!"; +``` + +However, the [Java Coding Conventions][coding-conventions] advise always using curly braces for `if` statements, which helps to avoid errors. +Your team may choose to overrule them at its own risk. + +[trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() +[coding-conventions]: https://www.oracle.com/java/technologies/javase/codeconventions-statements.html#449 diff --git a/exercises/practice/bob/.approaches/method-based-if-statements/snippet.txt b/exercises/practice/bob/.approaches/method-based-if-statements/snippet.txt new file mode 100644 index 000000000..4abcfa820 --- /dev/null +++ b/exercises/practice/bob/.approaches/method-based-if-statements/snippet.txt @@ -0,0 +1,8 @@ +if (isSilent(inputTrimmed)) + return "Fine. Be that way!"; +if (isShouting(inputTrimmed) && isQuestioning(inputTrimmed)) + return "Calm down, I know what I'm doing!"; +if (isShouting(inputTrimmed)) + return "Whoa, chill out!"; +if (isQuestioning(inputTrimmed)) + return "Sure."; \ No newline at end of file diff --git a/exercises/practice/bob/.approaches/if-statements/content.md b/exercises/practice/bob/.approaches/variable-based-if-statements/content.md similarity index 99% rename from exercises/practice/bob/.approaches/if-statements/content.md rename to exercises/practice/bob/.approaches/variable-based-if-statements/content.md index 4c3791fc1..d20648458 100644 --- a/exercises/practice/bob/.approaches/if-statements/content.md +++ b/exercises/practice/bob/.approaches/variable-based-if-statements/content.md @@ -1,4 +1,4 @@ -# `if` statements +# Variable-Based `if` statements ```java import java.util.function.Predicate; diff --git a/exercises/practice/bob/.approaches/if-statements/snippet.txt b/exercises/practice/bob/.approaches/variable-based-if-statements/snippet.txt similarity index 100% rename from exercises/practice/bob/.approaches/if-statements/snippet.txt rename to exercises/practice/bob/.approaches/variable-based-if-statements/snippet.txt From f5685d24ab09a33f3772d8c87602ab2d84ab3263 Mon Sep 17 00:00:00 2001 From: shreyasgosavi <58467077+shreyasgosavi@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:07:22 +0530 Subject: [PATCH 05/67] Update POLICIES.md (#2915) Fix link to sampleFile.java, which might have been pointing to an older url that no longer works. --- POLICIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/POLICIES.md b/POLICIES.md index fa0b5132b..14a857e2a 100644 --- a/POLICIES.md +++ b/POLICIES.md @@ -51,7 +51,7 @@ References: [[1](https://github.com/exercism/java/issues/177#issuecomment-261291 > throw new UnsupportedOperationException("Delete this statement and write your own implementation."); > ``` > -> - Exercises of difficulty 5 or higher: copy the StubTemplate.java file (provided [here](https://github.com/exercism/java/tree/main/_template/src/main/java)) and rename it to fit the exercise. For example, for the exercise linked-list the file could be named LinkedList.java. Then either (1) add hints to the hints.md file (which gets merged into the README.md for the exercise) or (2) provide stubs as above for exercises that demand complicated method signatures. +> - Exercises of difficulty 5 or higher: copy the StubTemplate.java file (provided [here](https://github.com/exercism/java/blob/main/resources/exercise-template/src/main/java/ExerciseName.java)) and rename it to fit the exercise. For example, for the exercise linked-list the file could be named LinkedList.java. Then either (1) add hints to the hints.md file (which gets merged into the README.md for the exercise) or (2) provide stubs as above for exercises that demand complicated method signatures. References: [[1](https://github.com/exercism/java/issues/178)], [[2](https://github.com/exercism/java/pull/683#discussion_r125506930)], [[3](https://github.com/exercism/java/issues/977)], [[4](https://github.com/exercism/java/issues/1721)] From 9ad48a72e4f6c2dae3a299184510914683f983a3 Mon Sep 17 00:00:00 2001 From: Kah Goh Date: Tue, 25 Feb 2025 07:27:44 +0800 Subject: [PATCH 06/67] Add maps concept (#2914) --------- Co-authored-by: Manuel Maxera <95315128+manumafe98@users.noreply.github.com> --- concepts/maps/.meta/config.json | 7 + concepts/maps/about.md | 171 ++++++++++++ concepts/maps/introduction.md | 70 +++++ concepts/maps/links.json | 10 + config.json | 39 ++- .../.docs/hints.md | 39 +++ .../.docs/instructions.md | 95 +++++++ .../.docs/introduction.md | 72 +++++ .../.docs/introduction.md.tpl | 3 + .../.meta/config.json | 17 ++ .../.meta/design.md | 38 +++ .../src/reference/java/DialingCodes.java | 43 +++ .../build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../international-calling-connoisseur/gradlew | 249 ++++++++++++++++++ .../gradlew.bat | 92 +++++++ .../src/main/java/DialingCodes.java | 34 +++ .../src/test/java/DialingCodesTest.java | 139 ++++++++++ exercises/settings.gradle | 1 + 20 files changed, 1144 insertions(+), 6 deletions(-) create mode 100644 concepts/maps/.meta/config.json create mode 100644 concepts/maps/about.md create mode 100644 concepts/maps/introduction.md create mode 100644 concepts/maps/links.json create mode 100644 exercises/concept/international-calling-connoisseur/.docs/hints.md create mode 100644 exercises/concept/international-calling-connoisseur/.docs/instructions.md create mode 100644 exercises/concept/international-calling-connoisseur/.docs/introduction.md create mode 100644 exercises/concept/international-calling-connoisseur/.docs/introduction.md.tpl create mode 100644 exercises/concept/international-calling-connoisseur/.meta/config.json create mode 100644 exercises/concept/international-calling-connoisseur/.meta/design.md create mode 100644 exercises/concept/international-calling-connoisseur/.meta/src/reference/java/DialingCodes.java create mode 100644 exercises/concept/international-calling-connoisseur/build.gradle create mode 100644 exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.properties create mode 100755 exercises/concept/international-calling-connoisseur/gradlew create mode 100644 exercises/concept/international-calling-connoisseur/gradlew.bat create mode 100644 exercises/concept/international-calling-connoisseur/src/main/java/DialingCodes.java create mode 100644 exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java diff --git a/concepts/maps/.meta/config.json b/concepts/maps/.meta/config.json new file mode 100644 index 000000000..98c459cdc --- /dev/null +++ b/concepts/maps/.meta/config.json @@ -0,0 +1,7 @@ +{ + "blurb": "Maps are a collection of key value pairs.", + "authors": [ + "kahgoh" + ], + "contributors": [] +} diff --git a/concepts/maps/about.md b/concepts/maps/about.md new file mode 100644 index 000000000..142db1139 --- /dev/null +++ b/concepts/maps/about.md @@ -0,0 +1,171 @@ +# About Maps + +A **Map** is a data structure for storing key value pairs. +It is similar to dictionaries in other programming languages. +The [Map][map-javadoc] interface defines the operations you can make with a map. + +## HashMap + +Java has a number of different Map implementations. +[HashMap][hashmap-javadoc] is a commonly used one. + +```java +// Make an instance +Map fruitPrices = new HashMap<>(); +``` + +~~~~exercism/note +When defining a `Map` variable, it is recommended to define the variable as a `Map` type rather than the specific type, as in the above example. +This practice makes it easy to change the `Map` implementation later. +~~~~ + +`HashMap` also has a copy constructor. + +```java +// Make a copy of a map +Map copy = new HashMap<>(fruitPrices); +``` + +Add entries to the map using [put][map-put-javadoc]. + +```java +fruitPrices.put("apple", 100); +fruitPrices.put("pear", 80); +// => { "apple" => 100, "pear" => 80 } +``` + +Only one value can be associated with each key. +Calling `put` with the same key will update the key's value. + +```java +fruitPrices.put("pear", 40); +// => { "apple" => 100, "pear" => 40 } +``` + +Use [get][map-get-javadoc] to get the value for a key. + +```java +fruitPrices.get("apple"); // => 100 +``` + +Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key. + +```java +fruitPrices.containsKey("apple"); // => true +fruitPrices.containsKey("orange"); // => false +``` + +Remove entries with [remove][map-remove-javadoc]. + +```java +fruitPrices.put("plum", 90); // Add plum to map +fruitPrices.remove("plum"); // Removes plum from map +``` + +The [size][map-size-javadoc] method returns the number of entries. + +```java +fruitPrices.size(); // Returns 2 +``` + +You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively. + +```java +fruitPrices.keys(); // Returns "apple" and "pear" in a set +fruitPrices.values(); // Returns 100 and 80, in a Collection +``` + +## HashMap uses `hashCode` and `equals` + +HashMaps uses the object's [hashCode][object-hashcode-javadoc] and [equals][object-equals-javadoc] method to work out where to store and how to retrieve the values for a key. +For this reason, it is important that their return values do not change between storing and getting them, otherwise the HashMap may not be able to find the value. + +For example, lets say we have the following class that will be used as the key to a map: + +```java +public class Stock { + private String name; + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (Objects.equals(Stock.class, obj.getClass()) && obj instanceof Stock other) { + return Objects.equals(name, other.name); + } + return false; + } +} +``` + +The `hashCode` and `equals` depend on the `name` field, which can be changed via `setName`. +Altering the `hashCode` can produce surprising results: + +```java +Stock stock = new Stock(); +stock.setName("Beanies"); + +Map stockCount = new HashMap<>(); +stockCount.put(stock, 80); + +stockCount.get(stock); // Returns 80 + +Stock other = new Stock(); +other.setName("Beanies"); + +stockCount.get(other); // Returns 80 because "other" and "stock" are equal + +stock.setName("Choccies"); +stockCount.get(stock); // Returns null because hashCode value has changed + +stockCount.get(other); // Also returns null because "other" and "stock" are not equal + +stock.setName("Beanies"); +stockCount.get(stock); // HashCode restored, so returns 80 again + +stockCount.get(other); // Also returns 80 again because "other" and "stock" are back to equal +``` + +## Map.of and Map.copyOf + +Another common way to create maps is to use [Map.of][map-of-javadoc] or [Map.ofEntries][map-ofentries-javadoc]. + +```java +// Using Map.of +Map temperatures = Map.of("Mon", 30, "Tue", 28, "Wed", 32); + +// or using Map.ofEntries +Map temperatures2 = Map.ofEntries(Map.entry("Mon", 30, "Tue", 28, "Wed", 32)); +``` + +Unlike `HashMap`, they populate the map upfront and become read-only once created. +[Map.copyOf][map-copyof-javadoc] makes a read-only copy of a map. + +```java +Map readOnlyFruitPrices = Map.copyOf(fruitPrices); +``` + +Calling methods like `put`, `remove` or `clear` results in an `UnsupportedOperationException`. + +[map-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html +[hashmap-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/HashMap.html +[map-put-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#put(K,V) +[map-get-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object) +[map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) +[map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) +[map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size() +[map-of-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#of() +[map-ofentries-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#ofEntries(java.util.Map.Entry...) +[map-copyof-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#copyOf(java.util.Map) +[object-hashcode-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#hashCode() +[object-equals-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object) diff --git a/concepts/maps/introduction.md b/concepts/maps/introduction.md new file mode 100644 index 000000000..aba6d4f4a --- /dev/null +++ b/concepts/maps/introduction.md @@ -0,0 +1,70 @@ +# Introduction + +A **Map** is a data structure for storing key value pairs. +It is similar to dictionaries in other programming languages. +The [Map][map-javadoc] interface defines operations on a map. + +Java has a number of different Map implementations. +[HashMap][hashmap-javadoc] is a commonly used one. + +```java +// Make an instance +Map fruitPrices = new HashMap<>(); +``` + +Add entries to the map using [put][map-put-javadoc]. + +```java +fruitPrices.put("apple", 100); +fruitPrices.put("pear", 80); +// => { "apple" => 100, "pear" => 80 } +``` + +Only one value can be associated with each key. +Calling `put` with the same key will update the key's value. + +```java +fruitPrices.put("pear", 40); +// => { "apple" => 100, "pear" => 40 } +``` + +Use [get][map-get-javadoc] to get the value for a key. + +```java +fruitPrices.get("apple"); // => 100 +``` + +Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key. + +```java +fruitPrices.containsKey("apple"); // => true +fruitPrices.containsKey("orange"); // => false +``` + +Remove entries with [remove][map-remove-javadoc]. + +```java +fruitPrices.put("plum", 90); // Add plum to map +fruitPrices.remove("plum"); // Removes plum from map +``` + +The [size][map-size-javadoc] method returns the number of entries. + +```java +fruitPrices.size(); // Returns 2 +``` + +You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively.studentScores + +```java +fruitPrices.keys(); // Returns "apple" and "pear" in a set +fruitPrices.values(); // Returns 100 and 80, in a Collection +``` + +[map-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html +[hashmap-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/HashMap.html +[map-put-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#put(K,V) +[map-get-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object) +[map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) +[map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) +[map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size() diff --git a/concepts/maps/links.json b/concepts/maps/links.json new file mode 100644 index 000000000..22258ff94 --- /dev/null +++ b/concepts/maps/links.json @@ -0,0 +1,10 @@ +[ + { + "url": "https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html", + "description": "Interface Map documentation" + }, + { + "url": "https://dev.java/learn/api/collections-framework/maps/", + "description": "Using Maps to Store Key Value Pairs" + } + ] diff --git a/config.json b/config.json index 86038a6b9..a4f248fc7 100644 --- a/config.json +++ b/config.json @@ -308,6 +308,19 @@ "generic-types" ], "status": "beta" + }, + { + "slug": "international-calling-connoisseur", + "name": "International Calling Connoisseur", + "uuid": "03506c5a-601a-42cd-b037-c310208de84d", + "concepts": [ + "maps" + ], + "prerequisites": [ + "classes", + "foreach-loops", + "generic-types" + ] } ], "practice": [ @@ -989,7 +1002,8 @@ "practices": [], "prerequisites": [ "if-else-statements", - "for-loops" + "for-loops", + "maps" ], "difficulty": 5 }, @@ -1092,7 +1106,8 @@ "practices": [], "prerequisites": [ "for-loops", - "arrays" + "arrays", + "maps" ], "difficulty": 5 }, @@ -1144,7 +1159,8 @@ "practices": [], "prerequisites": [ "chars", - "exceptions" + "exceptions", + "maps" ], "difficulty": 6 }, @@ -1205,6 +1221,7 @@ "practices": [], "prerequisites": [ "foreach-loops", + "maps", "strings" ], "difficulty": 6 @@ -1272,6 +1289,7 @@ "uuid": "38a405e8-619d-400f-b53c-2f06461fdf9d", "practices": [], "prerequisites": [ + "maps", "strings" ], "difficulty": 6 @@ -1447,7 +1465,8 @@ "uuid": "2e760ae2-fadd-4d31-9639-c4554e2826e9", "practices": [], "prerequisites": [ - "enums" + "enums", + "maps" ], "difficulty": 7 }, @@ -1495,7 +1514,8 @@ "chars", "if-else-statements", "lists", - "for-loops" + "for-loops", + "maps" ], "difficulty": 7 }, @@ -1556,7 +1576,8 @@ "prerequisites": [ "arrays", "strings", - "if-else-statements" + "if-else-statements", + "maps" ], "difficulty": 7 }, @@ -1703,6 +1724,7 @@ "exceptions", "for-loops", "if-else-statements", + "maps", "numbers" ], "difficulty": 8 @@ -1905,6 +1927,11 @@ "slug": "lists", "name": "Lists" }, + { + "uuid": "2f6fdedb-a0ac-4bab-92d6-3be61520b9bc", + "slug": "maps", + "name": "Maps" + }, { "uuid": "54118389-9c01-431b-a850-f47da498f845", "slug": "method-overloading", diff --git a/exercises/concept/international-calling-connoisseur/.docs/hints.md b/exercises/concept/international-calling-connoisseur/.docs/hints.md new file mode 100644 index 000000000..056edd53d --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.docs/hints.md @@ -0,0 +1,39 @@ +# Hints + +## General + +- The [`Map` API documentation][map-docs] contains a list of methods available on the `Map` interface. + +## 1. Return the codes in a map + +- You will need to define a `Map` in [such a way][declaring-members] that you can use it in the class methods. + +## 2. Add entries to the dictionary + +- Maps have a [method][map-put-docs] to add and update the value for a key. + +## 3. Lookup a dialing code's country + +- Maps have a [method][map-get-docs] to get the key's value. + +## 4. Don't allow duplicates + +- There is a way to check if the map has a [key][map-contains-key-docs] or a [value][map-contains-value-docs]. + +## 5. Find a country's dialing code + +- There is a [way][map-values-docs] to get an iterable collection of values in a map. + +## 6. Update the country's dialing code + +- Do not forget about the country's previous dialing code will be in the map. +- There is a [method][map-remove-docs] to remove an entry from the map. + +[declaring-members]: https://dev.java/learn/classes-objects/creating-classes/#declaring-members +[map-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html +[map-put-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#put(K,V) +[map-get-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object) +[map-contains-key-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) +[map-contains-value-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsValue(java.lang.Object) +[map-values-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#values() +[map-remove-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) diff --git a/exercises/concept/international-calling-connoisseur/.docs/instructions.md b/exercises/concept/international-calling-connoisseur/.docs/instructions.md new file mode 100644 index 000000000..dc3934e28 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.docs/instructions.md @@ -0,0 +1,95 @@ +# Instructions + +In this exercise you'll be writing code to manage a dictionary of international dialing codes using a `Map`. + +The dictionary allows looking up the name of a country (the map's value, as a `String`) by the international dialing code (the map's key, as an `Integer`), + +## 1. Return the codes in a map + +Implement the method `getCodes` that takes no parameters and returns a map of the dialing codes to country currently in the dictionary. + +```java +DialingCodes dialingCodes = new DialingCodes(); +dialingCodes.getCodes(); +// => empty map +``` + +## 2. Add entries to the dictionary + +The dictionary needs to be populated. +Implement the `setDialingCode` method that takes a dialing code and the corresponding country and adds the dialing code and country. +If the dialing code is already in the map, update the map with the provided code and country. + +```java +DialingCodes dialingCodes = new DialingCodes(); +dialingCodes.setDialingCode(679, "Unknown"); +// => { 679 => "Unknown" } + +dialingCodes.setDialingCode(679, "Fiji"); +// => { 679 => "Fiji" } +``` + +## 3. Lookup a dialing code's country + +Implement the `getCountry` method that takes a map of dialing codes and a dialing code and returns the country name with the dialing code. + +```java +DialingCodes dialingCodes = new DialingCodes(); +dialingCodes.setDialingCode(55, "Brazil"); +dialingCodes.getCountry(55); +// => "Brazil" +``` + +## 4. Don't allow duplicates + +When adding a dialing code, care needs to be taken to prevent a code or country to be added twice. +In situations where this happens, it can be assumed that the first entry is the right entry. +Implement the `addNewDialingCode` method that adds an entry for the given dialing code and country. +However, unlike `setDialingCode`, it does nothing if the dialing code or the country is already in the map. + +```java +DialingCodes dialingCodes = new DialingCodes(); +dialingCodes.addNewDialingCode(32, "Belgium"); +dialingCodes.addNewDialingCode(379, "Vatican City"); +// => { 39 => "Italy", 379 => "Vatican City" } + + +dialingCodes.addNewDialingCode(32, "Other"); +dialingCodes.addNewDialingCode(39, "Vatican City"); +// => { 32 => "Belgium", 379 => "Vatican City" } +``` + +## 5. Find a country's dialing code + +Its rare, but mistakes can be made. +To correct the mistake, we will need to know what dialing code the country is currently mapped to. +To find which dialing code needs to be corrected, implement the `findDialingCode` method that takes in a map of dialing codes an a country and returns the country's dialing code. +Return `null` if the country is _not_ in the map. + +```java +DialingCodes dialingCodes = new DialingCodes(); +dialingCodes.addDialingCode(43, "UK"); +dialingCodes.findDialingCode("UK"); +// => 44 + +dialingCodes.findDialingCode("Unlisted"); +// => null +``` + +## 6. Update the country's dialing code + +Now that we know which dialing code needs to be corrected, we proceed to update the code. +Implement the `updateCountryDialingCode` method that takes the country's new dialing code and the country's name and updates accordingly. +Do nothing if the country is _not_ in the map (as this method is meant to only help correct mistakes). + +```java +DialingCodes dialingCodes = new DialingCodes(); +dialingCodes.addDialingCode(88, "Japan"); +// => { 88 => "Japan" } + +dialingCodes.updateCountryDialingCode(81, "Japan"); +// => { 81 => "Japan" } + +dialingCodes.updateCountryDialingCode(32, "Mars"); +// => { 81 => "Japan"} +``` diff --git a/exercises/concept/international-calling-connoisseur/.docs/introduction.md b/exercises/concept/international-calling-connoisseur/.docs/introduction.md new file mode 100644 index 000000000..a9d963a1a --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.docs/introduction.md @@ -0,0 +1,72 @@ +# Introduction + +## Maps + +A **Map** is a data structure for storing key value pairs. +It is similar to dictionaries in other programming languages. +The [Map][map-javadoc] interface defines operations on a map. + +Java has a number of different Map implementations. +[HashMap][hashmap-javadoc] is a commonly used one. + +```java +// Make an instance +Map fruitPrices = new HashMap<>(); +``` + +Add entries to the map using [put][map-put-javadoc]. + +```java +fruitPrices.put("apple", 100); +fruitPrices.put("pear", 80); +// => { "apple" => 100, "pear" => 80 } +``` + +Only one value can be associated with each key. +Calling `put` with the same key will update the key's value. + +```java +fruitPrices.put("pear", 40); +// => { "apple" => 100, "pear" => 40 } +``` + +Use [get][map-get-javadoc] to get the value for a key. + +```java +fruitPrices.get("apple"); // => 100 +``` + +Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key. + +```java +fruitPrices.containsKey("apple"); // => true +fruitPrices.containsKey("orange"); // => false +``` + +Remove entries with [remove][map-remove-javadoc]. + +```java +fruitPrices.put("plum", 90); // Add plum to map +fruitPrices.remove("plum"); // Removes plum from map +``` + +The [size][map-size-javadoc] method returns the number of entries. + +```java +fruitPrices.size(); // Returns 2 +``` + +You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively.studentScores + +```java +fruitPrices.keys(); // Returns "apple" and "pear" in a set +fruitPrices.values(); // Returns 100 and 80, in a Collection +``` + +[map-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/HashMap.html +[hashmap-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/HashMap.html +[map-put-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#put(K,V) +[map-get-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object) +[map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) +[map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) +[map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size() diff --git a/exercises/concept/international-calling-connoisseur/.docs/introduction.md.tpl b/exercises/concept/international-calling-connoisseur/.docs/introduction.md.tpl new file mode 100644 index 000000000..f1ea541d1 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.docs/introduction.md.tpl @@ -0,0 +1,3 @@ +# Introduction + +%{concept:maps} diff --git a/exercises/concept/international-calling-connoisseur/.meta/config.json b/exercises/concept/international-calling-connoisseur/.meta/config.json new file mode 100644 index 000000000..45d387012 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "kahgoh" + ], + "files": { + "solution": [ + "src/main/java/DialingCodes.java" + ], + "test": [ + "src/test/java/DialingCodesTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/DialingCodes.java" + ] + }, + "blurb": "Learn about maps while managing international calling codes." +} diff --git a/exercises/concept/international-calling-connoisseur/.meta/design.md b/exercises/concept/international-calling-connoisseur/.meta/design.md new file mode 100644 index 000000000..a173c4900 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.meta/design.md @@ -0,0 +1,38 @@ +# Design + +## Learning objectives + +- Know about the `Map` interface. +- Know about `HashMap`. +- Know how to put, remove and retrieve items from a `Map`. +- Know how to find the keys of the map. + +## Out of scope + +- Map equality. +- The importance of the key object's `hashCode` and `equals` implementation and how `HashMap` uses them. +- More advanced or specific types of `Map`, such as `WeakHashMap` or `TreeMap`. +- Handling concurrency. + +## Concepts + +- `map` + +## Prerequisites + +This exercise's prerequisites Concepts are: + +- `classes` +- `foreach-loops` +- `generic-types` + +## Analyzer + +This exercise could benefit from the following rules in the [analyzer]: + +- `actionable`: If the user directly returns the class field, recommend returning a copy to the student. + +If the solution does not receive any of the above feedback, it must be exemplar. +Leave a `celebratory` comment to celebrate the success! + +[analyzer]: https://github.com/exercism/java-analyzer diff --git a/exercises/concept/international-calling-connoisseur/.meta/src/reference/java/DialingCodes.java b/exercises/concept/international-calling-connoisseur/.meta/src/reference/java/DialingCodes.java new file mode 100644 index 000000000..d3ecb0a36 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.meta/src/reference/java/DialingCodes.java @@ -0,0 +1,43 @@ +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class DialingCodes { + + private final Map countryByDialingCode = new HashMap<>(); + + public Map getCodes() { + return Map.copyOf(countryByDialingCode); + } + + public void setDialingCode(Integer code, String country) { + countryByDialingCode.put(code, country); + } + + public String getCountry(Integer code) { + return countryByDialingCode.get(code); + } + + public void addNewDialingCode(Integer code, String country) { + if (!countryByDialingCode.containsValue(country)) { + countryByDialingCode.putIfAbsent(code, country); + } + } + + public Integer findDialingCode(String country) { + for (Map.Entry entry : countryByDialingCode.entrySet()) { + if (Objects.equals(entry.getValue(), country)) { + return entry.getKey(); + } + } + return null; + } + + public void updateCountryDialingCode(Integer code, String country) { + Integer existingCode = findDialingCode(country); + if (existingCode != null) { + countryByDialingCode.remove(existingCode); + countryByDialingCode.put(code, country); + } + } +} diff --git a/exercises/concept/international-calling-connoisseur/build.gradle b/exercises/concept/international-calling-connoisseur/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/concept/international-calling-connoisseur/gradlew.bat b/exercises/concept/international-calling-connoisseur/gradlew.bat new file mode 100644 index 000000000..25da30dbd --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/concept/international-calling-connoisseur/src/main/java/DialingCodes.java b/exercises/concept/international-calling-connoisseur/src/main/java/DialingCodes.java new file mode 100644 index 000000000..0c40e1835 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/src/main/java/DialingCodes.java @@ -0,0 +1,34 @@ +import java.util.Map; + +public class DialingCodes { + + public Map getCodes() { + throw new UnsupportedOperationException( + "Delete this statement and write your own implementation."); + } + + public void setDialingCode(Integer code, String country) { + throw new UnsupportedOperationException( + "Delete this statement and write your own implementation."); + } + + public String getCountry(Integer code) { + throw new UnsupportedOperationException( + "Delete this statement and write your own implementation."); + } + + public void addNewDialingCode(Integer code, String country) { + throw new UnsupportedOperationException( + "Delete this statement and write your own implementation."); + } + + public Integer findDialingCode(String country) { + throw new UnsupportedOperationException( + "Delete this statement and write your own implementation."); + } + + public void updateCountryDialingCode(Integer code, String country) { + throw new UnsupportedOperationException( + "Delete this statement and write your own implementation."); + } +} diff --git a/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java new file mode 100644 index 000000000..4d9727a3a --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java @@ -0,0 +1,139 @@ +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +public class DialingCodesTest { + + @Test + @Tag("task:1") + @DisplayName("getCodes initially returns an empty map") + public void testGetCodesReturnsMap() { + DialingCodes dialingCodes = new DialingCodes(); + + assertThat(dialingCodes.getCodes()).isEmpty(); + } + + @Test + @Tag("task:2") + @DisplayName("setDialingCode adds new entry") + public void testSetDialingCodeAddsEntry() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.setDialingCode(679, "Fiji"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(679, "Fiji")); + } + + @Test + @Tag("task:2") + @DisplayName("setDialingCode updates existing entry") + public void testSetDialingCodeUpdatesEntry() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.setDialingCode(679, "Unknown"); + dialingCodes.setDialingCode(679, "Fiji"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(679, "Fiji")); + } + + @Test + @Tag("task:2") + @DisplayName("setDialingCode with multiple entries") + public void testSetDialingCodeWithMultipleEntries() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.setDialingCode(60, "Malaysia"); + dialingCodes.setDialingCode(233, "Retrieving..."); + dialingCodes.setDialingCode(56, "Chile"); + dialingCodes.setDialingCode(233, "Ghana"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(60, "Malaysia"), entry(233, "Ghana"), + entry(56, "Chile")); + } + + @Test + @Tag("task:3") + @DisplayName("getCountry returns a code's country") + public void testGetCountryForCode() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.setDialingCode(55, "Brazil"); + + assertThat(dialingCodes.getCountry(55)).isEqualTo("Brazil"); + } + + @Test + @Tag("task:3") + @DisplayName("getCountry returns updated country") + public void testGetCountryForUpdatedCode() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.setDialingCode(962, "Retrieving..."); + dialingCodes.setDialingCode(962, "Jordan"); + + assertThat(dialingCodes.getCountry(962)).isEqualTo("Jordan"); + } + + @Test + @Tag("task:4") + @DisplayName("addNewDialingCode adds new codes") + public void testAddNewDialingCodeAddsNewCodes() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(32, "Belgium"); + dialingCodes.addNewDialingCode(379, "Vatican City"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(32, "Belgium"), + entry(379, "Vatican City")); + } + + @Test + @Tag("task:4") + @DisplayName("addNewDialingCode leaves already added code") + public void testAddNewDialingCodeLeavesExistingCode() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(32, "Belgium"); + dialingCodes.addNewDialingCode(379, "Vatican City"); + dialingCodes.addNewDialingCode(32, "Other"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(32, "Belgium"), + entry(379, "Vatican City")); + } + + @Test + @Tag("task:4") + @DisplayName("addNewDialingCode leaves already added country") + public void testAddNewDialingCodeLeavesExistingCountry() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(61, "Australia"); + dialingCodes.addNewDialingCode(1000, "Australia"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(61, "Australia")); + } + + @Test + @Tag("task:5") + @DisplayName("findDialingCode returns a country's dialing code") + public void testFindDialingCode() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(43, "UK"); + + assertThat(dialingCodes.findDialingCode("UK")).isEqualTo(43); + } + + @Test + @Tag("task:5") + @DisplayName("findDialingCode returns null for country not yet added") + public void testFindDialingCodeWithUnlistedCountry() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(43, "UK"); + + assertThat(dialingCodes.findDialingCode("Unlisted")).isNull(); + } + + @Test + @Tag("task:6") + @DisplayName("updateDialingCode updates the map") + public void testUpdateDialingCode() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(88, "Japan"); + dialingCodes.updateCountryDialingCode(81, "Japan"); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index a6686a473..470f7aba7 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -11,6 +11,7 @@ include 'concept:cars-assemble' include 'concept:elons-toy-car' include 'concept:football-match-reports' include 'concept:gotta-snatch-em-all' +include 'concept:international-calling-connoisseur' include 'concept:karls-languages' include 'concept:lasagna' include 'concept:log-levels' From 884488df261a6709d66315962a40a248a9abfbc7 Mon Sep 17 00:00:00 2001 From: Benjamin Sproule Date: Wed, 26 Feb 2025 01:56:20 +0000 Subject: [PATCH 07/67] Add missing assertion from updateDialingCode test (#2916) --- .../src/test/java/DialingCodesTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java index 4d9727a3a..5d97a48bf 100644 --- a/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java +++ b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java @@ -135,5 +135,7 @@ public void testUpdateDialingCode() { DialingCodes dialingCodes = new DialingCodes(); dialingCodes.addNewDialingCode(88, "Japan"); dialingCodes.updateCountryDialingCode(81, "Japan"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(81, "Japan")); } } From d9a8119c2edbff134213fd4d1dda018842a66bc3 Mon Sep 17 00:00:00 2001 From: jagdish-15 Date: Sat, 1 Mar 2025 04:02:32 +0530 Subject: [PATCH 08/67] Fix links in Pythagorean Triplet approaches (#2919) --- .../practice/pythagorean-triplet/.approaches/config.json | 4 ++++ .../practice/pythagorean-triplet/.approaches/introduction.md | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/exercises/practice/pythagorean-triplet/.approaches/config.json b/exercises/practice/pythagorean-triplet/.approaches/config.json index d359d60a4..826288047 100644 --- a/exercises/practice/pythagorean-triplet/.approaches/config.json +++ b/exercises/practice/pythagorean-triplet/.approaches/config.json @@ -2,6 +2,10 @@ "introduction": { "authors": [ "bobahop" + ], + "contributors": [ + "BNAndras", + "jagdish-15" ] }, "approaches": [ diff --git a/exercises/practice/pythagorean-triplet/.approaches/introduction.md b/exercises/practice/pythagorean-triplet/.approaches/introduction.md index e3a5351a2..d67297e1a 100644 --- a/exercises/practice/pythagorean-triplet/.approaches/introduction.md +++ b/exercises/practice/pythagorean-triplet/.approaches/introduction.md @@ -171,6 +171,6 @@ So if the nested `for` loops approach is fast enough, it may be preferred for re [parallel]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#parallel-- [flatmap]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#flatMap-java.util.function.IntFunction- [filter]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#filter-java.util.function.IntPredicate- -[approach-for-loops]: https://exercism.org/tracks/java/exercises/pythagorean-triplets/approaches/for-loops -[approach-intstream-parallel-flatmap-filter]: https://exercism.org/tracks/java/exercises/pythagorean-triplets/approaches/if-intstream-parallel-flatmap-filter +[approach-for-loops]: https://exercism.org/tracks/java/exercises/pythagorean-triplet/approaches/for-loops +[approach-intstream-parallel-flatmap-filter]: https://exercism.org/tracks/java/exercises/pythagorean-triplet/approaches/intstream-parallel-flatmap-filter [jmh]: https://github.com/openjdk/jmh From 81aa061a320504556af7af71d272403b9c0c5618 Mon Sep 17 00:00:00 2001 From: jagdish-15 Date: Sat, 1 Mar 2025 19:07:41 +0530 Subject: [PATCH 09/67] Sync repo with problem-specifications (#2920) --- .../.meta/config.json | 3 + .../affine-cipher/.docs/instructions.md | 2 +- .../practice/anagram/.docs/instructions.md | 11 ++- .../practice/grains/.docs/instructions.md | 14 ++-- .../practice/grains/.docs/introduction.md | 6 ++ exercises/practice/grains/.meta/config.json | 2 +- exercises/practice/leap/.meta/config.json | 2 +- .../rna-transcription/.meta/config.json | 2 +- exercises/practice/say/.meta/config.json | 2 +- .../practice/sieve/.docs/instructions.md | 75 +++++++++++++++++-- .../simple-cipher/.docs/instructions.md | 10 +-- 11 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 exercises/practice/grains/.docs/introduction.md diff --git a/exercises/concept/international-calling-connoisseur/.meta/config.json b/exercises/concept/international-calling-connoisseur/.meta/config.json index 45d387012..e65e319f0 100644 --- a/exercises/concept/international-calling-connoisseur/.meta/config.json +++ b/exercises/concept/international-calling-connoisseur/.meta/config.json @@ -11,6 +11,9 @@ ], "exemplar": [ ".meta/src/reference/java/DialingCodes.java" + ], + "invalidator": [ + "build.gradle" ] }, "blurb": "Learn about maps while managing international calling codes." diff --git a/exercises/practice/affine-cipher/.docs/instructions.md b/exercises/practice/affine-cipher/.docs/instructions.md index f6329db93..1603dbbce 100644 --- a/exercises/practice/affine-cipher/.docs/instructions.md +++ b/exercises/practice/affine-cipher/.docs/instructions.md @@ -20,7 +20,7 @@ Where: - `i` is the letter's index from `0` to the length of the alphabet - 1. - `m` is the length of the alphabet. - For the Roman alphabet `m` is `26`. + For the Latin alphabet `m` is `26`. - `a` and `b` are integers which make up the encryption key. Values `a` and `m` must be _coprime_ (or, _relatively prime_) for automatic decryption to succeed, i.e., they have number `1` as their only common factor (more information can be found in the [Wikipedia article about coprime integers][coprime-integers]). diff --git a/exercises/practice/anagram/.docs/instructions.md b/exercises/practice/anagram/.docs/instructions.md index a7298485b..dca24f526 100644 --- a/exercises/practice/anagram/.docs/instructions.md +++ b/exercises/practice/anagram/.docs/instructions.md @@ -1,13 +1,12 @@ # Instructions -Your task is to, given a target word and a set of candidate words, to find the subset of the candidates that are anagrams of the target. +Given a target word and one or more candidate words, your task is to find the candidates that are anagrams of the target. An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`. A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`. -The target and candidates are words of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`). -Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `StoP` is not an anagram of `sTOp`. -The anagram set is the subset of the candidate set that are anagrams of the target (in any order). -Words in the anagram set should have the same letter case as in the candidate set. +The target word and candidate words are made up of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`). +Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `"StoP"` is not an anagram of `"sTOp"`. +The words you need to find should be taken from the candidate words, using the same letter case. -Given the target `"stone"` and candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`. +Given the target `"stone"` and the candidate words `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, and `"Seton"`, the anagram words you need to find are `"tones"`, `"notes"`, and `"Seton"`. diff --git a/exercises/practice/grains/.docs/instructions.md b/exercises/practice/grains/.docs/instructions.md index df479fc0a..f5b752a81 100644 --- a/exercises/practice/grains/.docs/instructions.md +++ b/exercises/practice/grains/.docs/instructions.md @@ -1,15 +1,11 @@ # Instructions -Calculate the number of grains of wheat on a chessboard given that the number on each square doubles. +Calculate the number of grains of wheat on a chessboard. -There once was a wise servant who saved the life of a prince. -The king promised to pay whatever the servant could dream up. -Knowing that the king loved chess, the servant told the king he would like to have grains of wheat. -One grain on the first square of a chess board, with the number of grains doubling on each successive square. +A chessboard has 64 squares. +Square 1 has one grain, square 2 has two grains, square 3 has four grains, and so on, doubling each time. -There are 64 squares on a chessboard (where square 1 has one grain, square 2 has two grains, and so on). +Write code that calculates: -Write code that shows: - -- how many grains were on a given square, and +- the number of grains on a given square - the total number of grains on the chessboard diff --git a/exercises/practice/grains/.docs/introduction.md b/exercises/practice/grains/.docs/introduction.md new file mode 100644 index 000000000..0df4f46f7 --- /dev/null +++ b/exercises/practice/grains/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +There once was a wise servant who saved the life of a prince. +The king promised to pay whatever the servant could dream up. +Knowing that the king loved chess, the servant told the king he would like to have grains of wheat. +One grain on the first square of a chessboard, with the number of grains doubling on each successive square. diff --git a/exercises/practice/grains/.meta/config.json b/exercises/practice/grains/.meta/config.json index 611ddb233..671ae4999 100644 --- a/exercises/practice/grains/.meta/config.json +++ b/exercises/practice/grains/.meta/config.json @@ -26,5 +26,5 @@ }, "blurb": "Calculate the number of grains of wheat on a chessboard given that the number on each square doubles.", "source": "The CodeRanch Cattle Drive, Assignment 6", - "source_url": "https://coderanch.com/wiki/718824/Grains" + "source_url": "https://web.archive.org/web/20240908084142/https://coderanch.com/wiki/718824/Grains" } diff --git a/exercises/practice/leap/.meta/config.json b/exercises/practice/leap/.meta/config.json index 234e5a6b5..25541b165 100644 --- a/exercises/practice/leap/.meta/config.json +++ b/exercises/practice/leap/.meta/config.json @@ -25,5 +25,5 @@ }, "blurb": "Determine whether a given year is a leap year.", "source": "CodeRanch Cattle Drive, Assignment 3", - "source_url": "https://coderanch.com/t/718816/Leap" + "source_url": "https://web.archive.org/web/20240907033714/https://coderanch.com/t/718816/Leap" } diff --git a/exercises/practice/rna-transcription/.meta/config.json b/exercises/practice/rna-transcription/.meta/config.json index 3b7e9fecf..f750ce6a6 100644 --- a/exercises/practice/rna-transcription/.meta/config.json +++ b/exercises/practice/rna-transcription/.meta/config.json @@ -38,7 +38,7 @@ "build.gradle" ] }, - "blurb": "Given a DNA strand, return its RNA Complement Transcription.", + "blurb": "Given a DNA strand, return its RNA complement.", "source": "Hyperphysics", "source_url": "https://web.archive.org/web/20220408112140/http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html" } diff --git a/exercises/practice/say/.meta/config.json b/exercises/practice/say/.meta/config.json index 4ffa776c6..8df32cdda 100644 --- a/exercises/practice/say/.meta/config.json +++ b/exercises/practice/say/.meta/config.json @@ -21,5 +21,5 @@ }, "blurb": "Given a number from 0 to 999,999,999,999, spell out that number in English.", "source": "A variation on the JavaRanch CattleDrive, Assignment 4", - "source_url": "https://coderanch.com/wiki/718804" + "source_url": "https://web.archive.org/web/20240907035912/https://coderanch.com/wiki/718804" } diff --git a/exercises/practice/sieve/.docs/instructions.md b/exercises/practice/sieve/.docs/instructions.md index 085c0a57d..71292e178 100644 --- a/exercises/practice/sieve/.docs/instructions.md +++ b/exercises/practice/sieve/.docs/instructions.md @@ -6,37 +6,96 @@ A prime number is a number larger than 1 that is only divisible by 1 and itself. For example, 2, 3, 5, 7, 11, and 13 are prime numbers. By contrast, 6 is _not_ a prime number as it not only divisible by 1 and itself, but also by 2 and 3. -To use the Sieve of Eratosthenes, you first create a list of all the numbers between 2 and your given number. -Then you repeat the following steps: +To use the Sieve of Eratosthenes, first, write out all the numbers from 2 up to and including your given number. +Then, follow these steps: -1. Find the next unmarked number in your list (skipping over marked numbers). +1. Find the next unmarked number (skipping over marked numbers). This is a prime number. 2. Mark all the multiples of that prime number as **not** prime. -You keep repeating these steps until you've gone through every number in your list. +Repeat the steps until you've gone through every number. At the end, all the unmarked numbers are prime. ~~~~exercism/note -The tests don't check that you've implemented the algorithm, only that you've come up with the correct list of primes. -To check you are implementing the Sieve correctly, a good first test is to check that you do not use division or remainder operations. +The Sieve of Eratosthenes marks off multiples of each prime using addition (repeatedly adding the prime) or multiplication (directly computing its multiples), rather than checking each number for divisibility. + +The tests don't check that you've implemented the algorithm, only that you've come up with the correct primes. ~~~~ ## Example Let's say you're finding the primes less than or equal to 10. -- List out 2, 3, 4, 5, 6, 7, 8, 9, 10, leaving them all unmarked. +- Write out 2, 3, 4, 5, 6, 7, 8, 9, 10, leaving them all unmarked. + + ```text + 2 3 4 5 6 7 8 9 10 + ``` + - 2 is unmarked and is therefore a prime. Mark 4, 6, 8 and 10 as "not prime". + + ```text + 2 3 [4] 5 [6] 7 [8] 9 [10] + ↑ + ``` + - 3 is unmarked and is therefore a prime. Mark 6 and 9 as not prime _(marking 6 is optional - as it's already been marked)_. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 4 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 5 is unmarked and is therefore a prime. Mark 10 as not prime _(optional - as it's already been marked)_. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 6 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 7 is unmarked and is therefore a prime. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 8 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 9 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + - 10 is marked as "not prime", so we stop as there are no more numbers to check. -You've examined all numbers and found 2, 3, 5, and 7 are still unmarked, which means they're the primes less than or equal to 10. + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +You've examined all the numbers and found that 2, 3, 5, and 7 are still unmarked, meaning they're the primes less than or equal to 10. diff --git a/exercises/practice/simple-cipher/.docs/instructions.md b/exercises/practice/simple-cipher/.docs/instructions.md index 475af6182..337857442 100644 --- a/exercises/practice/simple-cipher/.docs/instructions.md +++ b/exercises/practice/simple-cipher/.docs/instructions.md @@ -11,14 +11,14 @@ If anyone wishes to decipher these, and get at their meaning, he must substitute Ciphers are very straight-forward algorithms that allow us to render text less readable while still allowing easy deciphering. They are vulnerable to many forms of cryptanalysis, but Caesar was lucky that his enemies were not cryptanalysts. -The Caesar Cipher was used for some messages from Julius Caesar that were sent afield. +The Caesar cipher was used for some messages from Julius Caesar that were sent afield. Now Caesar knew that the cipher wasn't very good, but he had one ally in that respect: almost nobody could read well. So even being a couple letters off was sufficient so that people couldn't recognize the few words that they did know. -Your task is to create a simple shift cipher like the Caesar Cipher. -This image is a great example of the Caesar Cipher: +Your task is to create a simple shift cipher like the Caesar cipher. +This image is a great example of the Caesar cipher: -![Caesar Cipher][img-caesar-cipher] +![Caesar cipher][img-caesar-cipher] For example: @@ -44,7 +44,7 @@ would return the obscured "ldpdsdqgdehdu" In the example above, we've set a = 0 for the key value. So when the plaintext is added to the key, we end up with the same message coming out. So "aaaa" is not an ideal key. -But if we set the key to "dddd", we would get the same thing as the Caesar Cipher. +But if we set the key to "dddd", we would get the same thing as the Caesar cipher. ## Step 3 From 235a13e24a1a4427fbe404ea1b7243b7beac0dd0 Mon Sep 17 00:00:00 2001 From: Kadri Samuel Date: Sun, 2 Mar 2025 01:31:29 +0100 Subject: [PATCH 10/67] Fix about & introduction in International Calling Connoisseur (#2917) - Remove redundant "studentScores", which seems to be there by mistake. - Fix broken links to keySet and values methods. - Fix references to keySet (should be keySet, not keys). - Fix spacing for alignment in examples. --------- Co-authored-by: Kah Goh Co-authored-by: jagdish-15 --- concepts/maps/about.md | 12 +++++++----- concepts/maps/introduction.md | 12 +++++++----- .../.docs/introduction.md | 14 ++++++++------ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/concepts/maps/about.md b/concepts/maps/about.md index 142db1139..86a820cf1 100644 --- a/concepts/maps/about.md +++ b/concepts/maps/about.md @@ -51,15 +51,15 @@ fruitPrices.get("apple"); // => 100 Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key. ```java -fruitPrices.containsKey("apple"); // => true +fruitPrices.containsKey("apple"); // => true fruitPrices.containsKey("orange"); // => false ``` Remove entries with [remove][map-remove-javadoc]. ```java -fruitPrices.put("plum", 90); // Add plum to map -fruitPrices.remove("plum"); // Removes plum from map +fruitPrices.put("plum", 90); // Add plum to map +fruitPrices.remove("plum"); // Removes plum from map ``` The [size][map-size-javadoc] method returns the number of entries. @@ -68,10 +68,10 @@ The [size][map-size-javadoc] method returns the number of entries. fruitPrices.size(); // Returns 2 ``` -You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively. +You can use the [keySet][map-keyset-javadoc] or [values][map-values-javadoc] methods to obtain the keys or the values in a Map as a Set or collection respectively. ```java -fruitPrices.keys(); // Returns "apple" and "pear" in a set +fruitPrices.keySet(); // Returns "apple" and "pear" in a set fruitPrices.values(); // Returns 100 and 80, in a Collection ``` @@ -169,3 +169,5 @@ Calling methods like `put`, `remove` or `clear` results in an `UnsupportedOperat [map-copyof-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#copyOf(java.util.Map) [object-hashcode-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#hashCode() [object-equals-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html#equals(java.lang.Object) +[map-keyset-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#keySet() +[map-values-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#values() diff --git a/concepts/maps/introduction.md b/concepts/maps/introduction.md index aba6d4f4a..60bfbe7e3 100644 --- a/concepts/maps/introduction.md +++ b/concepts/maps/introduction.md @@ -37,15 +37,15 @@ fruitPrices.get("apple"); // => 100 Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key. ```java -fruitPrices.containsKey("apple"); // => true +fruitPrices.containsKey("apple"); // => true fruitPrices.containsKey("orange"); // => false ``` Remove entries with [remove][map-remove-javadoc]. ```java -fruitPrices.put("plum", 90); // Add plum to map -fruitPrices.remove("plum"); // Removes plum from map +fruitPrices.put("plum", 90); // Add plum to map +fruitPrices.remove("plum"); // Removes plum from map ``` The [size][map-size-javadoc] method returns the number of entries. @@ -54,10 +54,10 @@ The [size][map-size-javadoc] method returns the number of entries. fruitPrices.size(); // Returns 2 ``` -You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively.studentScores +You can use the [keySet][map-keyset-javadoc] or [values][map-values-javadoc] methods to obtain the keys or the values in a Map as a Set or collection respectively. ```java -fruitPrices.keys(); // Returns "apple" and "pear" in a set +fruitPrices.keySet(); // Returns "apple" and "pear" in a set fruitPrices.values(); // Returns 100 and 80, in a Collection ``` @@ -68,3 +68,5 @@ fruitPrices.values(); // Returns 100 and 80, in a Collection [map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) [map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) [map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size() +[map-keyset-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#keySet() +[map-values-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#values() diff --git a/exercises/concept/international-calling-connoisseur/.docs/introduction.md b/exercises/concept/international-calling-connoisseur/.docs/introduction.md index a9d963a1a..c407eea39 100644 --- a/exercises/concept/international-calling-connoisseur/.docs/introduction.md +++ b/exercises/concept/international-calling-connoisseur/.docs/introduction.md @@ -39,15 +39,15 @@ fruitPrices.get("apple"); // => 100 Use [containsKey][map-containskey-javadoc] to see if the map contains a particular key. ```java -fruitPrices.containsKey("apple"); // => true -fruitPrices.containsKey("orange"); // => false +fruitPrices.containsKey("apple"); // => true +fruitPrices.containsKey("orange"); // => false ``` Remove entries with [remove][map-remove-javadoc]. ```java -fruitPrices.put("plum", 90); // Add plum to map -fruitPrices.remove("plum"); // Removes plum from map +fruitPrices.put("plum", 90); // Add plum to map +fruitPrices.remove("plum"); // Removes plum from map ``` The [size][map-size-javadoc] method returns the number of entries. @@ -56,10 +56,10 @@ The [size][map-size-javadoc] method returns the number of entries. fruitPrices.size(); // Returns 2 ``` -You can use the [keys] or [values] methods to obtain the keys or the values in a Map as a Set or collection respectively.studentScores +You can use the [keySet][map-keyset-javadoc] or [values][map-values-javadoc] methods to obtain the keys or the values in a Map as a Set or collection respectively. ```java -fruitPrices.keys(); // Returns "apple" and "pear" in a set +fruitPrices.keySet(); // Returns "apple" and "pear" in a set fruitPrices.values(); // Returns 100 and 80, in a Collection ``` @@ -70,3 +70,5 @@ fruitPrices.values(); // Returns 100 and 80, in a Collection [map-containskey-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) [map-remove-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) [map-size-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#size() +[map-keyset-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#keySet() +[map-values-javadoc]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#values() From f193defcf392813d0d6e8573ea2275f0770efcb6 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Thu, 13 Mar 2025 02:07:15 +0530 Subject: [PATCH 11/67] Correct UK dialing code and fix typo in the International Calling Connoisseur instructions (#2925) [no important files changed] --- .../international-calling-connoisseur/.docs/instructions.md | 4 ++-- .../src/test/java/DialingCodesTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exercises/concept/international-calling-connoisseur/.docs/instructions.md b/exercises/concept/international-calling-connoisseur/.docs/instructions.md index dc3934e28..e8f728f0f 100644 --- a/exercises/concept/international-calling-connoisseur/.docs/instructions.md +++ b/exercises/concept/international-calling-connoisseur/.docs/instructions.md @@ -63,12 +63,12 @@ dialingCodes.addNewDialingCode(39, "Vatican City"); Its rare, but mistakes can be made. To correct the mistake, we will need to know what dialing code the country is currently mapped to. -To find which dialing code needs to be corrected, implement the `findDialingCode` method that takes in a map of dialing codes an a country and returns the country's dialing code. +To find which dialing code needs to be corrected, implement the `findDialingCode` method that takes in a map of dialing codes and a country and returns the country's dialing code. Return `null` if the country is _not_ in the map. ```java DialingCodes dialingCodes = new DialingCodes(); -dialingCodes.addDialingCode(43, "UK"); +dialingCodes.addDialingCode(44, "UK"); dialingCodes.findDialingCode("UK"); // => 44 diff --git a/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java index 5d97a48bf..e88e7379f 100644 --- a/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java +++ b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java @@ -113,9 +113,9 @@ public void testAddNewDialingCodeLeavesExistingCountry() { @DisplayName("findDialingCode returns a country's dialing code") public void testFindDialingCode() { DialingCodes dialingCodes = new DialingCodes(); - dialingCodes.addNewDialingCode(43, "UK"); + dialingCodes.addNewDialingCode(44, "UK"); - assertThat(dialingCodes.findDialingCode("UK")).isEqualTo(43); + assertThat(dialingCodes.findDialingCode("UK")).isEqualTo(44); } @Test @@ -123,7 +123,7 @@ public void testFindDialingCode() { @DisplayName("findDialingCode returns null for country not yet added") public void testFindDialingCodeWithUnlistedCountry() { DialingCodes dialingCodes = new DialingCodes(); - dialingCodes.addNewDialingCode(43, "UK"); + dialingCodes.addNewDialingCode(44, "UK"); assertThat(dialingCodes.findDialingCode("Unlisted")).isNull(); } From f2720d83c858cb96311c9f94a0f9a9876eed410f Mon Sep 17 00:00:00 2001 From: Christian Willner <34183939+vaeng@users.noreply.github.com> Date: Sun, 16 Mar 2025 01:37:39 +0100 Subject: [PATCH 12/67] Rename elon's toy car to jedlik's toy car (#2926) --------- Co-authored-by: Jeremy Walker --- config.json | 4 +-- .../gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 0 bytes .../.docs/hints.md | 0 .../.docs/instructions.md | 24 +++++++++--------- .../.docs/introduction.md | 0 .../.docs/introduction.md.tpl | 0 .../.meta/config.json | 8 +++--- .../.meta/design.md | 0 .../src/reference/java/JedliksToyCar.java} | 6 ++--- .../build.gradle | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 526 bytes .../gradle/wrapper/gradle-wrapper.properties | 0 .../gradlew | 0 .../gradlew.bat | 0 .../src/main/java/JedliksToyCar.java} | 12 ++++----- .../src/test/java/JedliksToyCarTest.java} | 24 +++++++++--------- exercises/settings.gradle | 2 +- 17 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.jar rename exercises/concept/{elons-toy-car => jedliks-toy-car}/.docs/hints.md (100%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/.docs/instructions.md (61%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/.docs/introduction.md (100%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/.docs/introduction.md.tpl (100%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/.meta/config.json (64%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/.meta/design.md (100%) rename exercises/concept/{elons-toy-car/.meta/src/reference/java/ElonsToyCar.java => jedliks-toy-car/.meta/src/reference/java/JedliksToyCar.java} (84%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/build.gradle (100%) create mode 100644 exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar rename exercises/concept/{elons-toy-car => jedliks-toy-car}/gradle/wrapper/gradle-wrapper.properties (100%) rename exercises/concept/{elons-toy-car => jedliks-toy-car}/gradlew (100%) mode change 100755 => 100644 rename exercises/concept/{elons-toy-car => jedliks-toy-car}/gradlew.bat (100%) rename exercises/concept/{elons-toy-car/src/main/java/ElonsToyCar.java => jedliks-toy-car/src/main/java/JedliksToyCar.java} (60%) rename exercises/concept/{elons-toy-car/src/test/java/ElonsToyCarTest.java => jedliks-toy-car/src/test/java/JedliksToyCarTest.java} (85%) diff --git a/config.json b/config.json index a4f248fc7..7c8f6b933 100644 --- a/config.json +++ b/config.json @@ -110,8 +110,8 @@ "status": "active" }, { - "slug": "elons-toy-car", - "name": "Elon's Toy Car", + "slug": "jedliks-toy-car", + "name": "Jedlik's Toy Car", "uuid": "2ae791e9-eb7a-4344-841d-0c4797e5106c", "concepts": [ "classes" diff --git a/exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e6441136f3d4ba8a0da8d277868979cfbc8ad796..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43453 zcma&N1CXTcmMvW9vTb(Rwr$&4wr$(C?dmSu>@vG-+vuvg^_??!{yS%8zW-#zn-LkA z5&1^$^{lnmUON?}LBF8_K|(?T0Ra(xUH{($5eN!MR#ZihR#HxkUPe+_R8Cn`RRs(P z_^*#_XlXmGv7!4;*Y%p4nw?{bNp@UZHv1?Um8r6)Fei3p@ClJn0ECfg1hkeuUU@Or zDaPa;U3fE=3L}DooL;8f;P0ipPt0Z~9P0)lbStMS)ag54=uL9ia-Lm3nh|@(Y?B`; zx_#arJIpXH!U{fbCbI^17}6Ri*H<>OLR%c|^mh8+)*h~K8Z!9)DPf zR2h?lbDZQ`p9P;&DQ4F0sur@TMa!Y}S8irn(%d-gi0*WxxCSk*A?3lGh=gcYN?FGl z7D=Js!i~0=u3rox^eO3i@$0=n{K1lPNU zwmfjRVmLOCRfe=seV&P*1Iq=^i`502keY8Uy-WNPwVNNtJFx?IwAyRPZo2Wo1+S(xF37LJZ~%i)kpFQ3Fw=mXfd@>%+)RpYQLnr}B~~zoof(JVm^^&f zxKV^+3D3$A1G;qh4gPVjhrC8e(VYUHv#dy^)(RoUFM?o%W-EHxufuWf(l*@-l+7vt z=l`qmR56K~F|v<^Pd*p~1_y^P0P^aPC##d8+HqX4IR1gu+7w#~TBFphJxF)T$2WEa zxa?H&6=Qe7d(#tha?_1uQys2KtHQ{)Qco)qwGjrdNL7thd^G5i8Os)CHqc>iOidS} z%nFEDdm=GXBw=yXe1W-ShHHFb?Cc70+$W~z_+}nAoHFYI1MV1wZegw*0y^tC*s%3h zhD3tN8b=Gv&rj}!SUM6|ajSPp*58KR7MPpI{oAJCtY~JECm)*m_x>AZEu>DFgUcby z1Qaw8lU4jZpQ_$;*7RME+gq1KySGG#Wql>aL~k9tLrSO()LWn*q&YxHEuzmwd1?aAtI zBJ>P=&$=l1efe1CDU;`Fd+_;&wI07?V0aAIgc(!{a z0Jg6Y=inXc3^n!U0Atk`iCFIQooHqcWhO(qrieUOW8X(x?(RD}iYDLMjSwffH2~tB z)oDgNBLB^AJBM1M^c5HdRx6fBfka`(LD-qrlh5jqH~);#nw|iyp)()xVYak3;Ybik z0j`(+69aK*B>)e_p%=wu8XC&9e{AO4c~O1U`5X9}?0mrd*m$_EUek{R?DNSh(=br# z#Q61gBzEpmy`$pA*6!87 zSDD+=@fTY7<4A?GLqpA?Pb2z$pbCc4B4zL{BeZ?F-8`s$?>*lXXtn*NC61>|*w7J* z$?!iB{6R-0=KFmyp1nnEmLsA-H0a6l+1uaH^g%c(p{iT&YFrbQ$&PRb8Up#X3@Zsk zD^^&LK~111%cqlP%!_gFNa^dTYT?rhkGl}5=fL{a`UViaXWI$k-UcHJwmaH1s=S$4 z%4)PdWJX;hh5UoK?6aWoyLxX&NhNRqKam7tcOkLh{%j3K^4Mgx1@i|Pi&}<^5>hs5 zm8?uOS>%)NzT(%PjVPGa?X%`N2TQCKbeH2l;cTnHiHppPSJ<7y-yEIiC!P*ikl&!B z%+?>VttCOQM@ShFguHVjxX^?mHX^hSaO_;pnyh^v9EumqSZTi+#f&_Vaija0Q-e*| z7ulQj6Fs*bbmsWp{`auM04gGwsYYdNNZcg|ph0OgD>7O}Asn7^Z=eI>`$2*v78;sj-}oMoEj&@)9+ycEOo92xSyY344^ z11Hb8^kdOvbf^GNAK++bYioknrpdN>+u8R?JxG=!2Kd9r=YWCOJYXYuM0cOq^FhEd zBg2puKy__7VT3-r*dG4c62Wgxi52EMCQ`bKgf*#*ou(D4-ZN$+mg&7$u!! z-^+Z%;-3IDwqZ|K=ah85OLwkO zKxNBh+4QHh)u9D?MFtpbl)us}9+V!D%w9jfAMYEb>%$A;u)rrI zuBudh;5PN}_6J_}l55P3l_)&RMlH{m!)ai-i$g)&*M`eN$XQMw{v^r@-125^RRCF0 z^2>|DxhQw(mtNEI2Kj(;KblC7x=JlK$@78`O~>V!`|1Lm-^JR$-5pUANAnb(5}B}JGjBsliK4& zk6y(;$e&h)lh2)L=bvZKbvh@>vLlreBdH8No2>$#%_Wp1U0N7Ank!6$dFSi#xzh|( zRi{Uw%-4W!{IXZ)fWx@XX6;&(m_F%c6~X8hx=BN1&q}*( zoaNjWabE{oUPb!Bt$eyd#$5j9rItB-h*5JiNi(v^e|XKAj*8(k<5-2$&ZBR5fF|JA z9&m4fbzNQnAU}r8ab>fFV%J0z5awe#UZ|bz?Ur)U9bCIKWEzi2%A+5CLqh?}K4JHi z4vtM;+uPsVz{Lfr;78W78gC;z*yTch~4YkLr&m-7%-xc ztw6Mh2d>_iO*$Rd8(-Cr1_V8EO1f*^@wRoSozS) zy1UoC@pruAaC8Z_7~_w4Q6n*&B0AjOmMWa;sIav&gu z|J5&|{=a@vR!~k-OjKEgPFCzcJ>#A1uL&7xTDn;{XBdeM}V=l3B8fE1--DHjSaxoSjNKEM9|U9#m2<3>n{Iuo`r3UZp;>GkT2YBNAh|b z^jTq-hJp(ebZh#Lk8hVBP%qXwv-@vbvoREX$TqRGTgEi$%_F9tZES@z8Bx}$#5eeG zk^UsLBH{bc2VBW)*EdS({yw=?qmevwi?BL6*=12k9zM5gJv1>y#ML4!)iiPzVaH9% zgSImetD@dam~e>{LvVh!phhzpW+iFvWpGT#CVE5TQ40n%F|p(sP5mXxna+Ev7PDwA zamaV4m*^~*xV+&p;W749xhb_X=$|LD;FHuB&JL5?*Y2-oIT(wYY2;73<^#46S~Gx| z^cez%V7x$81}UWqS13Gz80379Rj;6~WdiXWOSsdmzY39L;Hg3MH43o*y8ibNBBH`(av4|u;YPq%{R;IuYow<+GEsf@R?=@tT@!}?#>zIIn0CoyV!hq3mw zHj>OOjfJM3F{RG#6ujzo?y32m^tgSXf@v=J$ELdJ+=5j|=F-~hP$G&}tDZsZE?5rX ztGj`!S>)CFmdkccxM9eGIcGnS2AfK#gXwj%esuIBNJQP1WV~b~+D7PJTmWGTSDrR` zEAu4B8l>NPuhsk5a`rReSya2nfV1EK01+G!x8aBdTs3Io$u5!6n6KX%uv@DxAp3F@{4UYg4SWJtQ-W~0MDb|j-$lwVn znAm*Pl!?Ps&3wO=R115RWKb*JKoexo*)uhhHBncEDMSVa_PyA>k{Zm2(wMQ(5NM3# z)jkza|GoWEQo4^s*wE(gHz?Xsg4`}HUAcs42cM1-qq_=+=!Gk^y710j=66(cSWqUe zklbm8+zB_syQv5A2rj!Vbw8;|$@C!vfNmNV!yJIWDQ>{+2x zKjuFX`~~HKG~^6h5FntRpnnHt=D&rq0>IJ9#F0eM)Y-)GpRjiN7gkA8wvnG#K=q{q z9dBn8_~wm4J<3J_vl|9H{7q6u2A!cW{bp#r*-f{gOV^e=8S{nc1DxMHFwuM$;aVI^ zz6A*}m8N-&x8;aunp1w7_vtB*pa+OYBw=TMc6QK=mbA-|Cf* zvyh8D4LRJImooUaSb7t*fVfih<97Gf@VE0|z>NcBwBQze);Rh!k3K_sfunToZY;f2 z^HmC4KjHRVg+eKYj;PRN^|E0>Gj_zagfRbrki68I^#~6-HaHg3BUW%+clM1xQEdPYt_g<2K+z!$>*$9nQ>; zf9Bei{?zY^-e{q_*|W#2rJG`2fy@{%6u0i_VEWTq$*(ZN37|8lFFFt)nCG({r!q#9 z5VK_kkSJ3?zOH)OezMT{!YkCuSSn!K#-Rhl$uUM(bq*jY? zi1xbMVthJ`E>d>(f3)~fozjg^@eheMF6<)I`oeJYx4*+M&%c9VArn(OM-wp%M<-`x z7sLP1&3^%Nld9Dhm@$3f2}87!quhI@nwd@3~fZl_3LYW-B?Ia>ui`ELg z&Qfe!7m6ze=mZ`Ia9$z|ARSw|IdMpooY4YiPN8K z4B(ts3p%2i(Td=tgEHX z0UQ_>URBtG+-?0E;E7Ld^dyZ;jjw0}XZ(}-QzC6+NN=40oDb2^v!L1g9xRvE#@IBR zO!b-2N7wVfLV;mhEaXQ9XAU+>=XVA6f&T4Z-@AX!leJ8obP^P^wP0aICND?~w&NykJ#54x3_@r7IDMdRNy4Hh;h*!u(Ol(#0bJdwEo$5437-UBjQ+j=Ic>Q2z` zJNDf0yO6@mr6y1#n3)s(W|$iE_i8r@Gd@!DWDqZ7J&~gAm1#~maIGJ1sls^gxL9LLG_NhU!pTGty!TbhzQnu)I*S^54U6Yu%ZeCg`R>Q zhBv$n5j0v%O_j{QYWG!R9W?5_b&67KB$t}&e2LdMvd(PxN6Ir!H4>PNlerpBL>Zvyy!yw z-SOo8caEpDt(}|gKPBd$qND5#a5nju^O>V&;f890?yEOfkSG^HQVmEbM3Ugzu+UtH zC(INPDdraBN?P%kE;*Ae%Wto&sgw(crfZ#Qy(<4nk;S|hD3j{IQRI6Yq|f^basLY; z-HB&Je%Gg}Jt@={_C{L$!RM;$$|iD6vu#3w?v?*;&()uB|I-XqEKqZPS!reW9JkLewLb!70T7n`i!gNtb1%vN- zySZj{8-1>6E%H&=V}LM#xmt`J3XQoaD|@XygXjdZ1+P77-=;=eYpoEQ01B@L*a(uW zrZeZz?HJsw_4g0vhUgkg@VF8<-X$B8pOqCuWAl28uB|@r`19DTUQQsb^pfqB6QtiT z*`_UZ`fT}vtUY#%sq2{rchyfu*pCg;uec2$-$N_xgjZcoumE5vSI{+s@iLWoz^Mf; zuI8kDP{!XY6OP~q5}%1&L}CtfH^N<3o4L@J@zg1-mt{9L`s^z$Vgb|mr{@WiwAqKg zp#t-lhrU>F8o0s1q_9y`gQNf~Vb!F%70f}$>i7o4ho$`uciNf=xgJ>&!gSt0g;M>*x4-`U)ysFW&Vs^Vk6m%?iuWU+o&m(2Jm26Y(3%TL; zA7T)BP{WS!&xmxNw%J=$MPfn(9*^*TV;$JwRy8Zl*yUZi8jWYF>==j~&S|Xinsb%c z2?B+kpet*muEW7@AzjBA^wAJBY8i|#C{WtO_or&Nj2{=6JTTX05}|H>N2B|Wf!*3_ z7hW*j6p3TvpghEc6-wufFiY!%-GvOx*bZrhZu+7?iSrZL5q9}igiF^*R3%DE4aCHZ zqu>xS8LkW+Auv%z-<1Xs92u23R$nk@Pk}MU5!gT|c7vGlEA%G^2th&Q*zfg%-D^=f z&J_}jskj|Q;73NP4<4k*Y%pXPU2Thoqr+5uH1yEYM|VtBPW6lXaetokD0u z9qVek6Q&wk)tFbQ8(^HGf3Wp16gKmr>G;#G(HRBx?F`9AIRboK+;OfHaLJ(P>IP0w zyTbTkx_THEOs%Q&aPrxbZrJlio+hCC_HK<4%f3ZoSAyG7Dn`=X=&h@m*|UYO-4Hq0 z-Bq&+Ie!S##4A6OGoC~>ZW`Y5J)*ouaFl_e9GA*VSL!O_@xGiBw!AF}1{tB)z(w%c zS1Hmrb9OC8>0a_$BzeiN?rkPLc9%&;1CZW*4}CDDNr2gcl_3z+WC15&H1Zc2{o~i) z)LLW=WQ{?ricmC`G1GfJ0Yp4Dy~Ba;j6ZV4r{8xRs`13{dD!xXmr^Aga|C=iSmor% z8hi|pTXH)5Yf&v~exp3o+sY4B^^b*eYkkCYl*T{*=-0HniSA_1F53eCb{x~1k3*`W zr~};p1A`k{1DV9=UPnLDgz{aJH=-LQo<5%+Em!DNN252xwIf*wF_zS^!(XSm(9eoj z=*dXG&n0>)_)N5oc6v!>-bd(2ragD8O=M|wGW z!xJQS<)u70m&6OmrF0WSsr@I%T*c#Qo#Ha4d3COcX+9}hM5!7JIGF>7<~C(Ear^Sn zm^ZFkV6~Ula6+8S?oOROOA6$C&q&dp`>oR-2Ym3(HT@O7Sd5c~+kjrmM)YmgPH*tL zX+znN>`tv;5eOfX?h{AuX^LK~V#gPCu=)Tigtq9&?7Xh$qN|%A$?V*v=&-2F$zTUv z`C#WyIrChS5|Kgm_GeudCFf;)!WH7FI60j^0o#65o6`w*S7R@)88n$1nrgU(oU0M9 zx+EuMkC>(4j1;m6NoGqEkpJYJ?vc|B zOlwT3t&UgL!pX_P*6g36`ZXQ; z9~Cv}ANFnJGp(;ZhS(@FT;3e)0)Kp;h^x;$*xZn*k0U6-&FwI=uOGaODdrsp-!K$Ac32^c{+FhI-HkYd5v=`PGsg%6I`4d9Jy)uW0y%) zm&j^9WBAp*P8#kGJUhB!L?a%h$hJgQrx!6KCB_TRo%9{t0J7KW8!o1B!NC)VGLM5! zpZy5Jc{`r{1e(jd%jsG7k%I+m#CGS*BPA65ZVW~fLYw0dA-H_}O zrkGFL&P1PG9p2(%QiEWm6x;U-U&I#;Em$nx-_I^wtgw3xUPVVu zqSuKnx&dIT-XT+T10p;yjo1Y)z(x1fb8Dzfn8e yu?e%!_ptzGB|8GrCfu%p?(_ zQccdaaVK$5bz;*rnyK{_SQYM>;aES6Qs^lj9lEs6_J+%nIiuQC*fN;z8md>r_~Mfl zU%p5Dt_YT>gQqfr@`cR!$NWr~+`CZb%dn;WtzrAOI>P_JtsB76PYe*<%H(y>qx-`Kq!X_; z<{RpAqYhE=L1r*M)gNF3B8r(<%8mo*SR2hu zccLRZwGARt)Hlo1euqTyM>^!HK*!Q2P;4UYrysje@;(<|$&%vQekbn|0Ruu_Io(w4#%p6ld2Yp7tlA`Y$cciThP zKzNGIMPXX%&Ud0uQh!uQZz|FB`4KGD?3!ND?wQt6!n*f4EmCoJUh&b?;B{|lxs#F- z31~HQ`SF4x$&v00@(P+j1pAaj5!s`)b2RDBp*PB=2IB>oBF!*6vwr7Dp%zpAx*dPr zb@Zjq^XjN?O4QcZ*O+8>)|HlrR>oD*?WQl5ri3R#2?*W6iJ>>kH%KnnME&TT@ZzrHS$Q%LC?n|e>V+D+8D zYc4)QddFz7I8#}y#Wj6>4P%34dZH~OUDb?uP%-E zwjXM(?Sg~1!|wI(RVuxbu)-rH+O=igSho_pDCw(c6b=P zKk4ATlB?bj9+HHlh<_!&z0rx13K3ZrAR8W)!@Y}o`?a*JJsD+twZIv`W)@Y?Amu_u zz``@-e2X}27$i(2=9rvIu5uTUOVhzwu%mNazS|lZb&PT;XE2|B&W1>=B58#*!~D&) zfVmJGg8UdP*fx(>Cj^?yS^zH#o-$Q-*$SnK(ZVFkw+er=>N^7!)FtP3y~Xxnu^nzY zikgB>Nj0%;WOltWIob|}%lo?_C7<``a5hEkx&1ku$|)i>Rh6@3h*`slY=9U}(Ql_< zaNG*J8vb&@zpdhAvv`?{=zDedJ23TD&Zg__snRAH4eh~^oawdYi6A3w8<Ozh@Kw)#bdktM^GVb zrG08?0bG?|NG+w^&JvD*7LAbjED{_Zkc`3H!My>0u5Q}m!+6VokMLXxl`Mkd=g&Xx z-a>m*#G3SLlhbKB!)tnzfWOBV;u;ftU}S!NdD5+YtOjLg?X}dl>7m^gOpihrf1;PY zvll&>dIuUGs{Qnd- zwIR3oIrct8Va^Tm0t#(bJD7c$Z7DO9*7NnRZorrSm`b`cxz>OIC;jSE3DO8`hX955ui`s%||YQtt2 z5DNA&pG-V+4oI2s*x^>-$6J?p=I>C|9wZF8z;VjR??Icg?1w2v5Me+FgAeGGa8(3S z4vg*$>zC-WIVZtJ7}o9{D-7d>zCe|z#<9>CFve-OPAYsneTb^JH!Enaza#j}^mXy1 z+ULn^10+rWLF6j2>Ya@@Kq?26>AqK{A_| zQKb*~F1>sE*=d?A?W7N2j?L09_7n+HGi{VY;MoTGr_)G9)ot$p!-UY5zZ2Xtbm=t z@dpPSGwgH=QtIcEulQNI>S-#ifbnO5EWkI;$A|pxJd885oM+ zGZ0_0gDvG8q2xebj+fbCHYfAXuZStH2j~|d^sBAzo46(K8n59+T6rzBwK)^rfPT+B zyIFw)9YC-V^rhtK`!3jrhmW-sTmM+tPH+;nwjL#-SjQPUZ53L@A>y*rt(#M(qsiB2 zx6B)dI}6Wlsw%bJ8h|(lhkJVogQZA&n{?Vgs6gNSXzuZpEyu*xySy8ro07QZ7Vk1!3tJphN_5V7qOiyK8p z#@jcDD8nmtYi1^l8ml;AF<#IPK?!pqf9D4moYk>d99Im}Jtwj6c#+A;f)CQ*f-hZ< z=p_T86jog%!p)D&5g9taSwYi&eP z#JuEK%+NULWus;0w32-SYFku#i}d~+{Pkho&^{;RxzP&0!RCm3-9K6`>KZpnzS6?L z^H^V*s!8<>x8bomvD%rh>Zp3>Db%kyin;qtl+jAv8Oo~1g~mqGAC&Qi_wy|xEt2iz zWAJEfTV%cl2Cs<1L&DLRVVH05EDq`pH7Oh7sR`NNkL%wi}8n>IXcO40hp+J+sC!W?!krJf!GJNE8uj zg-y~Ns-<~D?yqbzVRB}G>0A^f0!^N7l=$m0OdZuqAOQqLc zX?AEGr1Ht+inZ-Qiwnl@Z0qukd__a!C*CKuGdy5#nD7VUBM^6OCpxCa2A(X;e0&V4 zM&WR8+wErQ7UIc6LY~Q9x%Sn*Tn>>P`^t&idaOEnOd(Ufw#>NoR^1QdhJ8s`h^|R_ zXX`c5*O~Xdvh%q;7L!_!ohf$NfEBmCde|#uVZvEo>OfEq%+Ns7&_f$OR9xsihRpBb z+cjk8LyDm@U{YN>+r46?nn{7Gh(;WhFw6GAxtcKD+YWV?uge>;+q#Xx4!GpRkVZYu zzsF}1)7$?%s9g9CH=Zs+B%M_)+~*j3L0&Q9u7!|+T`^O{xE6qvAP?XWv9_MrZKdo& z%IyU)$Q95AB4!#hT!_dA>4e@zjOBD*Y=XjtMm)V|+IXzjuM;(l+8aA5#Kaz_$rR6! zj>#&^DidYD$nUY(D$mH`9eb|dtV0b{S>H6FBfq>t5`;OxA4Nn{J(+XihF(stSche7$es&~N$epi&PDM_N`As;*9D^L==2Q7Z2zD+CiU(|+-kL*VG+&9!Yb3LgPy?A zm7Z&^qRG_JIxK7-FBzZI3Q<;{`DIxtc48k> zc|0dmX;Z=W$+)qE)~`yn6MdoJ4co;%!`ddy+FV538Y)j(vg}5*k(WK)KWZ3WaOG!8 z!syGn=s{H$odtpqFrT#JGM*utN7B((abXnpDM6w56nhw}OY}0TiTG1#f*VFZr+^-g zbP10`$LPq_;PvrA1XXlyx2uM^mrjTzX}w{yuLo-cOClE8MMk47T25G8M!9Z5ypOSV zAJUBGEg5L2fY)ZGJb^E34R2zJ?}Vf>{~gB!8=5Z) z9y$>5c)=;o0HeHHSuE4U)#vG&KF|I%-cF6f$~pdYJWk_dD}iOA>iA$O$+4%@>JU08 zS`ep)$XLPJ+n0_i@PkF#ri6T8?ZeAot$6JIYHm&P6EB=BiaNY|aA$W0I+nz*zkz_z zkEru!tj!QUffq%)8y0y`T&`fuus-1p>=^hnBiBqD^hXrPs`PY9tU3m0np~rISY09> z`P3s=-kt_cYcxWd{de@}TwSqg*xVhp;E9zCsnXo6z z?f&Sv^U7n4`xr=mXle94HzOdN!2kB~4=%)u&N!+2;z6UYKUDqi-s6AZ!haB;@&B`? z_TRX0%@suz^TRdCb?!vNJYPY8L_}&07uySH9%W^Tc&1pia6y1q#?*Drf}GjGbPjBS zbOPcUY#*$3sL2x4v_i*Y=N7E$mR}J%|GUI(>WEr+28+V z%v5{#e!UF*6~G&%;l*q*$V?&r$Pp^sE^i-0$+RH3ERUUdQ0>rAq2(2QAbG}$y{de( z>{qD~GGuOk559Y@%$?N^1ApVL_a704>8OD%8Y%8B;FCt%AoPu8*D1 zLB5X>b}Syz81pn;xnB}%0FnwazlWfUV)Z-~rZg6~b z6!9J$EcE&sEbzcy?CI~=boWA&eeIa%z(7SE^qgVLz??1Vbc1*aRvc%Mri)AJaAG!p z$X!_9Ds;Zz)f+;%s&dRcJt2==P{^j3bf0M=nJd&xwUGlUFn?H=2W(*2I2Gdu zv!gYCwM10aeus)`RIZSrCK=&oKaO_Ry~D1B5!y0R=%!i2*KfXGYX&gNv_u+n9wiR5 z*e$Zjju&ODRW3phN925%S(jL+bCHv6rZtc?!*`1TyYXT6%Ju=|X;6D@lq$8T zW{Y|e39ioPez(pBH%k)HzFITXHvnD6hw^lIoUMA;qAJ^CU?top1fo@s7xT13Fvn1H z6JWa-6+FJF#x>~+A;D~;VDs26>^oH0EI`IYT2iagy23?nyJ==i{g4%HrAf1-*v zK1)~@&(KkwR7TL}L(A@C_S0G;-GMDy=MJn2$FP5s<%wC)4jC5PXoxrQBFZ_k0P{{s@sz+gX`-!=T8rcB(=7vW}^K6oLWMmp(rwDh}b zwaGGd>yEy6fHv%jM$yJXo5oMAQ>c9j`**}F?MCry;T@47@r?&sKHgVe$MCqk#Z_3S z1GZI~nOEN*P~+UaFGnj{{Jo@16`(qVNtbU>O0Hf57-P>x8Jikp=`s8xWs^dAJ9lCQ z)GFm+=OV%AMVqVATtN@|vp61VVAHRn87}%PC^RAzJ%JngmZTasWBAWsoAqBU+8L8u z4A&Pe?fmTm0?mK-BL9t+{y7o(7jm+RpOhL9KnY#E&qu^}B6=K_dB}*VlSEiC9fn)+V=J;OnN)Ta5v66ic1rG+dGAJ1 z1%Zb_+!$=tQ~lxQrzv3x#CPb?CekEkA}0MYSgx$Jdd}q8+R=ma$|&1a#)TQ=l$1tQ z=tL9&_^vJ)Pk}EDO-va`UCT1m#Uty1{v^A3P~83_#v^ozH}6*9mIjIr;t3Uv%@VeW zGL6(CwCUp)Jq%G0bIG%?{_*Y#5IHf*5M@wPo6A{$Um++Co$wLC=J1aoG93&T7Ho}P z=mGEPP7GbvoG!uD$k(H3A$Z))+i{Hy?QHdk>3xSBXR0j!11O^mEe9RHmw!pvzv?Ua~2_l2Yh~_!s1qS`|0~0)YsbHSz8!mG)WiJE| z2f($6TQtt6L_f~ApQYQKSb=`053LgrQq7G@98#igV>y#i==-nEjQ!XNu9 z~;mE+gtj4IDDNQJ~JVk5Ux6&LCSFL!y=>79kE9=V}J7tD==Ga+IW zX)r7>VZ9dY=V&}DR))xUoV!u(Z|%3ciQi_2jl}3=$Agc(`RPb z8kEBpvY>1FGQ9W$n>Cq=DIpski};nE)`p3IUw1Oz0|wxll^)4dq3;CCY@RyJgFgc# zKouFh!`?Xuo{IMz^xi-h=StCis_M7yq$u) z?XHvw*HP0VgR+KR6wI)jEMX|ssqYvSf*_3W8zVTQzD?3>H!#>InzpSO)@SC8q*ii- z%%h}_#0{4JG;Jm`4zg};BPTGkYamx$Xo#O~lBirRY)q=5M45n{GCfV7h9qwyu1NxOMoP4)jjZMxmT|IQQh0U7C$EbnMN<3)Kk?fFHYq$d|ICu>KbY_hO zTZM+uKHe(cIZfEqyzyYSUBZa8;Fcut-GN!HSA9ius`ltNebF46ZX_BbZNU}}ZOm{M2&nANL9@0qvih15(|`S~z}m&h!u4x~(%MAO$jHRWNfuxWF#B)E&g3ghSQ9|> z(MFaLQj)NE0lowyjvg8z0#m6FIuKE9lDO~Glg}nSb7`~^&#(Lw{}GVOS>U)m8bF}x zVjbXljBm34Cs-yM6TVusr+3kYFjr28STT3g056y3cH5Tmge~ASxBj z%|yb>$eF;WgrcOZf569sDZOVwoo%8>XO>XQOX1OyN9I-SQgrm;U;+#3OI(zrWyow3 zk==|{lt2xrQ%FIXOTejR>;wv(Pb8u8}BUpx?yd(Abh6? zsoO3VYWkeLnF43&@*#MQ9-i-d0t*xN-UEyNKeyNMHw|A(k(_6QKO=nKMCxD(W(Yop zsRQ)QeL4X3Lxp^L%wzi2-WVSsf61dqliPUM7srDB?Wm6Lzn0&{*}|IsKQW;02(Y&| zaTKv|`U(pSzuvR6Rduu$wzK_W-Y-7>7s?G$)U}&uK;<>vU}^^ns@Z!p+9?St1s)dG zK%y6xkPyyS1$~&6v{kl?Md6gwM|>mt6Upm>oa8RLD^8T{0?HC!Z>;(Bob7el(DV6x zi`I)$&E&ngwFS@bi4^xFLAn`=fzTC;aimE^!cMI2n@Vo%Ae-ne`RF((&5y6xsjjAZ zVguVoQ?Z9uk$2ON;ersE%PU*xGO@T*;j1BO5#TuZKEf(mB7|g7pcEA=nYJ{s3vlbg zd4-DUlD{*6o%Gc^N!Nptgay>j6E5;3psI+C3Q!1ZIbeCubW%w4pq9)MSDyB{HLm|k zxv-{$$A*pS@csolri$Ge<4VZ}e~78JOL-EVyrbxKra^d{?|NnPp86!q>t<&IP07?Z z^>~IK^k#OEKgRH+LjllZXk7iA>2cfH6+(e&9ku5poo~6y{GC5>(bRK7hwjiurqAiZ zg*DmtgY}v83IjE&AbiWgMyFbaRUPZ{lYiz$U^&Zt2YjG<%m((&_JUbZcfJ22(>bi5 z!J?<7AySj0JZ&<-qXX;mcV!f~>G=sB0KnjWca4}vrtunD^1TrpfeS^4dvFr!65knK zZh`d;*VOkPs4*-9kL>$GP0`(M!j~B;#x?Ba~&s6CopvO86oM?-? zOw#dIRc;6A6T?B`Qp%^<U5 z19x(ywSH$_N+Io!6;e?`tWaM$`=Db!gzx|lQ${DG!zb1Zl&|{kX0y6xvO1o z220r<-oaS^^R2pEyY;=Qllqpmue|5yI~D|iI!IGt@iod{Opz@*ml^w2bNs)p`M(Io z|E;;m*Xpjd9l)4G#KaWfV(t8YUn@A;nK^#xgv=LtnArX|vWQVuw3}B${h+frU2>9^ z!l6)!Uo4`5k`<<;E(ido7M6lKTgWezNLq>U*=uz&s=cc$1%>VrAeOoUtA|T6gO4>UNqsdK=NF*8|~*sl&wI=x9-EGiq*aqV!(VVXA57 zw9*o6Ir8Lj1npUXvlevtn(_+^X5rzdR>#(}4YcB9O50q97%rW2me5_L=%ffYPUSRc z!vv?Kv>dH994Qi>U(a<0KF6NH5b16enCp+mw^Hb3Xs1^tThFpz!3QuN#}KBbww`(h z7GO)1olDqy6?T$()R7y%NYx*B0k_2IBiZ14&8|JPFxeMF{vSTxF-Vi3+ZOI=Thq2} zyQgjYY1_7^ZQHh{?P))4+qUiQJLi1&{yE>h?~jU%tjdV0h|FENbM3X(KnJdPKc?~k zh=^Ixv*+smUll!DTWH!jrV*wSh*(mx0o6}1@JExzF(#9FXgmTXVoU+>kDe68N)dkQ zH#_98Zv$}lQwjKL@yBd;U(UD0UCl322=pav<=6g>03{O_3oKTq;9bLFX1ia*lw;#K zOiYDcBJf)82->83N_Y(J7Kr_3lE)hAu;)Q(nUVydv+l+nQ$?|%MWTy`t>{havFSQloHwiIkGK9YZ79^9?AZo0ZyQlVR#}lF%dn5n%xYksXf8gnBm=wO7g_^! zauQ-bH1Dc@3ItZ-9D_*pH}p!IG7j8A_o94#~>$LR|TFq zZ-b00*nuw|-5C2lJDCw&8p5N~Z1J&TrcyErds&!l3$eSz%`(*izc;-?HAFD9AHb-| z>)id`QCrzRws^9(#&=pIx9OEf2rmlob8sK&xPCWS+nD~qzU|qG6KwA{zbikcfQrdH z+ zQg>O<`K4L8rN7`GJB0*3<3`z({lWe#K!4AZLsI{%z#ja^OpfjU{!{)x0ZH~RB0W5X zTwN^w=|nA!4PEU2=LR05x~}|B&ZP?#pNgDMwD*ajI6oJqv!L81gu=KpqH22avXf0w zX3HjbCI!n9>l046)5rr5&v5ja!xkKK42zmqHzPx$9Nn_MZk`gLeSLgC=LFf;H1O#B zn=8|^1iRrujHfbgA+8i<9jaXc;CQBAmQvMGQPhFec2H1knCK2x!T`e6soyrqCamX% zTQ4dX_E*8so)E*TB$*io{$c6X)~{aWfaqdTh=xEeGvOAN9H&-t5tEE-qso<+C!2>+ zskX51H-H}#X{A75wqFe-J{?o8Bx|>fTBtl&tcbdR|132Ztqu5X0i-pisB-z8n71%q%>EF}yy5?z=Ve`}hVh{Drv1YWL zW=%ug_&chF11gDv3D6B)Tz5g54H0mDHNjuKZ+)CKFk4Z|$RD zfRuKLW`1B>B?*RUfVd0+u8h3r-{@fZ{k)c!93t1b0+Q9vOaRnEn1*IL>5Z4E4dZ!7 ztp4GP-^1d>8~LMeb}bW!(aAnB1tM_*la=Xx)q(I0Y@__Zd$!KYb8T2VBRw%e$iSdZ zkwdMwd}eV9q*;YvrBFTv1>1+}{H!JK2M*C|TNe$ZSA>UHKk);wz$(F$rXVc|sI^lD zV^?_J!3cLM;GJuBMbftbaRUs$;F}HDEDtIeHQ)^EJJ1F9FKJTGH<(Jj`phE6OuvE) zqK^K`;3S{Y#1M@8yRQwH`?kHMq4tHX#rJ>5lY3DM#o@or4&^_xtBC(|JpGTfrbGkA z2Tu+AyT^pHannww!4^!$5?@5v`LYy~T`qs7SYt$JgrY(w%C+IWA;ZkwEF)u5sDvOK zGk;G>Mh&elvXDcV69J_h02l&O;!{$({fng9Rlc3ID#tmB^FIG^w{HLUpF+iB`|
NnX)EH+Nua)3Y(c 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 "Driven 0 meters" ``` ## 3. Display the battery percentage -Implement the `ElonsToyCar.batteryDisplay()` method to return the battery percentage as displayed on the LED display: +Implement the `JedliksToyCar.batteryDisplay()` method to return the battery percentage as displayed on the LED display: ```java -ElonsToyCar car = ElonsToyCar.buy(); +JedliksToyCar car = JedliksToyCar.buy(); car.batteryDisplay(); // => "Battery at 100%" ``` ## 4. Update the number of meters driven when driving -Implement the `ElonsToyCar.drive()` method that updates the number of meters driven: +Implement the `JedliksToyCar.drive()` method that updates the number of meters driven: ```java -ElonsToyCar car = ElonsToyCar.buy(); +JedliksToyCar car = JedliksToyCar.buy(); car.drive(); car.drive(); car.distanceDisplay(); @@ -55,10 +55,10 @@ car.distanceDisplay(); ## 5. Update the battery percentage when driving -Update the `ElonsToyCar.drive()` method to update the battery percentage: +Update the `JedliksToyCar.drive()` method to update the battery percentage: ```java -ElonsToyCar car = ElonsToyCar.buy(); +JedliksToyCar car = JedliksToyCar.buy(); car.drive(); car.drive(); car.batteryDisplay(); @@ -67,10 +67,10 @@ car.batteryDisplay(); ## 6. Prevent driving when the battery is drained -Update the `ElonsToyCar.drive()` method to not increase the distance driven nor decrease the battery percentage when the battery is drained (at 0%): +Update the `JedliksToyCar.drive()` method to not increase the distance driven nor decrease the battery percentage when the battery is drained (at 0%): ```java -ElonsToyCar car = ElonsToyCar.buy(); +JedliksToyCar car = JedliksToyCar.buy(); // Drain the battery // ... diff --git a/exercises/concept/elons-toy-car/.docs/introduction.md b/exercises/concept/jedliks-toy-car/.docs/introduction.md similarity index 100% rename from exercises/concept/elons-toy-car/.docs/introduction.md rename to exercises/concept/jedliks-toy-car/.docs/introduction.md diff --git a/exercises/concept/elons-toy-car/.docs/introduction.md.tpl b/exercises/concept/jedliks-toy-car/.docs/introduction.md.tpl similarity index 100% rename from exercises/concept/elons-toy-car/.docs/introduction.md.tpl rename to exercises/concept/jedliks-toy-car/.docs/introduction.md.tpl diff --git a/exercises/concept/elons-toy-car/.meta/config.json b/exercises/concept/jedliks-toy-car/.meta/config.json similarity index 64% rename from exercises/concept/elons-toy-car/.meta/config.json rename to exercises/concept/jedliks-toy-car/.meta/config.json index e55a4b908..300ca8c16 100644 --- a/exercises/concept/elons-toy-car/.meta/config.json +++ b/exercises/concept/jedliks-toy-car/.meta/config.json @@ -7,18 +7,18 @@ ], "files": { "solution": [ - "src/main/java/ElonsToyCar.java" + "src/main/java/JedliksToyCar.java" ], "test": [ - "src/test/java/ElonsToyCarTest.java" + "src/test/java/JedliksToyCarTest.java" ], "exemplar": [ - ".meta/src/reference/java/ElonsToyCar.java" + ".meta/src/reference/java/JedliksToyCar.java" ], "invalidator": [ "build.gradle" ] }, - "icon": "elons-toys", + "icon": "jedliks-toys", "blurb": "Learn about classes by working on a remote controlled car." } diff --git a/exercises/concept/elons-toy-car/.meta/design.md b/exercises/concept/jedliks-toy-car/.meta/design.md similarity index 100% rename from exercises/concept/elons-toy-car/.meta/design.md rename to exercises/concept/jedliks-toy-car/.meta/design.md diff --git a/exercises/concept/elons-toy-car/.meta/src/reference/java/ElonsToyCar.java b/exercises/concept/jedliks-toy-car/.meta/src/reference/java/JedliksToyCar.java similarity index 84% rename from exercises/concept/elons-toy-car/.meta/src/reference/java/ElonsToyCar.java rename to exercises/concept/jedliks-toy-car/.meta/src/reference/java/JedliksToyCar.java index 683e4bb32..96ef6955c 100644 --- a/exercises/concept/elons-toy-car/.meta/src/reference/java/ElonsToyCar.java +++ b/exercises/concept/jedliks-toy-car/.meta/src/reference/java/JedliksToyCar.java @@ -1,4 +1,4 @@ -class ElonsToyCar { +class JedliksToyCar { private int batteryPercentage = 100; private int distanceDrivenInMeters = 0; @@ -21,7 +21,7 @@ public String batteryDisplay() { return "Battery at " + batteryPercentage + "%"; } - public static ElonsToyCar buy() { - return new ElonsToyCar(); + public static JedliksToyCar buy() { + return new JedliksToyCar(); } } diff --git a/exercises/concept/elons-toy-car/build.gradle b/exercises/concept/jedliks-toy-car/build.gradle similarity index 100% rename from exercises/concept/elons-toy-car/build.gradle rename to exercises/concept/jedliks-toy-car/build.gradle diff --git a/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..71858cda34775e40119cf99663074619ad53ce87 GIT binary patch literal 526 zcmWIWW@Zs#;Nak3U|>)LVgwLi;AHT14RO@<^mEhq@pN|e3w8|+VP#+hDS5wtZ&Y+K zkTeskJHH-CXQZVANl7T?sk-yqe+1*6GRqEERIIzBD1Y) zVu0LeP$)rA6@+58Q)Gm&j`jlSQXpmzfC$7_=B5EzE{IUgbOG{H)Y&Y6R1QRuRv?51 klA%Cs76v5zL=UaHH!DjA$See6*N3~OLPH72138!*09O)6<^TWy literal 0 HcmV?d00001 diff --git a/exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.properties rename to exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.properties diff --git a/exercises/concept/elons-toy-car/gradlew b/exercises/concept/jedliks-toy-car/gradlew old mode 100755 new mode 100644 similarity index 100% rename from exercises/concept/elons-toy-car/gradlew rename to exercises/concept/jedliks-toy-car/gradlew diff --git a/exercises/concept/elons-toy-car/gradlew.bat b/exercises/concept/jedliks-toy-car/gradlew.bat similarity index 100% rename from exercises/concept/elons-toy-car/gradlew.bat rename to exercises/concept/jedliks-toy-car/gradlew.bat diff --git a/exercises/concept/elons-toy-car/src/main/java/ElonsToyCar.java b/exercises/concept/jedliks-toy-car/src/main/java/JedliksToyCar.java similarity index 60% rename from exercises/concept/elons-toy-car/src/main/java/ElonsToyCar.java rename to exercises/concept/jedliks-toy-car/src/main/java/JedliksToyCar.java index f3efa7259..d9906a7b8 100644 --- a/exercises/concept/elons-toy-car/src/main/java/ElonsToyCar.java +++ b/exercises/concept/jedliks-toy-car/src/main/java/JedliksToyCar.java @@ -1,17 +1,17 @@ -public class ElonsToyCar { - public static ElonsToyCar buy() { - throw new UnsupportedOperationException("Please implement the (static) ElonsToyCar.buy() method"); +public class JedliksToyCar { + public static JedliksToyCar buy() { + throw new UnsupportedOperationException("Please implement the (static) JedliksToyCar.buy() method"); } public String distanceDisplay() { - throw new UnsupportedOperationException("Please implement the ElonsToyCar.distanceDisplay() method"); + throw new UnsupportedOperationException("Please implement the JedliksToyCar.distanceDisplay() method"); } public String batteryDisplay() { - throw new UnsupportedOperationException("Please implement the ElonsToyCar.batteryDisplay() method"); + throw new UnsupportedOperationException("Please implement the JedliksToyCar.batteryDisplay() method"); } public void drive() { - throw new UnsupportedOperationException("Please implement the ElonsToyCar.drive() method"); + throw new UnsupportedOperationException("Please implement the JedliksToyCar.drive() method"); } } diff --git a/exercises/concept/elons-toy-car/src/test/java/ElonsToyCarTest.java b/exercises/concept/jedliks-toy-car/src/test/java/JedliksToyCarTest.java similarity index 85% rename from exercises/concept/elons-toy-car/src/test/java/ElonsToyCarTest.java rename to exercises/concept/jedliks-toy-car/src/test/java/JedliksToyCarTest.java index f97c2a798..19db64391 100644 --- a/exercises/concept/elons-toy-car/src/test/java/ElonsToyCarTest.java +++ b/exercises/concept/jedliks-toy-car/src/test/java/JedliksToyCarTest.java @@ -4,12 +4,12 @@ import static org.assertj.core.api.Assertions.assertThat; -public class ElonsToyCarTest { +public class JedliksToyCarTest { @Test @Tag("task:1") @DisplayName("The static buy method returns a new remote controlled car instance") public void buy_new_car_returns_instance() { - ElonsToyCar car = ElonsToyCar.buy(); + JedliksToyCar car = JedliksToyCar.buy(); assertThat(car).isNotNull(); } @@ -17,8 +17,8 @@ public void buy_new_car_returns_instance() { @Tag("task:1") @DisplayName("The static buy method returns each time a new remote controlled car instance") public void buy_new_car_returns_new_car_each_time() { - ElonsToyCar car1 = ElonsToyCar.buy(); - ElonsToyCar car2 = ElonsToyCar.buy(); + JedliksToyCar car1 = JedliksToyCar.buy(); + JedliksToyCar car2 = JedliksToyCar.buy(); assertThat(car1).isNotEqualTo(car2); } @@ -26,7 +26,7 @@ public void buy_new_car_returns_new_car_each_time() { @Tag("task:2") @DisplayName("The distanceDisplay method shows 0 meters message on a new car") public void new_car_distance_display() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); assertThat(car.distanceDisplay()).isEqualTo("Driven 0 meters"); } @@ -34,7 +34,7 @@ public void new_car_distance_display() { @Tag("task:3") @DisplayName("The batteryDisplay method shows full battery message on a new car") public void new_car_battery_display() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); assertThat(car.batteryDisplay()).isEqualTo("Battery at 100%"); } @@ -42,7 +42,7 @@ public void new_car_battery_display() { @Tag("task:4") @DisplayName("The distanceDisplay method shows the correct message after driving once") public void distance_display_after_driving_once() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); car.drive(); assertThat(car.distanceDisplay()).isEqualTo("Driven 20 meters"); } @@ -51,7 +51,7 @@ public void distance_display_after_driving_once() { @Tag("task:4") @DisplayName("The distanceDisplay method shows the correct message after driving multiple times") public void distance_display_after_driving_multiple_times() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); for (int i = 0; i < 17; i++) { car.drive(); @@ -64,7 +64,7 @@ public void distance_display_after_driving_multiple_times() { @Tag("task:5") @DisplayName("The batteryDisplay method shows the correct message after driving once") public void battery_display_after_driving_once() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); car.drive(); assertThat(car.batteryDisplay()).isEqualTo("Battery at 99%"); @@ -74,7 +74,7 @@ public void battery_display_after_driving_once() { @Tag("task:5") @DisplayName("The batteryDisplay method shows the correct battery percentage after driving multiple times") public void battery_display_after_driving_multiple_times() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); for (int i = 0; i < 23; i++) { car.drive(); @@ -87,7 +87,7 @@ public void battery_display_after_driving_multiple_times() { @Tag("task:5") @DisplayName("The batteryDisplay method shows battery empty after draining all battery") public void battery_display_when_battery_empty() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); // Drain the battery for (int i = 0; i < 100; i++) { @@ -104,7 +104,7 @@ public void battery_display_when_battery_empty() { @Tag("task:6") @DisplayName("The distanceDisplay method shows the correct message after driving and draining all battery") public void distance_display_when_battery_empty() { - ElonsToyCar car = new ElonsToyCar(); + JedliksToyCar car = new JedliksToyCar(); // Drain the battery for (int i = 0; i < 100; i++) { diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 470f7aba7..2ff1e9a14 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -8,7 +8,7 @@ include 'concept:booking-up-for-beauty' include 'concept:calculator-conundrum' include 'concept:captains-log' include 'concept:cars-assemble' -include 'concept:elons-toy-car' +include 'concept:jedliks-toy-car' include 'concept:football-match-reports' include 'concept:gotta-snatch-em-all' include 'concept:international-calling-connoisseur' From 6c4a0b332f2e62b7fb5377c94463f5fbef06f86d Mon Sep 17 00:00:00 2001 From: Kah Goh Date: Tue, 18 Mar 2025 21:41:05 +0800 Subject: [PATCH 13/67] Add junit-platform-launcher to test runtime (#2905) This is fix the following warning about using deprecated Gradle features: The automatic loading of test framework implementation dependencies has been deprecated. This is scheduled to be removed in Gradle 9.0. Declare the desired test framework directly on the test suite or explicitly declare the test framework implementation dependencies on the test's runtime classpath. Consult the upgrading guide for further information: https://docs.gradle.org/8.7/userguide/upgrading_version_8.html#test_framework_implementation_dependencies --- exercises/concept/annalyns-infiltration/build.gradle | 2 ++ exercises/concept/bird-watcher/build.gradle | 2 ++ exercises/concept/blackjack/build.gradle | 2 ++ exercises/concept/booking-up-for-beauty/build.gradle | 2 ++ exercises/concept/calculator-conundrum/build.gradle | 2 ++ exercises/concept/captains-log/build.gradle | 2 ++ exercises/concept/cars-assemble/build.gradle | 2 ++ exercises/concept/football-match-reports/build.gradle | 2 ++ exercises/concept/gotta-snatch-em-all/build.gradle | 2 ++ exercises/concept/jedliks-toy-car/build.gradle | 2 ++ exercises/concept/karls-languages/build.gradle | 2 ++ exercises/concept/lasagna/build.gradle | 2 ++ exercises/concept/log-levels/build.gradle | 2 ++ exercises/concept/logs-logs-logs/build.gradle | 2 ++ exercises/concept/need-for-speed/build.gradle | 2 ++ exercises/concept/remote-control-competition/build.gradle | 2 ++ exercises/concept/salary-calculator/build.gradle | 2 ++ exercises/concept/secrets/build.gradle | 2 ++ exercises/concept/squeaky-clean/build.gradle | 2 ++ exercises/concept/tim-from-marketing/build.gradle | 2 ++ exercises/concept/wizards-and-warriors-2/build.gradle | 2 ++ exercises/concept/wizards-and-warriors/build.gradle | 2 ++ exercises/practice/acronym/build.gradle | 2 ++ exercises/practice/affine-cipher/build.gradle | 2 ++ exercises/practice/all-your-base/build.gradle | 2 ++ exercises/practice/allergies/build.gradle | 2 ++ exercises/practice/alphametics/build.gradle | 2 ++ exercises/practice/anagram/build.gradle | 2 ++ exercises/practice/armstrong-numbers/build.gradle | 2 ++ exercises/practice/atbash-cipher/build.gradle | 2 ++ exercises/practice/bank-account/build.gradle | 2 ++ exercises/practice/binary-search-tree/build.gradle | 2 ++ exercises/practice/binary-search/build.gradle | 2 ++ exercises/practice/bob/build.gradle | 2 ++ exercises/practice/book-store/build.gradle | 2 ++ exercises/practice/bottle-song/build.gradle | 2 ++ exercises/practice/bowling/build.gradle | 2 ++ exercises/practice/change/build.gradle | 2 ++ exercises/practice/circular-buffer/build.gradle | 2 ++ exercises/practice/clock/build.gradle | 2 ++ exercises/practice/collatz-conjecture/build.gradle | 2 ++ exercises/practice/complex-numbers/build.gradle | 2 ++ exercises/practice/connect/build.gradle | 2 ++ exercises/practice/crypto-square/build.gradle | 2 ++ exercises/practice/custom-set/build.gradle | 2 ++ exercises/practice/darts/build.gradle | 2 ++ exercises/practice/diamond/build.gradle | 2 ++ exercises/practice/difference-of-squares/build.gradle | 2 ++ exercises/practice/dnd-character/build.gradle | 2 ++ exercises/practice/dominoes/build.gradle | 2 ++ exercises/practice/dot-dsl/build.gradle | 2 ++ exercises/practice/eliuds-eggs/build.gradle | 2 ++ exercises/practice/error-handling/build.gradle | 2 ++ exercises/practice/etl/build.gradle | 2 ++ exercises/practice/flatten-array/build.gradle | 2 ++ exercises/practice/food-chain/build.gradle | 2 ++ exercises/practice/forth/build.gradle | 2 ++ exercises/practice/game-of-life/build.gradle | 2 ++ exercises/practice/gigasecond/build.gradle | 2 ++ exercises/practice/go-counting/build.gradle | 2 ++ exercises/practice/grade-school/build.gradle | 2 ++ exercises/practice/grains/build.gradle | 2 ++ exercises/practice/grep/build.gradle | 2 ++ exercises/practice/hamming/build.gradle | 2 ++ exercises/practice/hello-world/build.gradle | 2 ++ exercises/practice/high-scores/build.gradle | 2 ++ exercises/practice/house/build.gradle | 2 ++ exercises/practice/isbn-verifier/build.gradle | 2 ++ exercises/practice/isogram/build.gradle | 2 ++ exercises/practice/killer-sudoku-helper/build.gradle | 2 ++ exercises/practice/kindergarten-garden/build.gradle | 2 ++ exercises/practice/knapsack/build.gradle | 2 ++ exercises/practice/largest-series-product/build.gradle | 2 ++ exercises/practice/leap/build.gradle | 2 ++ exercises/practice/ledger/build.gradle | 2 ++ exercises/practice/linked-list/build.gradle | 2 ++ exercises/practice/list-ops/build.gradle | 2 ++ exercises/practice/luhn/build.gradle | 2 ++ exercises/practice/markdown/build.gradle | 2 ++ exercises/practice/matching-brackets/build.gradle | 2 ++ exercises/practice/matrix/build.gradle | 2 ++ exercises/practice/mazy-mice/build.gradle | 2 ++ exercises/practice/meetup/build.gradle | 2 ++ exercises/practice/micro-blog/build.gradle | 2 ++ exercises/practice/minesweeper/build.gradle | 2 ++ exercises/practice/nth-prime/build.gradle | 2 ++ exercises/practice/nucleotide-count/build.gradle | 2 ++ exercises/practice/ocr-numbers/build.gradle | 2 ++ exercises/practice/palindrome-products/build.gradle | 2 ++ exercises/practice/pangram/build.gradle | 2 ++ exercises/practice/parallel-letter-frequency/build.gradle | 2 ++ exercises/practice/pascals-triangle/build.gradle | 2 ++ exercises/practice/perfect-numbers/build.gradle | 2 ++ exercises/practice/phone-number/build.gradle | 2 ++ exercises/practice/pig-latin/build.gradle | 2 ++ exercises/practice/poker/build.gradle | 2 ++ exercises/practice/pov/build.gradle | 2 ++ exercises/practice/prime-factors/build.gradle | 2 ++ exercises/practice/protein-translation/build.gradle | 2 ++ exercises/practice/proverb/build.gradle | 2 ++ exercises/practice/pythagorean-triplet/build.gradle | 2 ++ exercises/practice/queen-attack/build.gradle | 2 ++ exercises/practice/rail-fence-cipher/build.gradle | 2 ++ exercises/practice/raindrops/build.gradle | 2 ++ exercises/practice/rational-numbers/build.gradle | 2 ++ exercises/practice/react/build.gradle | 2 ++ exercises/practice/rectangles/build.gradle | 2 ++ exercises/practice/resistor-color-duo/build.gradle | 2 ++ exercises/practice/resistor-color-trio/build.gradle | 2 ++ exercises/practice/resistor-color/build.gradle | 2 ++ exercises/practice/reverse-string/build.gradle | 2 ++ exercises/practice/rna-transcription/build.gradle | 2 ++ exercises/practice/robot-name/build.gradle | 2 ++ exercises/practice/robot-simulator/build.gradle | 2 ++ exercises/practice/roman-numerals/build.gradle | 2 ++ exercises/practice/rotational-cipher/build.gradle | 2 ++ exercises/practice/run-length-encoding/build.gradle | 2 ++ exercises/practice/saddle-points/build.gradle | 2 ++ exercises/practice/satellite/build.gradle | 2 ++ exercises/practice/say/build.gradle | 2 ++ exercises/practice/scrabble-score/build.gradle | 2 ++ exercises/practice/secret-handshake/build.gradle | 2 ++ exercises/practice/series/build.gradle | 2 ++ exercises/practice/sgf-parsing/build.gradle | 2 ++ exercises/practice/sieve/build.gradle | 2 ++ exercises/practice/simple-cipher/build.gradle | 2 ++ exercises/practice/simple-linked-list/build.gradle | 2 ++ exercises/practice/space-age/build.gradle | 2 ++ exercises/practice/spiral-matrix/build.gradle | 2 ++ exercises/practice/square-root/build.gradle | 2 ++ exercises/practice/state-of-tic-tac-toe/build.gradle | 2 ++ exercises/practice/sublist/build.gradle | 2 ++ exercises/practice/sum-of-multiples/build.gradle | 2 ++ exercises/practice/tournament/build.gradle | 2 ++ exercises/practice/transpose/build.gradle | 2 ++ exercises/practice/tree-building/build.gradle | 2 ++ exercises/practice/triangle/build.gradle | 2 ++ exercises/practice/twelve-days/build.gradle | 2 ++ exercises/practice/two-bucket/build.gradle | 2 ++ exercises/practice/two-fer/build.gradle | 2 ++ exercises/practice/variable-length-quantity/build.gradle | 2 ++ exercises/practice/word-count/build.gradle | 2 ++ exercises/practice/word-search/build.gradle | 2 ++ exercises/practice/wordy/build.gradle | 2 ++ exercises/practice/yacht/build.gradle | 2 ++ exercises/practice/zebra-puzzle/build.gradle | 2 ++ exercises/practice/zipper/build.gradle | 2 ++ resources/exercise-template/build.gradle | 2 ++ 148 files changed, 296 insertions(+) diff --git a/exercises/concept/annalyns-infiltration/build.gradle b/exercises/concept/annalyns-infiltration/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/annalyns-infiltration/build.gradle +++ b/exercises/concept/annalyns-infiltration/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/bird-watcher/build.gradle b/exercises/concept/bird-watcher/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/bird-watcher/build.gradle +++ b/exercises/concept/bird-watcher/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/blackjack/build.gradle b/exercises/concept/blackjack/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/blackjack/build.gradle +++ b/exercises/concept/blackjack/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/booking-up-for-beauty/build.gradle b/exercises/concept/booking-up-for-beauty/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/booking-up-for-beauty/build.gradle +++ b/exercises/concept/booking-up-for-beauty/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/calculator-conundrum/build.gradle b/exercises/concept/calculator-conundrum/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/calculator-conundrum/build.gradle +++ b/exercises/concept/calculator-conundrum/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/captains-log/build.gradle b/exercises/concept/captains-log/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/captains-log/build.gradle +++ b/exercises/concept/captains-log/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/cars-assemble/build.gradle b/exercises/concept/cars-assemble/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/cars-assemble/build.gradle +++ b/exercises/concept/cars-assemble/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/football-match-reports/build.gradle b/exercises/concept/football-match-reports/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/football-match-reports/build.gradle +++ b/exercises/concept/football-match-reports/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/gotta-snatch-em-all/build.gradle b/exercises/concept/gotta-snatch-em-all/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/gotta-snatch-em-all/build.gradle +++ b/exercises/concept/gotta-snatch-em-all/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/jedliks-toy-car/build.gradle b/exercises/concept/jedliks-toy-car/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/jedliks-toy-car/build.gradle +++ b/exercises/concept/jedliks-toy-car/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/karls-languages/build.gradle b/exercises/concept/karls-languages/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/karls-languages/build.gradle +++ b/exercises/concept/karls-languages/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/lasagna/build.gradle b/exercises/concept/lasagna/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/lasagna/build.gradle +++ b/exercises/concept/lasagna/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/log-levels/build.gradle b/exercises/concept/log-levels/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/log-levels/build.gradle +++ b/exercises/concept/log-levels/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/logs-logs-logs/build.gradle b/exercises/concept/logs-logs-logs/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/logs-logs-logs/build.gradle +++ b/exercises/concept/logs-logs-logs/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/need-for-speed/build.gradle b/exercises/concept/need-for-speed/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/need-for-speed/build.gradle +++ b/exercises/concept/need-for-speed/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/remote-control-competition/build.gradle b/exercises/concept/remote-control-competition/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/remote-control-competition/build.gradle +++ b/exercises/concept/remote-control-competition/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/salary-calculator/build.gradle b/exercises/concept/salary-calculator/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/salary-calculator/build.gradle +++ b/exercises/concept/salary-calculator/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/secrets/build.gradle b/exercises/concept/secrets/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/secrets/build.gradle +++ b/exercises/concept/secrets/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/squeaky-clean/build.gradle b/exercises/concept/squeaky-clean/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/squeaky-clean/build.gradle +++ b/exercises/concept/squeaky-clean/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/tim-from-marketing/build.gradle b/exercises/concept/tim-from-marketing/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/tim-from-marketing/build.gradle +++ b/exercises/concept/tim-from-marketing/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/wizards-and-warriors-2/build.gradle b/exercises/concept/wizards-and-warriors-2/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/wizards-and-warriors-2/build.gradle +++ b/exercises/concept/wizards-and-warriors-2/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/concept/wizards-and-warriors/build.gradle b/exercises/concept/wizards-and-warriors/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/concept/wizards-and-warriors/build.gradle +++ b/exercises/concept/wizards-and-warriors/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/acronym/build.gradle b/exercises/practice/acronym/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/acronym/build.gradle +++ b/exercises/practice/acronym/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/affine-cipher/build.gradle b/exercises/practice/affine-cipher/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/affine-cipher/build.gradle +++ b/exercises/practice/affine-cipher/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/all-your-base/build.gradle b/exercises/practice/all-your-base/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/all-your-base/build.gradle +++ b/exercises/practice/all-your-base/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/allergies/build.gradle b/exercises/practice/allergies/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/allergies/build.gradle +++ b/exercises/practice/allergies/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/alphametics/build.gradle b/exercises/practice/alphametics/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/alphametics/build.gradle +++ b/exercises/practice/alphametics/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/anagram/build.gradle b/exercises/practice/anagram/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/anagram/build.gradle +++ b/exercises/practice/anagram/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/armstrong-numbers/build.gradle b/exercises/practice/armstrong-numbers/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/armstrong-numbers/build.gradle +++ b/exercises/practice/armstrong-numbers/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/atbash-cipher/build.gradle b/exercises/practice/atbash-cipher/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/atbash-cipher/build.gradle +++ b/exercises/practice/atbash-cipher/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/bank-account/build.gradle b/exercises/practice/bank-account/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/bank-account/build.gradle +++ b/exercises/practice/bank-account/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/binary-search-tree/build.gradle b/exercises/practice/binary-search-tree/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/binary-search-tree/build.gradle +++ b/exercises/practice/binary-search-tree/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/binary-search/build.gradle b/exercises/practice/binary-search/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/binary-search/build.gradle +++ b/exercises/practice/binary-search/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/bob/build.gradle b/exercises/practice/bob/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/bob/build.gradle +++ b/exercises/practice/bob/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/book-store/build.gradle b/exercises/practice/book-store/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/book-store/build.gradle +++ b/exercises/practice/book-store/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/bottle-song/build.gradle b/exercises/practice/bottle-song/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/bottle-song/build.gradle +++ b/exercises/practice/bottle-song/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/bowling/build.gradle b/exercises/practice/bowling/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/bowling/build.gradle +++ b/exercises/practice/bowling/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/change/build.gradle b/exercises/practice/change/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/change/build.gradle +++ b/exercises/practice/change/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/circular-buffer/build.gradle b/exercises/practice/circular-buffer/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/circular-buffer/build.gradle +++ b/exercises/practice/circular-buffer/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/clock/build.gradle b/exercises/practice/clock/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/clock/build.gradle +++ b/exercises/practice/clock/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/collatz-conjecture/build.gradle b/exercises/practice/collatz-conjecture/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/collatz-conjecture/build.gradle +++ b/exercises/practice/collatz-conjecture/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/complex-numbers/build.gradle b/exercises/practice/complex-numbers/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/complex-numbers/build.gradle +++ b/exercises/practice/complex-numbers/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/connect/build.gradle b/exercises/practice/connect/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/connect/build.gradle +++ b/exercises/practice/connect/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/crypto-square/build.gradle b/exercises/practice/crypto-square/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/crypto-square/build.gradle +++ b/exercises/practice/crypto-square/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/custom-set/build.gradle b/exercises/practice/custom-set/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/custom-set/build.gradle +++ b/exercises/practice/custom-set/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/darts/build.gradle b/exercises/practice/darts/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/darts/build.gradle +++ b/exercises/practice/darts/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/diamond/build.gradle b/exercises/practice/diamond/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/diamond/build.gradle +++ b/exercises/practice/diamond/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/difference-of-squares/build.gradle b/exercises/practice/difference-of-squares/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/difference-of-squares/build.gradle +++ b/exercises/practice/difference-of-squares/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/dnd-character/build.gradle b/exercises/practice/dnd-character/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/dnd-character/build.gradle +++ b/exercises/practice/dnd-character/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/dominoes/build.gradle b/exercises/practice/dominoes/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/dominoes/build.gradle +++ b/exercises/practice/dominoes/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/dot-dsl/build.gradle b/exercises/practice/dot-dsl/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/dot-dsl/build.gradle +++ b/exercises/practice/dot-dsl/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/eliuds-eggs/build.gradle b/exercises/practice/eliuds-eggs/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/eliuds-eggs/build.gradle +++ b/exercises/practice/eliuds-eggs/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/error-handling/build.gradle b/exercises/practice/error-handling/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/error-handling/build.gradle +++ b/exercises/practice/error-handling/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/etl/build.gradle b/exercises/practice/etl/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/etl/build.gradle +++ b/exercises/practice/etl/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/flatten-array/build.gradle b/exercises/practice/flatten-array/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/flatten-array/build.gradle +++ b/exercises/practice/flatten-array/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/food-chain/build.gradle b/exercises/practice/food-chain/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/food-chain/build.gradle +++ b/exercises/practice/food-chain/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/forth/build.gradle b/exercises/practice/forth/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/forth/build.gradle +++ b/exercises/practice/forth/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/game-of-life/build.gradle b/exercises/practice/game-of-life/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/practice/game-of-life/build.gradle +++ b/exercises/practice/game-of-life/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/gigasecond/build.gradle b/exercises/practice/gigasecond/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/gigasecond/build.gradle +++ b/exercises/practice/gigasecond/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/go-counting/build.gradle b/exercises/practice/go-counting/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/go-counting/build.gradle +++ b/exercises/practice/go-counting/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/grade-school/build.gradle b/exercises/practice/grade-school/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/grade-school/build.gradle +++ b/exercises/practice/grade-school/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/grains/build.gradle b/exercises/practice/grains/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/grains/build.gradle +++ b/exercises/practice/grains/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/grep/build.gradle b/exercises/practice/grep/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/grep/build.gradle +++ b/exercises/practice/grep/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/hamming/build.gradle b/exercises/practice/hamming/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/hamming/build.gradle +++ b/exercises/practice/hamming/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/hello-world/build.gradle b/exercises/practice/hello-world/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/hello-world/build.gradle +++ b/exercises/practice/hello-world/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/high-scores/build.gradle b/exercises/practice/high-scores/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/high-scores/build.gradle +++ b/exercises/practice/high-scores/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/house/build.gradle b/exercises/practice/house/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/house/build.gradle +++ b/exercises/practice/house/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/isbn-verifier/build.gradle b/exercises/practice/isbn-verifier/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/isbn-verifier/build.gradle +++ b/exercises/practice/isbn-verifier/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/isogram/build.gradle b/exercises/practice/isogram/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/isogram/build.gradle +++ b/exercises/practice/isogram/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/killer-sudoku-helper/build.gradle b/exercises/practice/killer-sudoku-helper/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/killer-sudoku-helper/build.gradle +++ b/exercises/practice/killer-sudoku-helper/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/kindergarten-garden/build.gradle b/exercises/practice/kindergarten-garden/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/kindergarten-garden/build.gradle +++ b/exercises/practice/kindergarten-garden/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/knapsack/build.gradle b/exercises/practice/knapsack/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/knapsack/build.gradle +++ b/exercises/practice/knapsack/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/largest-series-product/build.gradle b/exercises/practice/largest-series-product/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/largest-series-product/build.gradle +++ b/exercises/practice/largest-series-product/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/leap/build.gradle b/exercises/practice/leap/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/leap/build.gradle +++ b/exercises/practice/leap/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/ledger/build.gradle b/exercises/practice/ledger/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/ledger/build.gradle +++ b/exercises/practice/ledger/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/linked-list/build.gradle b/exercises/practice/linked-list/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/linked-list/build.gradle +++ b/exercises/practice/linked-list/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/list-ops/build.gradle b/exercises/practice/list-ops/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/list-ops/build.gradle +++ b/exercises/practice/list-ops/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/luhn/build.gradle b/exercises/practice/luhn/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/luhn/build.gradle +++ b/exercises/practice/luhn/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/markdown/build.gradle b/exercises/practice/markdown/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/markdown/build.gradle +++ b/exercises/practice/markdown/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/matching-brackets/build.gradle b/exercises/practice/matching-brackets/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/matching-brackets/build.gradle +++ b/exercises/practice/matching-brackets/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/matrix/build.gradle b/exercises/practice/matrix/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/matrix/build.gradle +++ b/exercises/practice/matrix/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/mazy-mice/build.gradle b/exercises/practice/mazy-mice/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/mazy-mice/build.gradle +++ b/exercises/practice/mazy-mice/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/meetup/build.gradle b/exercises/practice/meetup/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/meetup/build.gradle +++ b/exercises/practice/meetup/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/micro-blog/build.gradle b/exercises/practice/micro-blog/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/micro-blog/build.gradle +++ b/exercises/practice/micro-blog/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/minesweeper/build.gradle b/exercises/practice/minesweeper/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/minesweeper/build.gradle +++ b/exercises/practice/minesweeper/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/nth-prime/build.gradle b/exercises/practice/nth-prime/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/nth-prime/build.gradle +++ b/exercises/practice/nth-prime/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/nucleotide-count/build.gradle b/exercises/practice/nucleotide-count/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/nucleotide-count/build.gradle +++ b/exercises/practice/nucleotide-count/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/ocr-numbers/build.gradle b/exercises/practice/ocr-numbers/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/ocr-numbers/build.gradle +++ b/exercises/practice/ocr-numbers/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/palindrome-products/build.gradle b/exercises/practice/palindrome-products/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/palindrome-products/build.gradle +++ b/exercises/practice/palindrome-products/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/pangram/build.gradle b/exercises/practice/pangram/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/pangram/build.gradle +++ b/exercises/practice/pangram/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/parallel-letter-frequency/build.gradle b/exercises/practice/parallel-letter-frequency/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/parallel-letter-frequency/build.gradle +++ b/exercises/practice/parallel-letter-frequency/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/pascals-triangle/build.gradle b/exercises/practice/pascals-triangle/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/pascals-triangle/build.gradle +++ b/exercises/practice/pascals-triangle/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/perfect-numbers/build.gradle b/exercises/practice/perfect-numbers/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/perfect-numbers/build.gradle +++ b/exercises/practice/perfect-numbers/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/phone-number/build.gradle b/exercises/practice/phone-number/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/phone-number/build.gradle +++ b/exercises/practice/phone-number/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/pig-latin/build.gradle b/exercises/practice/pig-latin/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/pig-latin/build.gradle +++ b/exercises/practice/pig-latin/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/poker/build.gradle b/exercises/practice/poker/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/poker/build.gradle +++ b/exercises/practice/poker/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/pov/build.gradle b/exercises/practice/pov/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/pov/build.gradle +++ b/exercises/practice/pov/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/prime-factors/build.gradle b/exercises/practice/prime-factors/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/prime-factors/build.gradle +++ b/exercises/practice/prime-factors/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/protein-translation/build.gradle b/exercises/practice/protein-translation/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/protein-translation/build.gradle +++ b/exercises/practice/protein-translation/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/proverb/build.gradle b/exercises/practice/proverb/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/proverb/build.gradle +++ b/exercises/practice/proverb/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/pythagorean-triplet/build.gradle b/exercises/practice/pythagorean-triplet/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/pythagorean-triplet/build.gradle +++ b/exercises/practice/pythagorean-triplet/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/queen-attack/build.gradle b/exercises/practice/queen-attack/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/queen-attack/build.gradle +++ b/exercises/practice/queen-attack/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/rail-fence-cipher/build.gradle b/exercises/practice/rail-fence-cipher/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/rail-fence-cipher/build.gradle +++ b/exercises/practice/rail-fence-cipher/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/raindrops/build.gradle b/exercises/practice/raindrops/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/raindrops/build.gradle +++ b/exercises/practice/raindrops/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/rational-numbers/build.gradle b/exercises/practice/rational-numbers/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/rational-numbers/build.gradle +++ b/exercises/practice/rational-numbers/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/react/build.gradle b/exercises/practice/react/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/react/build.gradle +++ b/exercises/practice/react/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/rectangles/build.gradle b/exercises/practice/rectangles/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/rectangles/build.gradle +++ b/exercises/practice/rectangles/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/resistor-color-duo/build.gradle b/exercises/practice/resistor-color-duo/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/resistor-color-duo/build.gradle +++ b/exercises/practice/resistor-color-duo/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/resistor-color-trio/build.gradle b/exercises/practice/resistor-color-trio/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/resistor-color-trio/build.gradle +++ b/exercises/practice/resistor-color-trio/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/resistor-color/build.gradle b/exercises/practice/resistor-color/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/resistor-color/build.gradle +++ b/exercises/practice/resistor-color/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/reverse-string/build.gradle b/exercises/practice/reverse-string/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/reverse-string/build.gradle +++ b/exercises/practice/reverse-string/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/rna-transcription/build.gradle b/exercises/practice/rna-transcription/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/rna-transcription/build.gradle +++ b/exercises/practice/rna-transcription/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/robot-name/build.gradle b/exercises/practice/robot-name/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/robot-name/build.gradle +++ b/exercises/practice/robot-name/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/robot-simulator/build.gradle b/exercises/practice/robot-simulator/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/robot-simulator/build.gradle +++ b/exercises/practice/robot-simulator/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/roman-numerals/build.gradle b/exercises/practice/roman-numerals/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/roman-numerals/build.gradle +++ b/exercises/practice/roman-numerals/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/rotational-cipher/build.gradle b/exercises/practice/rotational-cipher/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/rotational-cipher/build.gradle +++ b/exercises/practice/rotational-cipher/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/run-length-encoding/build.gradle b/exercises/practice/run-length-encoding/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/run-length-encoding/build.gradle +++ b/exercises/practice/run-length-encoding/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/saddle-points/build.gradle b/exercises/practice/saddle-points/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/saddle-points/build.gradle +++ b/exercises/practice/saddle-points/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/satellite/build.gradle b/exercises/practice/satellite/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/satellite/build.gradle +++ b/exercises/practice/satellite/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/say/build.gradle b/exercises/practice/say/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/say/build.gradle +++ b/exercises/practice/say/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/scrabble-score/build.gradle b/exercises/practice/scrabble-score/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/scrabble-score/build.gradle +++ b/exercises/practice/scrabble-score/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/secret-handshake/build.gradle b/exercises/practice/secret-handshake/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/secret-handshake/build.gradle +++ b/exercises/practice/secret-handshake/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/series/build.gradle b/exercises/practice/series/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/series/build.gradle +++ b/exercises/practice/series/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/sgf-parsing/build.gradle b/exercises/practice/sgf-parsing/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/sgf-parsing/build.gradle +++ b/exercises/practice/sgf-parsing/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/sieve/build.gradle b/exercises/practice/sieve/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/sieve/build.gradle +++ b/exercises/practice/sieve/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/simple-cipher/build.gradle b/exercises/practice/simple-cipher/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/simple-cipher/build.gradle +++ b/exercises/practice/simple-cipher/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/simple-linked-list/build.gradle b/exercises/practice/simple-linked-list/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/simple-linked-list/build.gradle +++ b/exercises/practice/simple-linked-list/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/space-age/build.gradle b/exercises/practice/space-age/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/space-age/build.gradle +++ b/exercises/practice/space-age/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/spiral-matrix/build.gradle b/exercises/practice/spiral-matrix/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/spiral-matrix/build.gradle +++ b/exercises/practice/spiral-matrix/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/square-root/build.gradle b/exercises/practice/square-root/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/square-root/build.gradle +++ b/exercises/practice/square-root/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/state-of-tic-tac-toe/build.gradle b/exercises/practice/state-of-tic-tac-toe/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/state-of-tic-tac-toe/build.gradle +++ b/exercises/practice/state-of-tic-tac-toe/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/sublist/build.gradle b/exercises/practice/sublist/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/sublist/build.gradle +++ b/exercises/practice/sublist/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/sum-of-multiples/build.gradle b/exercises/practice/sum-of-multiples/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/sum-of-multiples/build.gradle +++ b/exercises/practice/sum-of-multiples/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/tournament/build.gradle b/exercises/practice/tournament/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/tournament/build.gradle +++ b/exercises/practice/tournament/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/transpose/build.gradle b/exercises/practice/transpose/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/transpose/build.gradle +++ b/exercises/practice/transpose/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/tree-building/build.gradle b/exercises/practice/tree-building/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/tree-building/build.gradle +++ b/exercises/practice/tree-building/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/triangle/build.gradle b/exercises/practice/triangle/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/triangle/build.gradle +++ b/exercises/practice/triangle/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/twelve-days/build.gradle b/exercises/practice/twelve-days/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/twelve-days/build.gradle +++ b/exercises/practice/twelve-days/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/two-bucket/build.gradle b/exercises/practice/two-bucket/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/two-bucket/build.gradle +++ b/exercises/practice/two-bucket/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/two-fer/build.gradle b/exercises/practice/two-fer/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/two-fer/build.gradle +++ b/exercises/practice/two-fer/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/variable-length-quantity/build.gradle b/exercises/practice/variable-length-quantity/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/variable-length-quantity/build.gradle +++ b/exercises/practice/variable-length-quantity/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/word-count/build.gradle b/exercises/practice/word-count/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/word-count/build.gradle +++ b/exercises/practice/word-count/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/word-search/build.gradle b/exercises/practice/word-search/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/word-search/build.gradle +++ b/exercises/practice/word-search/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/wordy/build.gradle b/exercises/practice/wordy/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/wordy/build.gradle +++ b/exercises/practice/wordy/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/yacht/build.gradle b/exercises/practice/yacht/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/yacht/build.gradle +++ b/exercises/practice/yacht/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/zebra-puzzle/build.gradle b/exercises/practice/zebra-puzzle/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/zebra-puzzle/build.gradle +++ b/exercises/practice/zebra-puzzle/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/exercises/practice/zipper/build.gradle b/exercises/practice/zipper/build.gradle index d2eca9ec7..d28f35dee 100644 --- a/exercises/practice/zipper/build.gradle +++ b/exercises/practice/zipper/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { diff --git a/resources/exercise-template/build.gradle b/resources/exercise-template/build.gradle index 1344305f7..dd3862eb9 100644 --- a/resources/exercise-template/build.gradle +++ b/resources/exercise-template/build.gradle @@ -10,6 +10,8 @@ dependencies { testImplementation platform("org.junit:junit-bom:5.10.0") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" } test { From 5e9667480025b02f43849df9796a32b8f93e2494 Mon Sep 17 00:00:00 2001 From: jagdish-15 Date: Tue, 18 Mar 2025 19:17:43 +0530 Subject: [PATCH 14/67] Sync Flatten-Array (#2928) This change also adds an instructions.append.md since the instructions have been updated to refer to "arrays" instead of "lists" and we want to avoid updating the exercises for now (which would invalidate existing solutions). --- .../.docs/instructions.append.md | 3 +++ .../flatten-array/.docs/instructions.md | 15 +++++++++----- .../flatten-array/.docs/introduction.md | 7 +++++++ .../practice/flatten-array/.meta/tests.toml | 20 +++++++++++++++++++ 4 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 exercises/practice/flatten-array/.docs/instructions.append.md create mode 100644 exercises/practice/flatten-array/.docs/introduction.md diff --git a/exercises/practice/flatten-array/.docs/instructions.append.md b/exercises/practice/flatten-array/.docs/instructions.append.md new file mode 100644 index 000000000..c5bf8cb0c --- /dev/null +++ b/exercises/practice/flatten-array/.docs/instructions.append.md @@ -0,0 +1,3 @@ +# Instructions append + +For the Java track, the input will be provided as a `List` instead of an array, and the output should also be a `List`. diff --git a/exercises/practice/flatten-array/.docs/instructions.md b/exercises/practice/flatten-array/.docs/instructions.md index 89dacfa32..b5b82713d 100644 --- a/exercises/practice/flatten-array/.docs/instructions.md +++ b/exercises/practice/flatten-array/.docs/instructions.md @@ -1,11 +1,16 @@ # Instructions -Take a nested list and return a single flattened list with all values except nil/null. +Take a nested array of any depth and return a fully flattened array. -The challenge is to take an arbitrarily-deep nested list-like structure and produce a flattened structure without any nil/null values. +Note that some language tracks may include null-like values in the input array, and the way these values are represented varies by track. +Such values should be excluded from the flattened array. -For example: +Additionally, the input may be of a different data type and contain different types, depending on the track. -input: [1,[2,3,null,4],[null],5] +Check the test suite for details. -output: [1,2,3,4,5] +## Example + +input: `[1, [2, 6, null], [[null, [4]], 5]]` + +output: `[1, 2, 6, 4, 5]` diff --git a/exercises/practice/flatten-array/.docs/introduction.md b/exercises/practice/flatten-array/.docs/introduction.md new file mode 100644 index 000000000..a31485746 --- /dev/null +++ b/exercises/practice/flatten-array/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +A shipment of emergency supplies has arrived, but there's a problem. +To protect from damage, the items — flashlights, first-aid kits, blankets — are packed inside boxes, and some of those boxes are nested several layers deep inside other boxes! + +To be prepared for an emergency, everything must be easily accessible in one box. +Can you unpack all the supplies and place them into a single box, so they're ready when needed most? diff --git a/exercises/practice/flatten-array/.meta/tests.toml b/exercises/practice/flatten-array/.meta/tests.toml index 6300219d7..44acf175d 100644 --- a/exercises/practice/flatten-array/.meta/tests.toml +++ b/exercises/practice/flatten-array/.meta/tests.toml @@ -32,12 +32,32 @@ description = "null values are omitted from the final result" [c6cf26de-8ccd-4410-84bd-b9efd88fd2bc] description = "consecutive null values at the front of the list are omitted from the final result" +include = false + +[bc72da10-5f55-4ada-baf3-50e4da02ec8e] +description = "consecutive null values at the front of the array are omitted from the final result" +reimplements = "c6cf26de-8ccd-4410-84bd-b9efd88fd2bc" [382c5242-587e-4577-b8ce-a5fb51e385a1] description = "consecutive null values in the middle of the list are omitted from the final result" +include = false + +[6991836d-0d9b-4703-80a0-3f1f23eb5981] +description = "consecutive null values in the middle of the array are omitted from the final result" +reimplements = "382c5242-587e-4577-b8ce-a5fb51e385a1" [ef1d4790-1b1e-4939-a179-51ace0829dbd] description = "6 level nest list with null values" +include = false + +[dc90a09c-5376-449c-a7b3-c2d20d540069] +description = "6 level nested array with null values" +reimplements = "ef1d4790-1b1e-4939-a179-51ace0829dbd" [85721643-705a-4150-93ab-7ae398e2942d] description = "all values in nested list are null" +include = false + +[51f5d9af-8f7f-4fb5-a156-69e8282cb275] +description = "all values in nested array are null" +reimplements = "85721643-705a-4150-93ab-7ae398e2942d" From 13ea0e9c44883f04b47f26f0794450a806af947c Mon Sep 17 00:00:00 2001 From: Brijesh Thummar Date: Wed, 19 Mar 2025 04:58:52 +0530 Subject: [PATCH 15/67] Remove redundant instructions for knapsack exercise (#2930) This commit removes the instructions.append.md which seems to contains hints for how to solve the exercise instead of instructions. --- exercises/practice/knapsack/.docs/instructions.append.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 exercises/practice/knapsack/.docs/instructions.append.md diff --git a/exercises/practice/knapsack/.docs/instructions.append.md b/exercises/practice/knapsack/.docs/instructions.append.md deleted file mode 100644 index 67e2260d7..000000000 --- a/exercises/practice/knapsack/.docs/instructions.append.md +++ /dev/null @@ -1,4 +0,0 @@ -# Instructions append - -- Use recursion -- Check if there are any overlapping subproblems whose results can be cached From 06e462bb8f837e52fc64fe069d896e5493cf8f72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 19:45:20 +0800 Subject: [PATCH 16/67] Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#2921) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.0 to 4.6.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08...4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index d442a7481..71113085b 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -47,7 +47,7 @@ jobs: - name: Test all exercises using java-test-runner run: bin/test-with-test-runner - name: Archive test results - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 with: name: test-results path: exercises/**/build/results.json From ecb775951225ec31eae5517230e85067b44b34b4 Mon Sep 17 00:00:00 2001 From: Kah Goh Date: Mon, 24 Mar 2025 21:33:42 +0800 Subject: [PATCH 17/67] Fix gradle-wrapper.jar (#2932) It seems that the gradle-wrapper.jar was accidentally corrupted in commit f2720d83c8. This is preventing gradlew from working. --- .../gradle/wrapper/gradle-wrapper.jar | Bin 526 -> 43453 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar index 71858cda34775e40119cf99663074619ad53ce87..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 From df966c341a9530ae2289613b6a0ccac32cdfbbc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 21:13:45 +0800 Subject: [PATCH 18/67] Bump actions/upload-artifact from 4.6.1 to 4.6.2 (#2934) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1...ea165f8d65b6e75b540449e92b4886f43607fa02) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 4.6.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/java.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 71113085b..c3a3174d3 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -47,7 +47,7 @@ jobs: - name: Test all exercises using java-test-runner run: bin/test-with-test-runner - name: Archive test results - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: name: test-results path: exercises/**/build/results.json From e4025168ae381856e3a0f66c947dba40d7538194 Mon Sep 17 00:00:00 2001 From: jagdish-15 Date: Tue, 29 Apr 2025 06:08:59 +0530 Subject: [PATCH 19/67] Update leap design.md for analyzer (#2940) --- exercises/practice/leap/.meta/design.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exercises/practice/leap/.meta/design.md b/exercises/practice/leap/.meta/design.md index ba1f404b6..6ef49b284 100644 --- a/exercises/practice/leap/.meta/design.md +++ b/exercises/practice/leap/.meta/design.md @@ -6,7 +6,8 @@ This exercise could benefit from the following rules in the [analyzer]: - `essential`: Verify that the solution does not use `java.time.Year.isLeap(int)` or `new java.util.GregorianCalendar().isLeapYear(int)`. - `essential`: Verify that the solution does not contain hard-coded years used in the tests. -- `actionable`: If the solution uses conditional statements like `if/else` or ternary expressions, instruct the student to use simple boolean logic instead. +- `actionable`: If the solution uses `if/else` statements, instruct the student to use simple boolean logic or a single ternary operator instead. +- `actionable`: If the solution uses more than 1 ternary operator, instruct the student that their solution can be simplified. - `actionable`: If the solution contains more than 3 checks, instruct the student that their solution can be simplified. [analyzer]: https://github.com/exercism/java-analyzer From a6065865cd7bde56026407f1fe43c7373d6657ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 13:14:50 +0800 Subject: [PATCH 20/67] Bump actions/setup-java from 4.7.0 to 4.7.1 (#2943) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.7.0 to 4.7.1. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/3a4f6e1af504cf6a31855fa899c6aa5355ba6c12...c5195efecf7bdfc987ee8bae7a71cb8b11521c00) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 4.7.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/java.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index c3a3174d3..1a4953152 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: java-version: 17 distribution: "temurin" @@ -31,7 +31,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 - uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 with: java-version: 17 distribution: "temurin" From a41402bdb660580bcb4abfe8ac91bbe6df0825c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Mon, 2 Jun 2025 05:40:32 -0700 Subject: [PATCH 21/67] Add `relative-distance` (#2947) --- config.json | 13 + .../relative-distance/.docs/instructions.md | 39 +++ .../relative-distance/.docs/introduction.md | 12 + .../relative-distance/.meta/config.json | 22 ++ .../src/reference/java/RelativeDistance.java | 68 +++++ .../relative-distance/.meta/tests.toml | 31 +++ .../practice/relative-distance/build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + exercises/practice/relative-distance/gradlew | 249 +++++++++++++++++ .../practice/relative-distance/gradlew.bat | 92 +++++++ .../src/main/java/RelativeDistance.java | 13 + .../src/test/java/RelativeDistanceTest.java | 256 ++++++++++++++++++ exercises/settings.gradle | 1 + 14 files changed, 827 insertions(+) create mode 100644 exercises/practice/relative-distance/.docs/instructions.md create mode 100644 exercises/practice/relative-distance/.docs/introduction.md create mode 100644 exercises/practice/relative-distance/.meta/config.json create mode 100644 exercises/practice/relative-distance/.meta/src/reference/java/RelativeDistance.java create mode 100644 exercises/practice/relative-distance/.meta/tests.toml create mode 100644 exercises/practice/relative-distance/build.gradle create mode 100644 exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.properties create mode 100755 exercises/practice/relative-distance/gradlew create mode 100644 exercises/practice/relative-distance/gradlew.bat create mode 100644 exercises/practice/relative-distance/src/main/java/RelativeDistance.java create mode 100644 exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java diff --git a/config.json b/config.json index 7c8f6b933..3d1ff49af 100644 --- a/config.json +++ b/config.json @@ -1054,6 +1054,19 @@ ], "difficulty": 5 }, + { + "slug": "relative-distance", + "name": "Relative Distance", + "uuid": "a3cf95fd-c7c1-4199-a253-7bae8d1aba9a", + "practices": [ + "maps" + ], + "prerequisites": [ + "lists", + "maps" + ], + "difficulty": 5 + }, { "slug": "robot-name", "name": "Robot Name", diff --git a/exercises/practice/relative-distance/.docs/instructions.md b/exercises/practice/relative-distance/.docs/instructions.md new file mode 100644 index 000000000..9046aee7c --- /dev/null +++ b/exercises/practice/relative-distance/.docs/instructions.md @@ -0,0 +1,39 @@ +# Instructions + +Your task is to determine the degree of separation between two individuals in a family tree. +This is similar to the pop culture idea that every Hollywood actor is [within six degrees of Kevin Bacon][six-bacons]. + +- You will be given an input, with all parent names and their children. +- Each name is unique, a child _can_ have one or two parents. +- The degree of separation is defined as the shortest number of connections from one person to another. +- If two individuals are not connected, return a value that represents "no known relationship." + Please see the test cases for the actual implementation. + +## Example + +Given the following family tree: + +```text + ┌──────────┐ ┌──────────┐ ┌───────────┐ + │ Helena │ │ Erdős ├─────┤ Shusaku │ + └───┬───┬──┘ └─────┬────┘ └────┬──────┘ + ┌───┘ └───────┐ └───────┬───────┘ +┌─────┴────┐ ┌────┴───┐ ┌─────┴────┐ +│ Isla ├─────┤ Tariq │ │ Kevin │ +└────┬─────┘ └────┬───┘ └──────────┘ + │ │ +┌────┴────┐ ┌────┴───┐ +│ Uma │ │ Morphy │ +└─────────┘ └────────┘ +``` + +The degree of separation between Tariq and Uma is 2 (Tariq → Isla → Uma). +There's no known relationship between Isla and Kevin, as there is no connection in the given data. +The degree of separation between Uma and Isla is 1. + +~~~~exercism/note +Isla and Tariq are siblings and have a separation of 1. +Similarly, this implementation would report a separation of 2 from you to your father's brother. +~~~~ + +[six-bacons]: https://en.m.wikipedia.org/wiki/Six_Degrees_of_Kevin_Bacon diff --git a/exercises/practice/relative-distance/.docs/introduction.md b/exercises/practice/relative-distance/.docs/introduction.md new file mode 100644 index 000000000..cb9fee6c7 --- /dev/null +++ b/exercises/practice/relative-distance/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +You've been hired to develop **Noble Knots**, the hottest new dating app for nobility! +With centuries of royal intermarriage, things have gotten… _complicated_. +To avoid any _oops-we're-twins_ situations, your job is to build a system that checks how closely two people are related. + +Noble Knots is inspired by Iceland's "[Islendinga-App][islendiga-app]," which is backed up by a database that traces all known family connections between Icelanders from the time of the settlement of Iceland. +Your algorithm will determine the **degree of separation** between two individuals in the royal family tree. + +Will your app help crown a perfect match? + +[islendiga-app]: http://www.islendingaapp.is/information-in-english/ diff --git a/exercises/practice/relative-distance/.meta/config.json b/exercises/practice/relative-distance/.meta/config.json new file mode 100644 index 000000000..c6b2cc422 --- /dev/null +++ b/exercises/practice/relative-distance/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "src/main/java/RelativeDistance.java" + ], + "test": [ + "src/test/java/RelativeDistanceTest.java" + ], + "example": [ + ".meta/src/reference/java/RelativeDistance.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Given a family tree, calculate the degree of separation.", + "source": "vaeng", + "source_url": "https://github.com/exercism/problem-specifications/pull/2537" +} diff --git a/exercises/practice/relative-distance/.meta/src/reference/java/RelativeDistance.java b/exercises/practice/relative-distance/.meta/src/reference/java/RelativeDistance.java new file mode 100644 index 000000000..3d170879b --- /dev/null +++ b/exercises/practice/relative-distance/.meta/src/reference/java/RelativeDistance.java @@ -0,0 +1,68 @@ +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +class RelativeDistance { + + private final Map> graph; + + RelativeDistance(Map> familyTree) { + final HashMap> connections = new HashMap<>(); + + for (Map.Entry> entry : familyTree.entrySet()) { + String parent = entry.getKey(); + List children = entry.getValue(); + + connections.putIfAbsent(parent, new HashSet<>()); + + for (String child : children) { + connections.putIfAbsent(child, new HashSet<>()); + + connections.get(parent).add(child); + connections.get(child).add(parent); + + for (String sibling : children) { + if (!sibling.equals(child)) { + connections.get(child).add(sibling); + } + } + } + } + + graph = connections; + } + + int degreeOfSeparation(String personA, String personB) { + if (!graph.containsKey(personA) || !graph.containsKey(personB)) { + return -1; + } + + Queue queue = new LinkedList<>(); + Map distances = new HashMap<>() { + { + put(personA, 0); + } + }; + queue.add(personA); + + while (!queue.isEmpty()) { + String current = queue.poll(); + int currentDistance = distances.get(current); + + for (String relative : graph.get(current)) { + if (!distances.containsKey(relative)) { + if (relative.equals(personB)) { + return currentDistance + 1; + } + distances.put(relative, currentDistance + 1); + queue.add(relative); + } + } + } + + return -1; + } +} diff --git a/exercises/practice/relative-distance/.meta/tests.toml b/exercises/practice/relative-distance/.meta/tests.toml new file mode 100644 index 000000000..66c91ba09 --- /dev/null +++ b/exercises/practice/relative-distance/.meta/tests.toml @@ -0,0 +1,31 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[4a1ded74-5d32-47fb-8ae5-321f51d06b5b] +description = "Direct parent-child relation" + +[30d17269-83e9-4f82-a0d7-8ef9656d8dce] +description = "Sibling relationship" + +[8dffa27d-a8ab-496d-80b3-2f21c77648b5] +description = "Two degrees of separation, grandchild" + +[34e56ec1-d528-4a42-908e-020a4606ee60] +description = "Unrelated individuals" + +[93ffe989-bad2-48c4-878f-3acb1ce2611b] +description = "Complex graph, cousins" + +[2cc2e76b-013a-433c-9486-1dbe29bf06e5] +description = "Complex graph, no shortcut, far removed nephew" + +[46c9fbcb-e464-455f-a718-049ea3c7400a] +description = "Complex graph, some shortcuts, cross-down and cross-up, cousins several times removed, with unrelated family tree" diff --git a/exercises/practice/relative-distance/build.gradle b/exercises/practice/relative-distance/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/practice/relative-distance/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 zcma&N1CXTcmMvW9vTb(Rwr$&4wr$(C?dmSu>@vG-+vuvg^_??!{yS%8zW-#zn-LkA z5&1^$^{lnmUON?}LBF8_K|(?T0Ra(xUH{($5eN!MR#ZihR#HxkUPe+_R8Cn`RRs(P z_^*#_XlXmGv7!4;*Y%p4nw?{bNp@UZHv1?Um8r6)Fei3p@ClJn0ECfg1hkeuUU@Or zDaPa;U3fE=3L}DooL;8f;P0ipPt0Z~9P0)lbStMS)ag54=uL9ia-Lm3nh|@(Y?B`; zx_#arJIpXH!U{fbCbI^17}6Ri*H<>OLR%c|^mh8+)*h~K8Z!9)DPf zR2h?lbDZQ`p9P;&DQ4F0sur@TMa!Y}S8irn(%d-gi0*WxxCSk*A?3lGh=gcYN?FGl z7D=Js!i~0=u3rox^eO3i@$0=n{K1lPNU zwmfjRVmLOCRfe=seV&P*1Iq=^i`502keY8Uy-WNPwVNNtJFx?IwAyRPZo2Wo1+S(xF37LJZ~%i)kpFQ3Fw=mXfd@>%+)RpYQLnr}B~~zoof(JVm^^&f zxKV^+3D3$A1G;qh4gPVjhrC8e(VYUHv#dy^)(RoUFM?o%W-EHxufuWf(l*@-l+7vt z=l`qmR56K~F|v<^Pd*p~1_y^P0P^aPC##d8+HqX4IR1gu+7w#~TBFphJxF)T$2WEa zxa?H&6=Qe7d(#tha?_1uQys2KtHQ{)Qco)qwGjrdNL7thd^G5i8Os)CHqc>iOidS} z%nFEDdm=GXBw=yXe1W-ShHHFb?Cc70+$W~z_+}nAoHFYI1MV1wZegw*0y^tC*s%3h zhD3tN8b=Gv&rj}!SUM6|ajSPp*58KR7MPpI{oAJCtY~JECm)*m_x>AZEu>DFgUcby z1Qaw8lU4jZpQ_$;*7RME+gq1KySGG#Wql>aL~k9tLrSO()LWn*q&YxHEuzmwd1?aAtI zBJ>P=&$=l1efe1CDU;`Fd+_;&wI07?V0aAIgc(!{a z0Jg6Y=inXc3^n!U0Atk`iCFIQooHqcWhO(qrieUOW8X(x?(RD}iYDLMjSwffH2~tB z)oDgNBLB^AJBM1M^c5HdRx6fBfka`(LD-qrlh5jqH~);#nw|iyp)()xVYak3;Ybik z0j`(+69aK*B>)e_p%=wu8XC&9e{AO4c~O1U`5X9}?0mrd*m$_EUek{R?DNSh(=br# z#Q61gBzEpmy`$pA*6!87 zSDD+=@fTY7<4A?GLqpA?Pb2z$pbCc4B4zL{BeZ?F-8`s$?>*lXXtn*NC61>|*w7J* z$?!iB{6R-0=KFmyp1nnEmLsA-H0a6l+1uaH^g%c(p{iT&YFrbQ$&PRb8Up#X3@Zsk zD^^&LK~111%cqlP%!_gFNa^dTYT?rhkGl}5=fL{a`UViaXWI$k-UcHJwmaH1s=S$4 z%4)PdWJX;hh5UoK?6aWoyLxX&NhNRqKam7tcOkLh{%j3K^4Mgx1@i|Pi&}<^5>hs5 zm8?uOS>%)NzT(%PjVPGa?X%`N2TQCKbeH2l;cTnHiHppPSJ<7y-yEIiC!P*ikl&!B z%+?>VttCOQM@ShFguHVjxX^?mHX^hSaO_;pnyh^v9EumqSZTi+#f&_Vaija0Q-e*| z7ulQj6Fs*bbmsWp{`auM04gGwsYYdNNZcg|ph0OgD>7O}Asn7^Z=eI>`$2*v78;sj-}oMoEj&@)9+ycEOo92xSyY344^ z11Hb8^kdOvbf^GNAK++bYioknrpdN>+u8R?JxG=!2Kd9r=YWCOJYXYuM0cOq^FhEd zBg2puKy__7VT3-r*dG4c62Wgxi52EMCQ`bKgf*#*ou(D4-ZN$+mg&7$u!! z-^+Z%;-3IDwqZ|K=ah85OLwkO zKxNBh+4QHh)u9D?MFtpbl)us}9+V!D%w9jfAMYEb>%$A;u)rrI zuBudh;5PN}_6J_}l55P3l_)&RMlH{m!)ai-i$g)&*M`eN$XQMw{v^r@-125^RRCF0 z^2>|DxhQw(mtNEI2Kj(;KblC7x=JlK$@78`O~>V!`|1Lm-^JR$-5pUANAnb(5}B}JGjBsliK4& zk6y(;$e&h)lh2)L=bvZKbvh@>vLlreBdH8No2>$#%_Wp1U0N7Ank!6$dFSi#xzh|( zRi{Uw%-4W!{IXZ)fWx@XX6;&(m_F%c6~X8hx=BN1&q}*( zoaNjWabE{oUPb!Bt$eyd#$5j9rItB-h*5JiNi(v^e|XKAj*8(k<5-2$&ZBR5fF|JA z9&m4fbzNQnAU}r8ab>fFV%J0z5awe#UZ|bz?Ur)U9bCIKWEzi2%A+5CLqh?}K4JHi z4vtM;+uPsVz{Lfr;78W78gC;z*yTch~4YkLr&m-7%-xc ztw6Mh2d>_iO*$Rd8(-Cr1_V8EO1f*^@wRoSozS) zy1UoC@pruAaC8Z_7~_w4Q6n*&B0AjOmMWa;sIav&gu z|J5&|{=a@vR!~k-OjKEgPFCzcJ>#A1uL&7xTDn;{XBdeM}V=l3B8fE1--DHjSaxoSjNKEM9|U9#m2<3>n{Iuo`r3UZp;>GkT2YBNAh|b z^jTq-hJp(ebZh#Lk8hVBP%qXwv-@vbvoREX$TqRGTgEi$%_F9tZES@z8Bx}$#5eeG zk^UsLBH{bc2VBW)*EdS({yw=?qmevwi?BL6*=12k9zM5gJv1>y#ML4!)iiPzVaH9% zgSImetD@dam~e>{LvVh!phhzpW+iFvWpGT#CVE5TQ40n%F|p(sP5mXxna+Ev7PDwA zamaV4m*^~*xV+&p;W749xhb_X=$|LD;FHuB&JL5?*Y2-oIT(wYY2;73<^#46S~Gx| z^cez%V7x$81}UWqS13Gz80379Rj;6~WdiXWOSsdmzY39L;Hg3MH43o*y8ibNBBH`(av4|u;YPq%{R;IuYow<+GEsf@R?=@tT@!}?#>zIIn0CoyV!hq3mw zHj>OOjfJM3F{RG#6ujzo?y32m^tgSXf@v=J$ELdJ+=5j|=F-~hP$G&}tDZsZE?5rX ztGj`!S>)CFmdkccxM9eGIcGnS2AfK#gXwj%esuIBNJQP1WV~b~+D7PJTmWGTSDrR` zEAu4B8l>NPuhsk5a`rReSya2nfV1EK01+G!x8aBdTs3Io$u5!6n6KX%uv@DxAp3F@{4UYg4SWJtQ-W~0MDb|j-$lwVn znAm*Pl!?Ps&3wO=R115RWKb*JKoexo*)uhhHBncEDMSVa_PyA>k{Zm2(wMQ(5NM3# z)jkza|GoWEQo4^s*wE(gHz?Xsg4`}HUAcs42cM1-qq_=+=!Gk^y710j=66(cSWqUe zklbm8+zB_syQv5A2rj!Vbw8;|$@C!vfNmNV!yJIWDQ>{+2x zKjuFX`~~HKG~^6h5FntRpnnHt=D&rq0>IJ9#F0eM)Y-)GpRjiN7gkA8wvnG#K=q{q z9dBn8_~wm4J<3J_vl|9H{7q6u2A!cW{bp#r*-f{gOV^e=8S{nc1DxMHFwuM$;aVI^ zz6A*}m8N-&x8;aunp1w7_vtB*pa+OYBw=TMc6QK=mbA-|Cf* zvyh8D4LRJImooUaSb7t*fVfih<97Gf@VE0|z>NcBwBQze);Rh!k3K_sfunToZY;f2 z^HmC4KjHRVg+eKYj;PRN^|E0>Gj_zagfRbrki68I^#~6-HaHg3BUW%+clM1xQEdPYt_g<2K+z!$>*$9nQ>; zf9Bei{?zY^-e{q_*|W#2rJG`2fy@{%6u0i_VEWTq$*(ZN37|8lFFFt)nCG({r!q#9 z5VK_kkSJ3?zOH)OezMT{!YkCuSSn!K#-Rhl$uUM(bq*jY? zi1xbMVthJ`E>d>(f3)~fozjg^@eheMF6<)I`oeJYx4*+M&%c9VArn(OM-wp%M<-`x z7sLP1&3^%Nld9Dhm@$3f2}87!quhI@nwd@3~fZl_3LYW-B?Ia>ui`ELg z&Qfe!7m6ze=mZ`Ia9$z|ARSw|IdMpooY4YiPN8K z4B(ts3p%2i(Td=tgEHX z0UQ_>URBtG+-?0E;E7Ld^dyZ;jjw0}XZ(}-QzC6+NN=40oDb2^v!L1g9xRvE#@IBR zO!b-2N7wVfLV;mhEaXQ9XAU+>=XVA6f&T4Z-@AX!leJ8obP^P^wP0aICND?~w&NykJ#54x3_@r7IDMdRNy4Hh;h*!u(Ol(#0bJdwEo$5437-UBjQ+j=Ic>Q2z` zJNDf0yO6@mr6y1#n3)s(W|$iE_i8r@Gd@!DWDqZ7J&~gAm1#~maIGJ1sls^gxL9LLG_NhU!pTGty!TbhzQnu)I*S^54U6Yu%ZeCg`R>Q zhBv$n5j0v%O_j{QYWG!R9W?5_b&67KB$t}&e2LdMvd(PxN6Ir!H4>PNlerpBL>Zvyy!yw z-SOo8caEpDt(}|gKPBd$qND5#a5nju^O>V&;f890?yEOfkSG^HQVmEbM3Ugzu+UtH zC(INPDdraBN?P%kE;*Ae%Wto&sgw(crfZ#Qy(<4nk;S|hD3j{IQRI6Yq|f^basLY; z-HB&Je%Gg}Jt@={_C{L$!RM;$$|iD6vu#3w?v?*;&()uB|I-XqEKqZPS!reW9JkLewLb!70T7n`i!gNtb1%vN- zySZj{8-1>6E%H&=V}LM#xmt`J3XQoaD|@XygXjdZ1+P77-=;=eYpoEQ01B@L*a(uW zrZeZz?HJsw_4g0vhUgkg@VF8<-X$B8pOqCuWAl28uB|@r`19DTUQQsb^pfqB6QtiT z*`_UZ`fT}vtUY#%sq2{rchyfu*pCg;uec2$-$N_xgjZcoumE5vSI{+s@iLWoz^Mf; zuI8kDP{!XY6OP~q5}%1&L}CtfH^N<3o4L@J@zg1-mt{9L`s^z$Vgb|mr{@WiwAqKg zp#t-lhrU>F8o0s1q_9y`gQNf~Vb!F%70f}$>i7o4ho$`uciNf=xgJ>&!gSt0g;M>*x4-`U)ysFW&Vs^Vk6m%?iuWU+o&m(2Jm26Y(3%TL; zA7T)BP{WS!&xmxNw%J=$MPfn(9*^*TV;$JwRy8Zl*yUZi8jWYF>==j~&S|Xinsb%c z2?B+kpet*muEW7@AzjBA^wAJBY8i|#C{WtO_or&Nj2{=6JTTX05}|H>N2B|Wf!*3_ z7hW*j6p3TvpghEc6-wufFiY!%-GvOx*bZrhZu+7?iSrZL5q9}igiF^*R3%DE4aCHZ zqu>xS8LkW+Auv%z-<1Xs92u23R$nk@Pk}MU5!gT|c7vGlEA%G^2th&Q*zfg%-D^=f z&J_}jskj|Q;73NP4<4k*Y%pXPU2Thoqr+5uH1yEYM|VtBPW6lXaetokD0u z9qVek6Q&wk)tFbQ8(^HGf3Wp16gKmr>G;#G(HRBx?F`9AIRboK+;OfHaLJ(P>IP0w zyTbTkx_THEOs%Q&aPrxbZrJlio+hCC_HK<4%f3ZoSAyG7Dn`=X=&h@m*|UYO-4Hq0 z-Bq&+Ie!S##4A6OGoC~>ZW`Y5J)*ouaFl_e9GA*VSL!O_@xGiBw!AF}1{tB)z(w%c zS1Hmrb9OC8>0a_$BzeiN?rkPLc9%&;1CZW*4}CDDNr2gcl_3z+WC15&H1Zc2{o~i) z)LLW=WQ{?ricmC`G1GfJ0Yp4Dy~Ba;j6ZV4r{8xRs`13{dD!xXmr^Aga|C=iSmor% z8hi|pTXH)5Yf&v~exp3o+sY4B^^b*eYkkCYl*T{*=-0HniSA_1F53eCb{x~1k3*`W zr~};p1A`k{1DV9=UPnLDgz{aJH=-LQo<5%+Em!DNN252xwIf*wF_zS^!(XSm(9eoj z=*dXG&n0>)_)N5oc6v!>-bd(2ragD8O=M|wGW z!xJQS<)u70m&6OmrF0WSsr@I%T*c#Qo#Ha4d3COcX+9}hM5!7JIGF>7<~C(Ear^Sn zm^ZFkV6~Ula6+8S?oOROOA6$C&q&dp`>oR-2Ym3(HT@O7Sd5c~+kjrmM)YmgPH*tL zX+znN>`tv;5eOfX?h{AuX^LK~V#gPCu=)Tigtq9&?7Xh$qN|%A$?V*v=&-2F$zTUv z`C#WyIrChS5|Kgm_GeudCFf;)!WH7FI60j^0o#65o6`w*S7R@)88n$1nrgU(oU0M9 zx+EuMkC>(4j1;m6NoGqEkpJYJ?vc|B zOlwT3t&UgL!pX_P*6g36`ZXQ; z9~Cv}ANFnJGp(;ZhS(@FT;3e)0)Kp;h^x;$*xZn*k0U6-&FwI=uOGaODdrsp-!K$Ac32^c{+FhI-HkYd5v=`PGsg%6I`4d9Jy)uW0y%) zm&j^9WBAp*P8#kGJUhB!L?a%h$hJgQrx!6KCB_TRo%9{t0J7KW8!o1B!NC)VGLM5! zpZy5Jc{`r{1e(jd%jsG7k%I+m#CGS*BPA65ZVW~fLYw0dA-H_}O zrkGFL&P1PG9p2(%QiEWm6x;U-U&I#;Em$nx-_I^wtgw3xUPVVu zqSuKnx&dIT-XT+T10p;yjo1Y)z(x1fb8Dzfn8e yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c z&{(nX_ht=QbJ%DzAya}!&uNu!4V0xI)QE$SY__m)SAKcN0P(&JcoK*Lxr@P zY&P=}&B3*UWNlc|&$Oh{BEqwK2+N2U$4WB7Fd|aIal`FGANUa9E-O)!gV`((ZGCc$ zBJA|FFrlg~9OBp#f7aHodCe{6= zay$6vN~zj1ddMZ9gQ4p32(7wD?(dE>KA2;SOzXRmPBiBc6g`eOsy+pVcHu=;Yd8@{ zSGgXf@%sKKQz~;!J;|2fC@emm#^_rnO0esEn^QxXgJYd`#FPWOUU5b;9eMAF zZhfiZb|gk8aJIw*YLp4!*(=3l8Cp{(%p?ho22*vN9+5NLV0TTazNY$B5L6UKUrd$n zjbX%#m7&F#U?QNOBXkiiWB*_tk+H?N3`vg;1F-I+83{M2!8<^nydGr5XX}tC!10&e z7D36bLaB56WrjL&HiiMVtpff|K%|*{t*ltt^5ood{FOG0<>k&1h95qPio)2`eL${YAGIx(b4VN*~nKn6E~SIQUuRH zQ+5zP6jfnP$S0iJ@~t!Ai3o`X7biohli;E zT#yXyl{bojG@-TGZzpdVDXhbmF%F9+-^YSIv|MT1l3j zrxOFq>gd2%U}?6}8mIj?M zc077Zc9fq(-)4+gXv?Az26IO6eV`RAJz8e3)SC7~>%rlzDwySVx*q$ygTR5kW2ds- z!HBgcq0KON9*8Ff$X0wOq$`T7ml(@TF)VeoF}x1OttjuVHn3~sHrMB++}f7f9H%@f z=|kP_?#+fve@{0MlbkC9tyvQ_R?lRdRJ@$qcB(8*jyMyeME5ns6ypVI1Xm*Zr{DuS zZ!1)rQfa89c~;l~VkCiHI|PCBd`S*2RLNQM8!g9L6?n`^evQNEwfO@&JJRme+uopQX0%Jo zgd5G&#&{nX{o?TQwQvF1<^Cg3?2co;_06=~Hcb6~4XWpNFL!WU{+CK;>gH%|BLOh7@!hsa(>pNDAmpcuVO-?;Bic17R}^|6@8DahH)G z!EmhsfunLL|3b=M0MeK2vqZ|OqUqS8npxwge$w-4pFVXFq$_EKrZY?BuP@Az@(k`L z`ViQBSk`y+YwRT;&W| z2e3UfkCo^uTA4}Qmmtqs+nk#gNr2W4 zTH%hhErhB)pkXR{B!q5P3-OM+M;qu~f>}IjtF%>w{~K-0*jPVLl?Chz&zIdxp}bjx zStp&Iufr58FTQ36AHU)0+CmvaOpKF;W@sMTFpJ`j;3d)J_$tNQI^c<^1o<49Z(~K> z;EZTBaVT%14(bFw2ob@?JLQ2@(1pCdg3S%E4*dJ}dA*v}_a4_P(a`cHnBFJxNobAv zf&Zl-Yt*lhn-wjZsq<9v-IsXxAxMZ58C@e0!rzhJ+D@9^3~?~yllY^s$?&oNwyH!#~6x4gUrfxplCvK#!f z$viuszW>MFEcFL?>ux*((!L$;R?xc*myjRIjgnQX79@UPD$6Dz0jutM@7h_pq z0Zr)#O<^y_K6jfY^X%A-ip>P%3saX{!v;fxT-*0C_j4=UMH+Xth(XVkVGiiKE#f)q z%Jp=JT)uy{&}Iq2E*xr4YsJ5>w^=#-mRZ4vPXpI6q~1aFwi+lQcimO45V-JXP;>(Q zo={U`{=_JF`EQj87Wf}{Qy35s8r1*9Mxg({CvOt}?Vh9d&(}iI-quvs-rm~P;eRA@ zG5?1HO}puruc@S{YNAF3vmUc2B4!k*yi))<5BQmvd3tr}cIs#9)*AX>t`=~{f#Uz0 z0&Nk!7sSZwJe}=)-R^$0{yeS!V`Dh7w{w5rZ9ir!Z7Cd7dwZcK;BT#V0bzTt>;@Cl z#|#A!-IL6CZ@eHH!CG>OO8!%G8&8t4)Ro@}USB*k>oEUo0LsljsJ-%5Mo^MJF2I8- z#v7a5VdJ-Cd%(a+y6QwTmi+?f8Nxtm{g-+WGL>t;s#epv7ug>inqimZCVm!uT5Pf6 ziEgQt7^%xJf#!aPWbuC_3Nxfb&CFbQy!(8ANpkWLI4oSnH?Q3f?0k1t$3d+lkQs{~(>06l&v|MpcFsyAv zin6N!-;pggosR*vV=DO(#+}4ps|5$`udE%Kdmp?G7B#y%H`R|i8skKOd9Xzx8xgR$>Zo2R2Ytktq^w#ul4uicxW#{ zFjG_RNlBroV_n;a7U(KIpcp*{M~e~@>Q#Av90Jc5v%0c>egEdY4v3%|K1XvB{O_8G zkTWLC>OZKf;XguMH2-Pw{BKbFzaY;4v2seZV0>^7Q~d4O=AwaPhP3h|!hw5aqOtT@ z!SNz}$of**Bl3TK209@F=Tn1+mgZa8yh(Png%Zd6Mt}^NSjy)etQrF zme*llAW=N_8R*O~d2!apJnF%(JcN??=`$qs3Y+~xs>L9x`0^NIn!8mMRFA_tg`etw z3k{9JAjnl@ygIiJcNHTy02GMAvBVqEss&t2<2mnw!; zU`J)0>lWiqVqo|ex7!+@0i>B~BSU1A_0w#Ee+2pJx0BFiZ7RDHEvE*ptc9md(B{&+ zKE>TM)+Pd>HEmdJao7U@S>nL(qq*A)#eLOuIfAS@j`_sK0UEY6OAJJ-kOrHG zjHx`g!9j*_jRcJ%>CE9K2MVf?BUZKFHY?EpV6ai7sET-tqk=nDFh-(65rhjtlKEY% z@G&cQ<5BKatfdA1FKuB=i>CCC5(|9TMW%K~GbA4}80I5%B}(gck#Wlq@$nO3%@QP_ z8nvPkJFa|znk>V92cA!K1rKtr)skHEJD;k8P|R8RkCq1Rh^&}Evwa4BUJz2f!2=MH zo4j8Y$YL2313}H~F7@J7mh>u%556Hw0VUOz-Un@ZASCL)y8}4XXS`t1AC*^>PLwIc zUQok5PFS=*#)Z!3JZN&eZ6ZDP^-c@StY*t20JhCnbMxXf=LK#;`4KHEqMZ-Ly9KsS zI2VUJGY&PmdbM+iT)zek)#Qc#_i4uH43 z@T5SZBrhNCiK~~esjsO9!qBpaWK<`>!-`b71Y5ReXQ4AJU~T2Njri1CEp5oKw;Lnm)-Y@Z3sEY}XIgSy%xo=uek(kAAH5MsV$V3uTUsoTzxp_rF=tx zV07vlJNKtJhCu`b}*#m&5LV4TAE&%KtHViDAdv#c^x`J7bg z&N;#I2GkF@SIGht6p-V}`!F_~lCXjl1BdTLIjD2hH$J^YFN`7f{Q?OHPFEM$65^!u zNwkelo*5+$ZT|oQ%o%;rBX$+?xhvjb)SHgNHE_yP%wYkkvXHS{Bf$OiKJ5d1gI0j< zF6N}Aq=(WDo(J{e-uOecxPD>XZ@|u-tgTR<972`q8;&ZD!cep^@B5CaqFz|oU!iFj zU0;6fQX&~15E53EW&w1s9gQQ~Zk16X%6 zjG`j0yq}4deX2?Tr(03kg>C(!7a|b9qFI?jcE^Y>-VhudI@&LI6Qa}WQ>4H_!UVyF z((cm&!3gmq@;BD#5P~0;_2qgZhtJS|>WdtjY=q zLnHH~Fm!cxw|Z?Vw8*~?I$g#9j&uvgm7vPr#&iZgPP~v~BI4jOv;*OQ?jYJtzO<^y z7-#C={r7CO810!^s(MT!@@Vz_SVU)7VBi(e1%1rvS!?PTa}Uv`J!EP3s6Y!xUgM^8 z4f!fq<3Wer_#;u!5ECZ|^c1{|q_lh3m^9|nsMR1#Qm|?4Yp5~|er2?W^7~cl;_r4WSme_o68J9p03~Hc%X#VcX!xAu%1`R!dfGJCp zV*&m47>s^%Ib0~-2f$6oSgn3jg8m%UA;ArcdcRyM5;}|r;)?a^D*lel5C`V5G=c~k zy*w_&BfySOxE!(~PI$*dwG><+-%KT5p?whOUMA*k<9*gi#T{h3DAxzAPxN&Xws8o9Cp*`PA5>d9*Z-ynV# z9yY*1WR^D8|C%I@vo+d8r^pjJ$>eo|j>XiLWvTWLl(^;JHCsoPgem6PvegHb-OTf| zvTgsHSa;BkbG=(NgPO|CZu9gUCGr$8*EoH2_Z#^BnxF0yM~t`|9ws_xZ8X8iZYqh! zAh;HXJ)3P&)Q0(&F>!LN0g#bdbis-cQxyGn9Qgh`q+~49Fqd2epikEUw9caM%V6WgP)532RMRW}8gNS%V%Hx7apSz}tn@bQy!<=lbhmAH=FsMD?leawbnP5BWM0 z5{)@EEIYMu5;u)!+HQWhQ;D3_Cm_NADNeb-f56}<{41aYq8p4=93d=-=q0Yx#knGYfXVt z+kMxlus}t2T5FEyCN~!}90O_X@@PQpuy;kuGz@bWft%diBTx?d)_xWd_-(!LmVrh**oKg!1CNF&LX4{*j|) zIvjCR0I2UUuuEXh<9}oT_zT#jOrJAHNLFT~Ilh9hGJPI1<5`C-WA{tUYlyMeoy!+U zhA#=p!u1R7DNg9u4|QfED-2TuKI}>p#2P9--z;Bbf4Op*;Q9LCbO&aL2i<0O$ByoI z!9;Ght733FC>Pz>$_mw(F`zU?`m@>gE`9_p*=7o=7av`-&ifU(^)UU`Kg3Kw`h9-1 z6`e6+im=|m2v`pN(2dE%%n8YyQz;#3Q-|x`91z?gj68cMrHl}C25|6(_dIGk*8cA3 zRHB|Nwv{@sP4W+YZM)VKI>RlB`n=Oj~Rzx~M+Khz$N$45rLn6k1nvvD^&HtsMA4`s=MmuOJID@$s8Ph4E zAmSV^+s-z8cfv~Yd(40Sh4JG#F~aB>WFoX7ykaOr3JaJ&Lb49=B8Vk-SQT9%7TYhv z?-Pprt{|=Y5ZQ1?od|A<_IJU93|l4oAfBm?3-wk{O<8ea+`}u%(kub(LFo2zFtd?4 zwpN|2mBNywv+d^y_8#<$r>*5+$wRTCygFLcrwT(qc^n&@9r+}Kd_u@Ithz(6Qb4}A zWo_HdBj#V$VE#l6pD0a=NfB0l^6W^g`vm^sta>Tly?$E&{F?TTX~DsKF~poFfmN%2 z4x`Dc{u{Lkqz&y!33;X}weD}&;7p>xiI&ZUb1H9iD25a(gI|`|;G^NwJPv=1S5e)j z;U;`?n}jnY6rA{V^ zxTd{bK)Gi^odL3l989DQlN+Zs39Xe&otGeY(b5>rlIqfc7Ap4}EC?j<{M=hlH{1+d zw|c}}yx88_xQr`{98Z!d^FNH77=u(p-L{W6RvIn40f-BldeF-YD>p6#)(Qzf)lfZj z?3wAMtPPp>vMehkT`3gToPd%|D8~4`5WK{`#+}{L{jRUMt zrFz+O$C7y8$M&E4@+p+oV5c%uYzbqd2Y%SSgYy#xh4G3hQv>V*BnuKQhBa#=oZB~w{azUB+q%bRe_R^ z>fHBilnRTUfaJ201czL8^~Ix#+qOHSO)A|xWLqOxB$dT2W~)e-r9;bm=;p;RjYahB z*1hegN(VKK+ztr~h1}YP@6cfj{e#|sS`;3tJhIJK=tVJ-*h-5y9n*&cYCSdg#EHE# zSIx=r#qOaLJoVVf6v;(okg6?*L_55atl^W(gm^yjR?$GplNP>BZsBYEf_>wM0Lc;T zhf&gpzOWNxS>m+mN92N0{;4uw`P+9^*|-1~$uXpggj4- z^SFc4`uzj2OwdEVT@}Q`(^EcQ_5(ZtXTql*yGzdS&vrS_w>~~ra|Nb5abwf}Y!uq6R5f&6g2ge~2p(%c< z@O)cz%%rr4*cRJ5f`n@lvHNk@lE1a*96Kw6lJ~B-XfJW%?&-y?;E&?1AacU@`N`!O z6}V>8^%RZ7SQnZ-z$(jsX`amu*5Fj8g!3RTRwK^`2_QHe;_2y_n|6gSaGyPmI#kA0sYV<_qOZc#-2BO%hX)f$s-Z3xlI!ub z^;3ru11DA`4heAu%}HIXo&ctujzE2!6DIGE{?Zs>2}J+p&C$rc7gJC35gxhflorvsb%sGOxpuWhF)dL_&7&Z99=5M0b~Qa;Mo!j&Ti_kXW!86N%n= zSC@6Lw>UQ__F&+&Rzv?gscwAz8IP!n63>SP)^62(HK98nGjLY2*e^OwOq`3O|C92? z;TVhZ2SK%9AGW4ZavTB9?)mUbOoF`V7S=XM;#3EUpR+^oHtdV!GK^nXzCu>tpR|89 zdD{fnvCaN^^LL%amZ^}-E+214g&^56rpdc@yv0b<3}Ys?)f|fXN4oHf$six)-@<;W&&_kj z-B}M5U*1sb4)77aR=@%I?|Wkn-QJVuA96an25;~!gq(g1@O-5VGo7y&E_srxL6ZfS z*R%$gR}dyONgju*D&?geiSj7SZ@ftyA|}(*Y4KbvU!YLsi1EDQQCnb+-cM=K1io78o!v*);o<XwjaQH%)uIP&Zm?)Nfbfn;jIr z)d#!$gOe3QHp}2NBak@yYv3m(CPKkwI|{;d=gi552u?xj9ObCU^DJFQp4t4e1tPzM zvsRIGZ6VF+{6PvqsplMZWhz10YwS={?`~O0Ec$`-!klNUYtzWA^f9m7tkEzCy<_nS z=&<(awFeZvt51>@o_~>PLs05CY)$;}Oo$VDO)?l-{CS1Co=nxjqben*O1BR>#9`0^ zkwk^k-wcLCLGh|XLjdWv0_Hg54B&OzCE^3NCP}~OajK-LuRW53CkV~Su0U>zN%yQP zH8UH#W5P3-!ToO-2k&)}nFe`t+mdqCxxAHgcifup^gKpMObbox9LFK;LP3}0dP-UW z?Zo*^nrQ6*$FtZ(>kLCc2LY*|{!dUn$^RW~m9leoF|@Jy|M5p-G~j%+P0_#orRKf8 zvuu5<*XO!B?1E}-*SY~MOa$6c%2cM+xa8}_8x*aVn~57v&W(0mqN1W`5a7*VN{SUH zXz98DDyCnX2EPl-`Lesf`=AQT%YSDb`$%;(jUTrNen$NPJrlpPDP}prI>Ml!r6bCT;mjsg@X^#&<}CGf0JtR{Ecwd&)2zuhr#nqdgHj+g2n}GK9CHuwO zk>oZxy{vcOL)$8-}L^iVfJHAGfwN$prHjYV0ju}8%jWquw>}_W6j~m<}Jf!G?~r5&Rx)!9JNX!ts#SGe2HzobV5); zpj@&`cNcO&q+%*<%D7za|?m5qlmFK$=MJ_iv{aRs+BGVrs)98BlN^nMr{V_fcl_;jkzRju+c-y?gqBC_@J0dFLq-D9@VN&-`R9U;nv$Hg?>$oe4N&Ht$V_(JR3TG^! zzJsbQbi zFE6-{#9{G{+Z}ww!ycl*7rRdmU#_&|DqPfX3CR1I{Kk;bHwF6jh0opI`UV2W{*|nn zf_Y@%wW6APb&9RrbEN=PQRBEpM(N1w`81s=(xQj6 z-eO0k9=Al|>Ej|Mw&G`%q8e$2xVz1v4DXAi8G};R$y)ww638Y=9y$ZYFDM$}vzusg zUf+~BPX>(SjA|tgaFZr_e0{)+z9i6G#lgt=F_n$d=beAt0Sa0a7>z-?vcjl3e+W}+ z1&9=|vC=$co}-Zh*%3588G?v&U7%N1Qf-wNWJ)(v`iO5KHSkC5&g7CrKu8V}uQGcfcz zmBz#Lbqwqy#Z~UzHgOQ;Q-rPxrRNvl(&u6ts4~0=KkeS;zqURz%!-ERppmd%0v>iRlEf+H$yl{_8TMJzo0 z>n)`On|7=WQdsqhXI?#V{>+~}qt-cQbokEbgwV3QvSP7&hK4R{Z{aGHVS3;+h{|Hz z6$Js}_AJr383c_+6sNR|$qu6dqHXQTc6?(XWPCVZv=)D#6_;D_8P-=zOGEN5&?~8S zl5jQ?NL$c%O)*bOohdNwGIKM#jSAC?BVY={@A#c9GmX0=T(0G}xs`-%f3r=m6-cpK z!%waekyAvm9C3%>sixdZj+I(wQlbB4wv9xKI*T13DYG^T%}zZYJ|0$Oj^YtY+d$V$ zAVudSc-)FMl|54n=N{BnZTM|!>=bhaja?o7s+v1*U$!v!qQ%`T-6fBvmdPbVmro&d zk07TOp*KuxRUSTLRrBj{mjsnF8`d}rMViY8j`jo~Hp$fkv9F_g(jUo#Arp;Xw0M$~ zRIN!B22~$kx;QYmOkos@%|5k)!QypDMVe}1M9tZfkpXKGOxvKXB!=lo`p?|R1l=tA zp(1}c6T3Fwj_CPJwVsYtgeRKg?9?}%oRq0F+r+kdB=bFUdVDRPa;E~~>2$w}>O>v=?|e>#(-Lyx?nbg=ckJ#5U6;RT zNvHhXk$P}m9wSvFyU3}=7!y?Y z=fg$PbV8d7g25&-jOcs{%}wTDKm>!Vk);&rr;O1nvO0VrU&Q?TtYVU=ir`te8SLlS zKSNmV=+vF|ATGg`4$N1uS|n??f}C_4Sz!f|4Ly8#yTW-FBfvS48Tef|-46C(wEO_%pPhUC5$-~Y?!0vFZ^Gu`x=m7X99_?C-`|h zfmMM&Y@zdfitA@KPw4Mc(YHcY1)3*1xvW9V-r4n-9ZuBpFcf{yz+SR{ zo$ZSU_|fgwF~aakGr(9Be`~A|3)B=9`$M-TWKipq-NqRDRQc}ABo*s_5kV%doIX7LRLRau_gd@Rd_aLFXGSU+U?uAqh z8qusWWcvgQ&wu{|sRXmv?sl=xc<$6AR$+cl& zFNh5q1~kffG{3lDUdvEZu5c(aAG~+64FxdlfwY^*;JSS|m~CJusvi-!$XR`6@XtY2 znDHSz7}_Bx7zGq-^5{stTRy|I@N=>*y$zz>m^}^{d&~h;0kYiq8<^Wq7Dz0w31ShO^~LUfW6rfitR0(=3;Uue`Y%y@ex#eKPOW zO~V?)M#AeHB2kovn1v=n^D?2{2jhIQd9t|_Q+c|ZFaWt+r&#yrOu-!4pXAJuxM+Cx z*H&>eZ0v8Y`t}8{TV6smOj=__gFC=eah)mZt9gwz>>W$!>b3O;Rm^Ig*POZP8Rl0f zT~o=Nu1J|lO>}xX&#P58%Yl z83`HRs5#32Qm9mdCrMlV|NKNC+Z~ z9OB8xk5HJ>gBLi+m@(pvpw)1(OaVJKs*$Ou#@Knd#bk+V@y;YXT?)4eP9E5{J%KGtYinNYJUH9PU3A}66c>Xn zZ{Bn0<;8$WCOAL$^NqTjwM?5d=RHgw3!72WRo0c;+houoUA@HWLZM;^U$&sycWrFd zE7ekt9;kb0`lps{>R(}YnXlyGY}5pPd9zBpgXeJTY_jwaJGSJQC#-KJqmh-;ad&F- z-Y)E>!&`Rz!HtCz>%yOJ|v(u7P*I$jqEY3}(Z-orn4 zlI?CYKNl`6I){#2P1h)y(6?i;^z`N3bxTV%wNvQW+eu|x=kbj~s8rhCR*0H=iGkSj zk23lr9kr|p7#qKL=UjgO`@UnvzU)`&fI>1Qs7ubq{@+lK{hH* zvl6eSb9%yngRn^T<;jG1SVa)eA>T^XX=yUS@NCKpk?ovCW1D@!=@kn;l_BrG;hOTC z6K&H{<8K#dI(A+zw-MWxS+~{g$tI7|SfP$EYKxA}LlVO^sT#Oby^grkdZ^^lA}uEF zBSj$weBJG{+Bh@Yffzsw=HyChS(dtLE3i*}Zj@~!_T-Ay7z=B)+*~3|?w`Zd)Co2t zC&4DyB!o&YgSw+fJn6`sn$e)29`kUwAc+1MND7YjV%lO;H2}fNy>hD#=gT ze+-aFNpyKIoXY~Vq-}OWPBe?Rfu^{ps8>Xy%42r@RV#*QV~P83jdlFNgkPN=T|Kt7 zV*M`Rh*30&AWlb$;ae130e@}Tqi3zx2^JQHpM>j$6x`#{mu%tZlwx9Gj@Hc92IuY* zarmT|*d0E~vt6<+r?W^UW0&#U&)8B6+1+;k^2|FWBRP9?C4Rk)HAh&=AS8FS|NQaZ z2j!iZ)nbEyg4ZTp-zHwVlfLC~tXIrv(xrP8PAtR{*c;T24ycA-;auWsya-!kF~CWZ zw_uZ|%urXgUbc@x=L=_g@QJ@m#5beS@6W195Hn7>_}z@Xt{DIEA`A&V82bc^#!q8$ zFh?z_Vn|ozJ;NPd^5uu(9tspo8t%&-U9Ckay-s@DnM*R5rtu|4)~e)`z0P-sy?)kc zs_k&J@0&0!q4~%cKL)2l;N*T&0;mqX5T{Qy60%JtKTQZ-xb%KOcgqwJmb%MOOKk7N zgq})R_6**{8A|6H?fO+2`#QU)p$Ei2&nbj6TpLSIT^D$|`TcSeh+)}VMb}LmvZ{O| ze*1IdCt3+yhdYVxcM)Q_V0bIXLgr6~%JS<<&dxIgfL=Vnx4YHuU@I34JXA|+$_S3~ zy~X#gO_X!cSs^XM{yzDGNM>?v(+sF#<0;AH^YrE8smx<36bUsHbN#y57K8WEu(`qHvQ6cAZPo=J5C(lSmUCZ57Rj6cx!e^rfaI5%w}unz}4 zoX=nt)FVNV%QDJH`o!u9olLD4O5fl)xp+#RloZlaA92o3x4->?rB4`gS$;WO{R;Z3>cG3IgFX2EA?PK^M}@%1%A;?f6}s&CV$cIyEr#q5;yHdNZ9h{| z-=dX+a5elJoDo?Eq&Og!nN6A)5yYpnGEp}?=!C-V)(*~z-+?kY1Q7qs#Rsy%hu_60rdbB+QQNr?S1 z?;xtjUv|*E3}HmuNyB9aFL5H~3Ho0UsmuMZELp1a#CA1g`P{-mT?BchuLEtK}!QZ=3AWakRu~?f9V~3F;TV`5%9Pcs_$gq&CcU}r8gOO zC2&SWPsSG{&o-LIGTBqp6SLQZPvYKp$$7L4WRRZ0BR$Kf0I0SCFkqveCp@f)o8W)! z$%7D1R`&j7W9Q9CGus_)b%+B#J2G;l*FLz#s$hw{BHS~WNLODV#(!u_2Pe&tMsq={ zdm7>_WecWF#D=?eMjLj=-_z`aHMZ=3_-&E8;ibPmM}61i6J3is*=dKf%HC>=xbj4$ zS|Q-hWQ8T5mWde6h@;mS+?k=89?1FU<%qH9B(l&O>k|u_aD|DY*@~(`_pb|B#rJ&g zR0(~(68fpUPz6TdS@4JT5MOPrqDh5_H(eX1$P2SQrkvN8sTxwV>l0)Qq z0pzTuvtEAKRDkKGhhv^jk%|HQ1DdF%5oKq5BS>szk-CIke{%js?~%@$uaN3^Uz6Wf z_iyx{bZ(;9y4X&>LPV=L=d+A}7I4GkK0c1Xts{rrW1Q7apHf-))`BgC^0^F(>At1* za@e7{lq%yAkn*NH8Q1{@{lKhRg*^TfGvv!Sn*ed*x@6>M%aaqySxR|oNadYt1mpUZ z6H(rupHYf&Z z29$5g#|0MX#aR6TZ$@eGxxABRKakDYtD%5BmKp;HbG_ZbT+=81E&=XRk6m_3t9PvD zr5Cqy(v?gHcYvYvXkNH@S#Po~q(_7MOuCAB8G$a9BC##gw^5mW16cML=T=ERL7wsk zzNEayTG?mtB=x*wc@ifBCJ|irFVMOvH)AFRW8WE~U()QT=HBCe@s$dA9O!@`zAAT) zaOZ7l6vyR+Nk_OOF!ZlZmjoImKh)dxFbbR~z(cMhfeX1l7S_`;h|v3gI}n9$sSQ>+3@AFAy9=B_y$)q;Wdl|C-X|VV3w8 z2S#>|5dGA8^9%Bu&fhmVRrTX>Z7{~3V&0UpJNEl0=N32euvDGCJ>#6dUSi&PxFW*s zS`}TB>?}H(T2lxBJ!V#2taV;q%zd6fOr=SGHpoSG*4PDaiG0pdb5`jelVipkEk%FV zThLc@Hc_AL1#D&T4D=w@UezYNJ%0=f3iVRuVL5H?eeZM}4W*bomebEU@e2d`M<~uW zf#Bugwf`VezG|^Qbt6R_=U0}|=k;mIIakz99*>FrsQR{0aQRP6ko?5<7bkDN8evZ& zB@_KqQG?ErKL=1*ZM9_5?Pq%lcS4uLSzN(Mr5=t6xHLS~Ym`UgM@D&VNu8e?_=nSFtF$u@hpPSmI4Vo_t&v?>$~K4y(O~Rb*(MFy_igM7 z*~yYUyR6yQgzWnWMUgDov!!g=lInM+=lOmOk4L`O?{i&qxy&D*_qorRbDwj6?)!ef z#JLd7F6Z2I$S0iYI={rZNk*<{HtIl^mx=h>Cim*04K4+Z4IJtd*-)%6XV2(MCscPiw_a+y*?BKbTS@BZ3AUao^%Zi#PhoY9Vib4N>SE%4>=Jco0v zH_Miey{E;FkdlZSq)e<{`+S3W=*ttvD#hB8w=|2aV*D=yOV}(&p%0LbEWH$&@$X3x~CiF-?ejQ*N+-M zc8zT@3iwkdRT2t(XS`d7`tJQAjRmKAhiw{WOqpuvFp`i@Q@!KMhwKgsA}%@sw8Xo5Y=F zhRJZg)O4uqNWj?V&&vth*H#je6T}}p_<>!Dr#89q@uSjWv~JuW(>FqoJ5^ho0%K?E z9?x_Q;kmcsQ@5=}z@tdljMSt9-Z3xn$k)kEjK|qXS>EfuDmu(Z8|(W?gY6-l z@R_#M8=vxKMAoi&PwnaIYw2COJM@atcgfr=zK1bvjW?9B`-+Voe$Q+H$j!1$Tjn+* z&LY<%)L@;zhnJlB^Og6I&BOR-m?{IW;tyYC%FZ!&Z>kGjHJ6cqM-F z&19n+e1=9AH1VrVeHrIzqlC`w9=*zfmrerF?JMzO&|Mmv;!4DKc(sp+jy^Dx?(8>1 zH&yS_4yL7m&GWX~mdfgH*AB4{CKo;+egw=PrvkTaoBU+P-4u?E|&!c z)DKc;>$$B6u*Zr1SjUh2)FeuWLWHl5TH(UHWkf zLs>7px!c5n;rbe^lO@qlYLzlDVp(z?6rPZel=YB)Uv&n!2{+Mb$-vQl=xKw( zve&>xYx+jW_NJh!FV||r?;hdP*jOXYcLCp>DOtJ?2S^)DkM{{Eb zS$!L$e_o0(^}n3tA1R3-$SNvgBq;DOEo}fNc|tB%%#g4RA3{|euq)p+xd3I8^4E&m zFrD%}nvG^HUAIKe9_{tXB;tl|G<%>yk6R;8L2)KUJw4yHJXUOPM>(-+jxq4R;z8H#>rnJy*)8N+$wA$^F zN+H*3t)eFEgxLw+Nw3};4WV$qj&_D`%ADV2%r zJCPCo%{=z7;`F98(us5JnT(G@sKTZ^;2FVitXyLe-S5(hV&Ium+1pIUB(CZ#h|g)u zSLJJ<@HgrDiA-}V_6B^x1>c9B6%~847JkQ!^KLZ2skm;q*edo;UA)~?SghG8;QbHh z_6M;ouo_1rq9=x$<`Y@EA{C%6-pEV}B(1#sDoe_e1s3^Y>n#1Sw;N|}8D|s|VPd+g z-_$QhCz`vLxxrVMx3ape1xu3*wjx=yKSlM~nFgkNWb4?DDr*!?U)L_VeffF<+!j|b zZ$Wn2$TDv3C3V@BHpSgv3JUif8%hk%OsGZ=OxH@8&4`bbf$`aAMchl^qN>Eyu3JH} z9-S!x8-s4fE=lad%Pkp8hAs~u?|uRnL48O|;*DEU! zuS0{cpk%1E0nc__2%;apFsTm0bKtd&A0~S3Cj^?72-*Owk3V!ZG*PswDfS~}2<8le z5+W^`Y(&R)yVF*tU_s!XMcJS`;(Tr`J0%>p=Z&InR%D3@KEzzI+-2)HK zuoNZ&o=wUC&+*?ofPb0a(E6(<2Amd6%uSu_^-<1?hsxs~0K5^f(LsGqgEF^+0_H=uNk9S0bb!|O8d?m5gQjUKevPaO+*VfSn^2892K~%crWM8+6 z25@V?Y@J<9w%@NXh-2!}SK_(X)O4AM1-WTg>sj1{lj5@=q&dxE^9xng1_z9w9DK>| z6Iybcd0e zyi;Ew!KBRIfGPGytQ6}z}MeXCfLY0?9%RiyagSp_D1?N&c{ zyo>VbJ4Gy`@Fv+5cKgUgs~na$>BV{*em7PU3%lloy_aEovR+J7TfQKh8BJXyL6|P8un-Jnq(ghd!_HEOh$zlv2$~y3krgeH;9zC}V3f`uDtW(%mT#944DQa~^8ZI+zAUu4U(j0YcDfKR$bK#gvn_{JZ>|gZ5+)u?T$w7Q%F^;!Wk?G z(le7r!ufT*cxS}PR6hIVtXa)i`d$-_1KkyBU>qmgz-=T};uxx&sKgv48akIWQ89F{ z0XiY?WM^~;|T8zBOr zs#zuOONzH?svv*jokd5SK8wG>+yMC)LYL|vLqm^PMHcT=`}V$=nIRHe2?h)8WQa6O zPAU}d`1y(>kZiP~Gr=mtJLMu`i<2CspL|q2DqAgAD^7*$xzM`PU4^ga`ilE134XBQ z99P(LhHU@7qvl9Yzg$M`+dlS=x^(m-_3t|h>S}E0bcFMn=C|KamQ)=w2^e)35p`zY zRV8X?d;s^>Cof2SPR&nP3E+-LCkS0J$H!eh8~k0qo$}00b=7!H_I2O+Ro@3O$nPdm ztmbOO^B+IHzQ5w>@@@J4cKw5&^_w6s!s=H%&byAbUtczPQ7}wfTqxxtQNfn*u73Qw zGuWsrky_ajPx-5`R<)6xHf>C(oqGf_Fw|-U*GfS?xLML$kv;h_pZ@Kk$y0X(S+K80 z6^|z)*`5VUkawg}=z`S;VhZhxyDfrE0$(PMurAxl~<>lfZa>JZ288ULK7D` zl9|#L^JL}Y$j*j`0-K6kH#?bRmg#5L3iB4Z)%iF@SqT+Lp|{i`m%R-|ZE94Np7Pa5 zCqC^V3}B(FR340pmF*qaa}M}+h6}mqE~7Sh!9bDv9YRT|>vBNAqv09zXHMlcuhKD| zcjjA(b*XCIwJ33?CB!+;{)vX@9xns_b-VO{i0y?}{!sdXj1GM8+$#v>W7nw;+O_9B z_{4L;C6ol?(?W0<6taGEn1^uG=?Q3i29sE`RfYCaV$3DKc_;?HsL?D_fSYg}SuO5U zOB_f4^vZ_x%o`5|C@9C5+o=mFy@au{s)sKw!UgC&L35aH(sgDxRE2De%(%OT=VUdN ziVLEmdOvJ&5*tCMKRyXctCwQu_RH%;m*$YK&m;jtbdH#Ak~13T1^f89tn`A%QEHWs~jnY~E}p_Z$XC z=?YXLCkzVSK+Id`xZYTegb@W8_baLt-Fq`Tv|=)JPbFsKRm)4UW;yT+J`<)%#ue9DPOkje)YF2fsCilK9MIIK>p*`fkoD5nGfmLwt)!KOT+> zOFq*VZktDDyM3P5UOg`~XL#cbzC}eL%qMB=Q5$d89MKuN#$6|4gx_Jt0Gfn8w&q}%lq4QU%6#jT*MRT% zrLz~C8FYKHawn-EQWN1B75O&quS+Z81(zN)G>~vN8VwC+e+y(`>HcxC{MrJ;H1Z4k zZWuv$w_F0-Ub%MVcpIc){4PGL^I7M{>;hS?;eH!;gmcOE66z3;Z1Phqo(t zVP(Hg6q#0gIKgsg7L7WE!{Y#1nI(45tx2{$34dDd#!Z0NIyrm)HOn5W#7;f4pQci# zDW!FI(g4e668kI9{2+mLwB+=#9bfqgX%!B34V-$wwSN(_cm*^{y0jQtv*4}eO^sOV z*9xoNvX)c9isB}Tgx&ZRjp3kwhTVK?r9;n!x>^XYT z@Q^7zp{rkIs{2mUSE^2!Gf6$6;j~&4=-0cSJJDizZp6LTe8b45;{AKM%v99}{{FfC zz709%u0mC=1KXTo(=TqmZQ;c?$M3z(!xah>aywrj40sc2y3rKFw4jCq+Y+u=CH@_V zxz|qeTwa>+<|H%8Dz5u>ZI5MmjTFwXS-Fv!TDd*`>3{krWoNVx$<133`(ftS?ZPyY z&4@ah^3^i`vL$BZa>O|Nt?ucewzsF)0zX3qmM^|waXr=T0pfIb0*$AwU=?Ipl|1Y; z*Pk6{C-p4MY;j@IJ|DW>QHZQJcp;Z~?8(Q+Kk3^0qJ}SCk^*n4W zu9ZFwLHUx-$6xvaQ)SUQcYd6fF8&x)V`1bIuX@>{mE$b|Yd(qomn3;bPwnDUc0F=; zh*6_((%bqAYQWQ~odER?h>1mkL4kpb3s7`0m@rDKGU*oyF)$j~Ffd4fXV$?`f~rHf zB%Y)@5SXZvfwm10RY5X?TEo)PK_`L6qgBp=#>fO49$D zDq8Ozj0q6213tV5Qq=;fZ0$|KroY{Dz=l@lU^J)?Ko@ti20TRplXzphBi>XGx4bou zEWrkNjz0t5j!_ke{g5I#PUlEU$Km8g8TE|XK=MkU@PT4T><2OVamoK;wJ}3X0L$vX zgd7gNa359*nc)R-0!`2X@FOTB`+oETOPc=ubp5R)VQgY+5BTZZJ2?9QwnO=dnulIUF3gFn;BODC2)65)HeVd%t86sL7Rv^Y+nbn+&l z6BAJY(ETvwI)Ts$aiE8rht4KD*qNyE{8{x6R|%akbTBzw;2+6Echkt+W+`u^XX z_z&x%n '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/practice/relative-distance/gradlew.bat b/exercises/practice/relative-distance/gradlew.bat new file mode 100644 index 000000000..25da30dbd --- /dev/null +++ b/exercises/practice/relative-distance/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/practice/relative-distance/src/main/java/RelativeDistance.java b/exercises/practice/relative-distance/src/main/java/RelativeDistance.java new file mode 100644 index 000000000..6b74ce091 --- /dev/null +++ b/exercises/practice/relative-distance/src/main/java/RelativeDistance.java @@ -0,0 +1,13 @@ +import java.util.List; +import java.util.Map; + +class RelativeDistance { + + RelativeDistance(Map> familyTree) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + int degreeOfSeparation(String personA, String personB) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } +} diff --git a/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java b/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java new file mode 100644 index 000000000..3002b1a91 --- /dev/null +++ b/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java @@ -0,0 +1,256 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class RelativeDistanceTest { + + @Test + public void testDirectParentChildRelation() { + Map> familyTree = new HashMap<>() { + { + put("Vera", List.of("Tomoko")); + put("Tomoko", List.of("Aditi")); + } + }; + + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Vera", "Tomoko")).isEqualTo(1); + } + + @Disabled("Remove to run test") + @Test + public void testSiblingRelationship() { + Map> familyTree = new HashMap<>() { + { + put("Dalia", List.of("Olga", "Yassin")); + } + }; + + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Olga", "Yassin")).isEqualTo(1); + } + + @Disabled("Remove to run test") + @Test + public void testTwoDegreesOfSeparationGrandchild() { + Map> familyTree = new HashMap<>() { + { + put("Khadija", List.of("Mateo")); + put("Mateo", List.of("Rami")); + } + }; + + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Khadija", "Rami")).isEqualTo(2); + } + + @Disabled("Remove to run test") + @Test + public void testUnrelatedIndividuals() { + Map> familyTree = new HashMap<>() { + { + put("Priya", List.of("Rami")); + put("Kaito", List.of("Elif")); + } + }; + + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Priya", "Kaito")).isEqualTo(-1); + } + + @Disabled("Remove to run test") + @Test + public void testComplexGraphCousins() { + Map> familyTree = new HashMap<>() { + { + put("Aiko", List.of("Bao", "Carlos")); + put("Bao", List.of("Dalia", "Elias")); + put("Carlos", List.of("Fatima", "Gustavo")); + put("Dalia", List.of("Hassan", "Isla")); + put("Elias", List.of("Javier")); + put("Fatima", List.of("Khadija", "Liam")); + put("Gustavo", List.of("Mina")); + put("Hassan", List.of("Noah", "Olga")); + put("Isla", List.of("Pedro")); + put("Javier", List.of("Quynh", "Ravi")); + put("Khadija", List.of("Sofia")); + put("Liam", List.of("Tariq", "Uma")); + put("Mina", List.of("Viktor", "Wang")); + put("Noah", List.of("Xiomara")); + put("Olga", List.of("Yuki")); + put("Pedro", List.of("Zane", "Aditi")); + put("Quynh", List.of("Boris")); + put("Ravi", List.of("Celine")); + put("Sofia", List.of("Diego", "Elif")); + put("Tariq", List.of("Farah")); + put("Uma", List.of("Giorgio")); + put("Viktor", List.of("Hana", "Ian")); + put("Wang", List.of("Jing")); + put("Xiomara", List.of("Kaito")); + put("Yuki", List.of("Leila")); + put("Zane", List.of("Mateo")); + put("Aditi", List.of("Nia")); + put("Boris", List.of("Oscar")); + put("Celine", List.of("Priya")); + put("Diego", List.of("Qi")); + put("Elif", List.of("Rami")); + put("Farah", List.of("Sven")); + put("Giorgio", List.of("Tomoko")); + put("Hana", List.of("Umar")); + put("Ian", List.of("Vera")); + put("Jing", List.of("Wyatt")); + put("Kaito", List.of("Xia")); + put("Leila", List.of("Yassin")); + put("Mateo", List.of("Zara")); + put("Nia", List.of("Antonio")); + put("Oscar", List.of("Bianca")); + put("Priya", List.of("Cai")); + put("Qi", List.of("Dimitri")); + put("Rami", List.of("Ewa")); + put("Sven", List.of("Fabio")); + put("Tomoko", List.of("Gabriela")); + put("Umar", List.of("Helena")); + put("Vera", List.of("Igor")); + put("Wyatt", List.of("Jun")); + put("Xia", List.of("Kim")); + put("Yassin", List.of("Lucia")); + put("Zara", List.of("Mohammed")); + } + }; + + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Dimitri", "Fabio")).isEqualTo(9); + } + + @Disabled("Remove to run test") + @Test + public void testComplexGraphNoShortcutFarRemovedNephew() { + Map> familyTree = new HashMap<>() { + { + put("Aiko", List.of("Bao", "Carlos")); + put("Bao", List.of("Dalia", "Elias")); + put("Carlos", List.of("Fatima", "Gustavo")); + put("Dalia", List.of("Hassan", "Isla")); + put("Elias", List.of("Javier")); + put("Fatima", List.of("Khadija", "Liam")); + put("Gustavo", List.of("Mina")); + put("Hassan", List.of("Noah", "Olga")); + put("Isla", List.of("Pedro")); + put("Javier", List.of("Quynh", "Ravi")); + put("Khadija", List.of("Sofia")); + put("Liam", List.of("Tariq", "Uma")); + put("Mina", List.of("Viktor", "Wang")); + put("Noah", List.of("Xiomara")); + put("Olga", List.of("Yuki")); + put("Pedro", List.of("Zane", "Aditi")); + put("Quynh", List.of("Boris")); + put("Ravi", List.of("Celine")); + put("Sofia", List.of("Diego", "Elif")); + put("Tariq", List.of("Farah")); + put("Uma", List.of("Giorgio")); + put("Viktor", List.of("Hana", "Ian")); + put("Wang", List.of("Jing")); + put("Xiomara", List.of("Kaito")); + put("Yuki", List.of("Leila")); + put("Zane", List.of("Mateo")); + put("Aditi", List.of("Nia")); + put("Boris", List.of("Oscar")); + put("Celine", List.of("Priya")); + put("Diego", List.of("Qi")); + put("Elif", List.of("Rami")); + put("Farah", List.of("Sven")); + put("Giorgio", List.of("Tomoko")); + put("Hana", List.of("Umar")); + put("Ian", List.of("Vera")); + put("Jing", List.of("Wyatt")); + put("Kaito", List.of("Xia")); + put("Leila", List.of("Yassin")); + put("Mateo", List.of("Zara")); + put("Nia", List.of("Antonio")); + put("Oscar", List.of("Bianca")); + put("Priya", List.of("Cai")); + put("Qi", List.of("Dimitri")); + put("Rami", List.of("Ewa")); + put("Sven", List.of("Fabio")); + put("Tomoko", List.of("Gabriela")); + put("Umar", List.of("Helena")); + put("Vera", List.of("Igor")); + put("Wyatt", List.of("Jun")); + put("Xia", List.of("Kim")); + put("Yassin", List.of("Lucia")); + put("Zara", List.of("Mohammed")); + } + }; + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Lucia", "Jun")).isEqualTo(14); + } + + @Disabled("Remove to run test") + @Test + public void testComplexGraphSomeShortcutsCrossDownAndCrossUpCousinsSeveralTimesRemovedWithUnrelatedFamilyTree() { + Map> familyTree = new HashMap<>() { + { + put("Aiko", List.of("Bao", "Carlos")); + put("Bao", List.of("Dalia")); + put("Carlos", List.of("Fatima", "Gustavo")); + put("Dalia", List.of("Hassan", "Isla")); + put("Fatima", List.of("Khadija", "Liam")); + put("Gustavo", List.of("Mina")); + put("Hassan", List.of("Noah", "Olga")); + put("Isla", List.of("Pedro")); + put("Javier", List.of("Quynh", "Ravi")); + put("Khadija", List.of("Sofia")); + put("Liam", List.of("Tariq", "Uma")); + put("Mina", List.of("Viktor", "Wang")); + put("Noah", List.of("Xiomara")); + put("Olga", List.of("Yuki")); + put("Pedro", List.of("Zane", "Aditi")); + put("Quynh", List.of("Boris")); + put("Ravi", List.of("Celine")); + put("Sofia", List.of("Diego", "Elif")); + put("Tariq", List.of("Farah")); + put("Uma", List.of("Giorgio")); + put("Viktor", List.of("Hana", "Ian")); + put("Wang", List.of("Jing")); + put("Xiomara", List.of("Kaito")); + put("Yuki", List.of("Leila")); + put("Zane", List.of("Mateo")); + put("Aditi", List.of("Nia")); + put("Boris", List.of("Oscar")); + put("Celine", List.of("Priya")); + put("Diego", List.of("Qi")); + put("Elif", List.of("Rami")); + put("Farah", List.of("Sven")); + put("Giorgio", List.of("Tomoko")); + put("Hana", List.of("Umar")); + put("Ian", List.of("Vera")); + put("Jing", List.of("Wyatt")); + put("Kaito", List.of("Xia")); + put("Leila", List.of("Yassin")); + put("Mateo", List.of("Zara")); + put("Nia", List.of("Antonio")); + put("Oscar", List.of("Bianca")); + put("Priya", List.of("Cai")); + put("Qi", List.of("Dimitri")); + put("Rami", List.of("Ewa")); + put("Sven", List.of("Fabio")); + put("Tomoko", List.of("Gabriela")); + put("Umar", List.of("Helena")); + put("Vera", List.of("Igor")); + put("Wyatt", List.of("Jun")); + put("Xia", List.of("Kim")); + put("Yassin", List.of("Lucia")); + put("Zara", List.of("Mohammed")); + } + }; + RelativeDistance rd = new RelativeDistance(familyTree); + assertThat(rd.degreeOfSeparation("Wyatt", "Xia")).isEqualTo(12); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 2ff1e9a14..713a0c583 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -118,6 +118,7 @@ include 'practice:raindrops' include 'practice:rational-numbers' include 'practice:react' include 'practice:rectangles' +include 'practice:relative-distance' include 'practice:resistor-color' include 'practice:resistor-color-duo' include 'practice:resistor-color-trio' From c04e72072cc6e270fa2a85b7187ed026eeaa1de0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:26:12 +0800 Subject: [PATCH 22/67] Bump DavidAnson/markdownlint-cli2-action from 19.1.0 to 20.0.0 (#2948) Bumps [DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action) from 19.1.0 to 20.0.0. - [Release notes](https://github.com/davidanson/markdownlint-cli2-action/releases) - [Commits](https://github.com/davidanson/markdownlint-cli2-action/compare/05f32210e84442804257b2a6f20b273450ec8265...992badcdf24e3b8eb7e87ff9287fe931bcb00c6e) --- updated-dependencies: - dependency-name: DavidAnson/markdownlint-cli2-action dependency-version: 20.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/markdown.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index 9bcd9dc76..abe9683a5 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -19,4 +19,4 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Lint markdown - uses: DavidAnson/markdownlint-cli2-action@05f32210e84442804257b2a6f20b273450ec8265 + uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e From 276d466462fde3297661b47bc98a2eb42a90f092 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Sat, 7 Jun 2025 17:42:24 +0530 Subject: [PATCH 23/67] Fix lint issues (#2951) --- POLICIES.md | 4 ++-- concepts/inheritance/about.md | 2 +- concepts/switch-statement/about.md | 4 ++-- exercises/concept/log-levels/.docs/hints.md | 4 ++-- exercises/concept/remote-control-competition/.docs/hints.md | 2 +- exercises/concept/salary-calculator/.docs/hints.md | 2 +- exercises/concept/wizards-and-warriors/.docs/hints.md | 4 ++-- exercises/practice/hello-world/.docs/instructions.append.md | 4 ++-- reference/contributing-to-practice-exercises.md | 2 +- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/POLICIES.md b/POLICIES.md index 14a857e2a..09d803f04 100644 --- a/POLICIES.md +++ b/POLICIES.md @@ -51,7 +51,7 @@ References: [[1](https://github.com/exercism/java/issues/177#issuecomment-261291 > throw new UnsupportedOperationException("Delete this statement and write your own implementation."); > ``` > -> - Exercises of difficulty 5 or higher: copy the StubTemplate.java file (provided [here](https://github.com/exercism/java/blob/main/resources/exercise-template/src/main/java/ExerciseName.java)) and rename it to fit the exercise. For example, for the exercise linked-list the file could be named LinkedList.java. Then either (1) add hints to the hints.md file (which gets merged into the README.md for the exercise) or (2) provide stubs as above for exercises that demand complicated method signatures. +> - Exercises of difficulty 5 or higher: copy the StubTemplate.java file (provided in [this template file](https://github.com/exercism/java/blob/main/resources/exercise-template/src/main/java/ExerciseName.java)) and rename it to fit the exercise. For example, for the exercise linked-list the file could be named LinkedList.java. Then either (1) add hints to the hints.md file (which gets merged into the README.md for the exercise) or (2) provide stubs as above for exercises that demand complicated method signatures. References: [[1](https://github.com/exercism/java/issues/178)], [[2](https://github.com/exercism/java/pull/683#discussion_r125506930)], [[3](https://github.com/exercism/java/issues/977)], [[4](https://github.com/exercism/java/issues/1721)] @@ -108,7 +108,7 @@ References: [[1](https://github.com/exercism/java/issues/365#issuecomment-292533 ### Good first issues -> Aim to keep 10-20 small and straightforward issues open at any given time. Identify any such issues by applying the "good first issue" label. You can view the current list of these issues [here](https://github.com/exercism/java/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). +> Aim to keep 10-20 small and straightforward issues open at any given time. Identify any such issues by applying the "good first issue" label. You can view the current list of labeled issues [on GitHub](https://github.com/exercism/java/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). References: [[1](https://github.com/exercism/java/issues/220#issue-196447088)], [[2](https://github.com/exercism/java/issues/1669)] diff --git a/concepts/inheritance/about.md b/concepts/inheritance/about.md index 4d1698bb8..bd6dd590e 100644 --- a/concepts/inheritance/about.md +++ b/concepts/inheritance/about.md @@ -18,7 +18,7 @@ There are four access modifiers: - `protected` - default (No keyword required) -You can read more about them [here][access-modifiers] +You can read more about them [in this article][access-modifiers] ## Inheritance vs Composition diff --git a/concepts/switch-statement/about.md b/concepts/switch-statement/about.md index 9d53f54cb..2c89c5c04 100644 --- a/concepts/switch-statement/about.md +++ b/concepts/switch-statement/about.md @@ -145,7 +145,7 @@ Starting with Java 14 (available as a preview before in Java 12 and 13) it is po However if you use the new `->` notation it must be followed by either: a single statement/expression, a `throw` statement or a `{}` block. No more confusion! -You can find more information on enhanced switch [here][switch1], [here][switch2] and on the [oracle documentation][oracle-doc]. +You can find more information on enhanced switch in [this article][switch1] and [this one][switch2], along with the official [Oracle documentation][oracle-doc]. In addition, a feature called `Guarded Patterns` was added in Java 21, which allows you to do checks in the case label itself. @@ -160,7 +160,7 @@ return switch (day) { }; ``` -You can find more information on the switch expression on Java 21 [here][switch-on-Java-21] +You can find more information on the switch expression on Java 21 in [this blog][switch-on-Java-21] [yield-keyword]: https://www.codejava.net/java-core/the-java-language/yield-keyword-in-java [switch1]: https://www.vojtechruzicka.com/java-enhanced-switch/ diff --git a/exercises/concept/log-levels/.docs/hints.md b/exercises/concept/log-levels/.docs/hints.md index 8453667c5..2e5bce849 100644 --- a/exercises/concept/log-levels/.docs/hints.md +++ b/exercises/concept/log-levels/.docs/hints.md @@ -7,12 +7,12 @@ ## 1. Get message from a log line - Different options to search for text in a string are explored in [this tutorial][tutorial-search-text-in-string]. -- How to split strings can be seen [here][tutorial-split-strings] +- How to split strings can be seen in [this tutorial][tutorial-split-strings] - Removing white space is [built-in][tutorial-trim-white-space]. ## 2. Get log level from a log line -- Changing a `String`'s casing is explored [here][tutorial-changing-case-upper] and [here][tutorial-changing-case-lower]. +- Changing a `String`'s casing is explored in [this changing to upper case][tutorial-changing-case-upper] and [this changing to lower case][tutorial-changing-case-lower] tutorial. ## 3. Reformat a log line diff --git a/exercises/concept/remote-control-competition/.docs/hints.md b/exercises/concept/remote-control-competition/.docs/hints.md index 1b48b5515..fbd4ebfa7 100644 --- a/exercises/concept/remote-control-competition/.docs/hints.md +++ b/exercises/concept/remote-control-competition/.docs/hints.md @@ -15,7 +15,7 @@ ## 3. Allow the production cars to be ranked - See [this discussion][sort] of sorting. -- See [here][comparable] for default comparison of objects. +- See [Comparable's Javadocs][comparable] for default comparison of objects. [interfaces]: https://docs.oracle.com/javase/tutorial/java/concepts/interface.html [sort]: https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html diff --git a/exercises/concept/salary-calculator/.docs/hints.md b/exercises/concept/salary-calculator/.docs/hints.md index 1c74c7964..68df6d809 100644 --- a/exercises/concept/salary-calculator/.docs/hints.md +++ b/exercises/concept/salary-calculator/.docs/hints.md @@ -2,7 +2,7 @@ ## General -- Refer to examples [here][ternary-operator-first] and [here][ternary-operator-second] for guidance on using _ternary operators_. +- Refer to examples in [this article][ternary-operator-first] and [this one][ternary-operator-second] for guidance on using _ternary operators_. ## 1. Determine the salary multiplier diff --git a/exercises/concept/wizards-and-warriors/.docs/hints.md b/exercises/concept/wizards-and-warriors/.docs/hints.md index ab6871b2f..d9d52fc58 100644 --- a/exercises/concept/wizards-and-warriors/.docs/hints.md +++ b/exercises/concept/wizards-and-warriors/.docs/hints.md @@ -13,7 +13,7 @@ The whole inheritance concept has a lot to do with the concepts around [overridi ## 2. Describe a Warrior - In Java, the `toString()` method is actually present inside the `Object` class (which is a superclass to all the classes in Java). - You can read more about it [here][object-class-java]. + You can read more about it at the official [Oracle documentation][object-class-java]. - To override this method inside your implementation class, you should have a method with same name i.e. `toString()` and same return type i.e. `String`. - The `toString()` method must be `public`. @@ -34,7 +34,7 @@ The whole inheritance concept has a lot to do with the concepts around [overridi ## 6. Describe a Wizard - In Java, the `toString()` method is actually present inside the `Object` class (which is a superclass to all the classes in Java). - You can read more about it [here][object-class-java]. + You can read more about it at the official [Oracle documentation][object-class-java]. - To override this method inside your implementation class, you should have a method with same name i.e. `toString()` and same return type i.e. `String`. - The `toString()` method must be `public`. diff --git a/exercises/practice/hello-world/.docs/instructions.append.md b/exercises/practice/hello-world/.docs/instructions.append.md index f70c88daa..f1580cb34 100644 --- a/exercises/practice/hello-world/.docs/instructions.append.md +++ b/exercises/practice/hello-world/.docs/instructions.append.md @@ -112,13 +112,13 @@ Seeing both kinds can be instructive and interesting. Mentors review submitted solutions for the track they've chosen to mentor to help users learn as much as possible. -To read more about mentoring, go to the following [link][Mentoring]. +To read more about mentoring, go to the [Exercism docs][Mentoring]. ### Contribute to Exercism The entire of Exercism is Open Source and is a labor of love for over 100 maintainers and many more contributors. -A starting point to jumping in and becoming a contributor can be found [here][Contributing]. +A starting point to jumping in and becoming a contributor can be found in the [Exercism contributing guide][Contributing]. [Tutorial]: #tutorial [Introduction]: #introduction diff --git a/reference/contributing-to-practice-exercises.md b/reference/contributing-to-practice-exercises.md index 327c68635..75b25ddd5 100644 --- a/reference/contributing-to-practice-exercises.md +++ b/reference/contributing-to-practice-exercises.md @@ -19,7 +19,7 @@ This can just be a Pull Request with an empty commit that states which new exerc The Java specific details you need to know about adding an exercise are: - Please add an entry to the `exercises` array in `config.json`. - You can find details about what should be in that entry [here][docs-building-config-json]. + You can find details about what should be in that entry at the [Exercism docs][docs-building-config-json]. You can also look at other entries in `config.json` as examples and try to mimic them. - Please add an entry for your exercise to `settings.gradle`. From cf0007187a909f1a532074c01be301c613717e6e Mon Sep 17 00:00:00 2001 From: Bahaa Mohamed <107966806+BahaaMohamed98@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:59:18 +0300 Subject: [PATCH 24/67] Fix typo in datetime concept (#2953) `addMonths` and `addYears` should be `plusMonths` and `plusYears`. --- concepts/datetime/about.md | 6 +++--- concepts/datetime/introduction.md | 2 +- .../concept/booking-up-for-beauty/.docs/introduction.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/concepts/datetime/about.md b/concepts/datetime/about.md index 2724d5cdb..8ab1fbad6 100644 --- a/concepts/datetime/about.md +++ b/concepts/datetime/about.md @@ -47,13 +47,13 @@ These methods return a _new_ `LocalDate` instance and do not update the existing ```java LocalDate date = LocalDate.of(2007, 12, 3); -date.addDays(3); +date.plusDays(3); // => 2007-12-06 -date.addMonths(1); +date.plusMonths(1); // => 2008-01-03 -date.addYears(1); +date.plusYears(1); // => 2008-12-03 ``` diff --git a/concepts/datetime/introduction.md b/concepts/datetime/introduction.md index afb15b114..2b9513189 100644 --- a/concepts/datetime/introduction.md +++ b/concepts/datetime/introduction.md @@ -41,7 +41,7 @@ These methods return a _new_ `LocalDate` instance and do not update the existing ```java LocalDate date = LocalDate.of(2007, 12, 3); -date.addDays(3); +date.plusDays(3); // => 2007-12-06 ``` diff --git a/exercises/concept/booking-up-for-beauty/.docs/introduction.md b/exercises/concept/booking-up-for-beauty/.docs/introduction.md index 2baa59e0f..ed132331c 100644 --- a/exercises/concept/booking-up-for-beauty/.docs/introduction.md +++ b/exercises/concept/booking-up-for-beauty/.docs/introduction.md @@ -43,7 +43,7 @@ These methods return a _new_ `LocalDate` instance and do not update the existing ```java LocalDate date = LocalDate.of(2007, 12, 3); -date.addDays(3); +date.plusDays(3); // => 2007-12-06 ``` From 85e54aa51af42a7770441ce0b0d06ed030d8ed10 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Fri, 13 Jun 2025 19:07:00 +0530 Subject: [PATCH 25/67] Resync exercise docs (#2955) This resync the docs with those from the problem specification. Fixes the markdown linting errors introduced by the markdown linter upgrade. --- .../binary-search-tree/.docs/instructions.md | 23 ++++++ .../practice/dot-dsl/.docs/instructions.md | 2 +- .../eliuds-eggs/.docs/introduction.md | 2 +- exercises/practice/luhn/.docs/instructions.md | 45 ++++++----- exercises/practice/luhn/.docs/introduction.md | 4 +- .../practice/meetup/.docs/instructions.md | 2 +- .../phone-number/.docs/instructions.md | 2 +- .../saddle-points/.docs/instructions.md | 11 +-- .../simple-cipher/.docs/instructions.md | 78 +++++++------------ 9 files changed, 86 insertions(+), 83 deletions(-) diff --git a/exercises/practice/binary-search-tree/.docs/instructions.md b/exercises/practice/binary-search-tree/.docs/instructions.md index c9bbba5b9..7625220e9 100644 --- a/exercises/practice/binary-search-tree/.docs/instructions.md +++ b/exercises/practice/binary-search-tree/.docs/instructions.md @@ -19,29 +19,52 @@ All data in the left subtree is less than or equal to the current node's data, a For example, if we had a node containing the data 4, and we added the data 2, our tree would look like this: +![A graph with root node 4 and a single child node 2.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2.svg) + +```text 4 / 2 +``` If we then added 6, it would look like this: +![A graph with root node 4 and two child nodes 2 and 6.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2-6.svg) + +```text 4 / \ 2 6 +``` If we then added 3, it would look like this +![A graph with root node 4, two child nodes 2 and 6, and a grandchild node 3.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2-6-3.svg) + +```text 4 / \ 2 6 \ 3 +``` And if we then added 1, 5, and 7, it would look like this +![A graph with root node 4, two child nodes 2 and 6, and four grandchild nodes 1, 3, 5 and 7.](https://assets.exercism.org/images/exercises/binary-search-tree/tree-4-2-6-1-3-5-7.svg) + +```text 4 / \ / \ 2 6 / \ / \ 1 3 5 7 +``` + +## Credit + +The images were created by [habere-et-dispertire][habere-et-dispertire] using [PGF/TikZ][pgf-tikz] by Till Tantau. + +[habere-et-dispertire]: https://exercism.org/profiles/habere-et-dispertire +[pgf-tikz]: https://en.wikipedia.org/wiki/PGF/TikZ diff --git a/exercises/practice/dot-dsl/.docs/instructions.md b/exercises/practice/dot-dsl/.docs/instructions.md index b3a63996d..5e65ebef9 100644 --- a/exercises/practice/dot-dsl/.docs/instructions.md +++ b/exercises/practice/dot-dsl/.docs/instructions.md @@ -22,7 +22,7 @@ Write a Domain Specific Language similar to the Graphviz dot language. Our DSL is similar to the Graphviz dot language in that our DSL will be used to create graph data structures. However, unlike the DOT Language, our DSL will be an internal DSL for use only in our language. -More information about the difference between internal and external DSLs can be found [here][fowler-dsl]. +[Learn more about the difference between internal and external DSLs][fowler-dsl]. [dsl]: https://en.wikipedia.org/wiki/Domain-specific_language [dot-language]: https://en.wikipedia.org/wiki/DOT_(graph_description_language) diff --git a/exercises/practice/eliuds-eggs/.docs/introduction.md b/exercises/practice/eliuds-eggs/.docs/introduction.md index 819897480..2b2e5c43d 100644 --- a/exercises/practice/eliuds-eggs/.docs/introduction.md +++ b/exercises/practice/eliuds-eggs/.docs/introduction.md @@ -58,7 +58,7 @@ The position information encoding is calculated as follows: ### Decimal number on the display -16 +8 ### Actual eggs in the coop diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md index 5bbf007b0..df2e304a3 100644 --- a/exercises/practice/luhn/.docs/instructions.md +++ b/exercises/practice/luhn/.docs/instructions.md @@ -1,6 +1,6 @@ # Instructions -Determine whether a credit card number is valid according to the [Luhn formula][luhn]. +Determine whether a number is valid according to the [Luhn formula][luhn]. The number will be provided as a string. @@ -10,54 +10,59 @@ Strings of length 1 or less are not valid. Spaces are allowed in the input, but they should be stripped before checking. All other non-digit characters are disallowed. -### Example 1: valid credit card number +## Examples -```text -4539 3195 0343 6467 -``` +### Valid credit card number -The first step of the Luhn algorithm is to double every second digit, starting from the right. -We will be doubling +The number to be checked is `4539 3195 0343 6467`. + +The first step of the Luhn algorithm is to start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. ```text 4539 3195 0343 6467 ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (double these) ``` -If doubling the number results in a number greater than 9 then subtract 9 from the product. -The results of our doubling: +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: ```text 8569 6195 0383 3437 ``` -Then sum all of the digits: +Finally, we sum all digits. +If the sum is evenly divisible by 10, the original number is valid. ```text -8+5+6+9+6+1+9+5+0+3+8+3+3+4+3+7 = 80 +8 + 5 + 6 + 9 + 6 + 1 + 9 + 5 + 0 + 3 + 8 + 3 + 3 + 4 + 3 + 7 = 80 ``` -If the sum is evenly divisible by 10, then the number is valid. -This number is valid! +80 is evenly divisible by 10, so number `4539 3195 0343 6467` is valid! + +### Invalid Canadian SIN + +The number to be checked is `066 123 468`. -### Example 2: invalid credit card number +We start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. ```text -8273 1232 7352 0569 +066 123 478 + ↑ ↑ ↑ ↑ (double these) ``` -Double the second digits, starting from the right +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: ```text -7253 2262 5312 0539 +036 226 458 ``` -Sum the digits +We sum the digits: ```text -7+2+5+3+2+2+6+2+5+3+1+2+0+5+3+9 = 57 +0 + 3 + 6 + 2 + 2 + 6 + 4 + 5 + 8 = 36 ``` -57 is not evenly divisible by 10, so this number is not valid. +36 is not evenly divisible by 10, so number `066 123 478` is not valid! [luhn]: https://en.wikipedia.org/wiki/Luhn_algorithm diff --git a/exercises/practice/luhn/.docs/introduction.md b/exercises/practice/luhn/.docs/introduction.md index ec2bd709d..dee48006e 100644 --- a/exercises/practice/luhn/.docs/introduction.md +++ b/exercises/practice/luhn/.docs/introduction.md @@ -2,10 +2,10 @@ At the Global Verification Authority, you've just been entrusted with a critical assignment. Across the city, from online purchases to secure logins, countless operations rely on the accuracy of numerical identifiers like credit card numbers, bank account numbers, transaction codes, and tracking IDs. -The Luhn algorithm is a simple checksum formula used to ensure these numbers are valid and error-free. +The Luhn algorithm is a simple checksum formula used to help identify mistyped numbers. A batch of identifiers has just arrived on your desk. All of them must pass the Luhn test to ensure they're legitimate. -If any fail, they'll be flagged as invalid, preventing errors or fraud, such as incorrect transactions or unauthorized access. +If any fail, they'll be flagged as invalid, preventing mistakes such as incorrect transactions or failed account verifications. Can you ensure this is done right? The integrity of many services depends on you. diff --git a/exercises/practice/meetup/.docs/instructions.md b/exercises/practice/meetup/.docs/instructions.md index 000de2fd1..8b1bda5eb 100644 --- a/exercises/practice/meetup/.docs/instructions.md +++ b/exercises/practice/meetup/.docs/instructions.md @@ -2,7 +2,7 @@ Your task is to find the exact date of a meetup, given a month, year, weekday and week. -There are five week values to consider: `first`, `second`, `third`, `fourth`, `last`, `teenth`. +There are six week values to consider: `first`, `second`, `third`, `fourth`, `last`, `teenth`. For example, you might be asked to find the date for the meetup on the first Monday in January 2018 (January 1, 2018). diff --git a/exercises/practice/phone-number/.docs/instructions.md b/exercises/practice/phone-number/.docs/instructions.md index 62ba48e96..5d4d3739f 100644 --- a/exercises/practice/phone-number/.docs/instructions.md +++ b/exercises/practice/phone-number/.docs/instructions.md @@ -1,6 +1,6 @@ # Instructions -Clean up user-entered phone numbers so that they can be sent SMS messages. +Clean up phone numbers so that they can be sent SMS messages. The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. All NANP-countries share the same international country code: `1`. diff --git a/exercises/practice/saddle-points/.docs/instructions.md b/exercises/practice/saddle-points/.docs/instructions.md index c585568b4..f69cdab95 100644 --- a/exercises/practice/saddle-points/.docs/instructions.md +++ b/exercises/practice/saddle-points/.docs/instructions.md @@ -13,11 +13,12 @@ Or it might have one, or even several. Here is a grid that has exactly one candidate tree. ```text - 1 2 3 4 - |----------- -1 | 9 8 7 8 -2 | 5 3 2 4 <--- potential tree house at row 2, column 1, for tree with height 5 -3 | 6 6 7 1 + ↓ + 1 2 3 4 + |----------- + 1 | 9 8 7 8 +→ 2 |[5] 3 2 4 + 3 | 6 6 7 1 ``` - Row 2 has values 5, 3, 2, and 4. The largest value is 5. diff --git a/exercises/practice/simple-cipher/.docs/instructions.md b/exercises/practice/simple-cipher/.docs/instructions.md index 337857442..afd0b57da 100644 --- a/exercises/practice/simple-cipher/.docs/instructions.md +++ b/exercises/practice/simple-cipher/.docs/instructions.md @@ -1,66 +1,40 @@ # Instructions -Implement a simple shift cipher like Caesar and a more secure substitution cipher. +Create an implementation of the [Vigenère cipher][wiki]. +The Vigenère cipher is a simple substitution cipher. -## Step 1 +## Cipher terminology -"If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out. -If anyone wishes to decipher these, and get at their meaning, he must substitute the fourth letter of the alphabet, namely D, for A, and so with the others." -—Suetonius, Life of Julius Caesar +A cipher is an algorithm used to encrypt, or encode, a string. +The unencrypted string is called the _plaintext_ and the encrypted string is called the _ciphertext_. +Converting plaintext to ciphertext is called _encoding_ while the reverse is called _decoding_. -Ciphers are very straight-forward algorithms that allow us to render text less readable while still allowing easy deciphering. -They are vulnerable to many forms of cryptanalysis, but Caesar was lucky that his enemies were not cryptanalysts. +In a _substitution cipher_, each plaintext letter is replaced with a ciphertext letter which is computed with the help of a _key_. +(Note, it is possible for replacement letter to be the same as the original letter.) -The Caesar cipher was used for some messages from Julius Caesar that were sent afield. -Now Caesar knew that the cipher wasn't very good, but he had one ally in that respect: almost nobody could read well. -So even being a couple letters off was sufficient so that people couldn't recognize the few words that they did know. +## Encoding details -Your task is to create a simple shift cipher like the Caesar cipher. -This image is a great example of the Caesar cipher: +In this cipher, the key is a series of lowercase letters, such as `"abcd"`. +Each letter of the plaintext is _shifted_ or _rotated_ by a distance based on a corresponding letter in the key. +An `"a"` in the key means a shift of 0 (that is, no shift). +A `"b"` in the key means a shift of 1. +A `"c"` in the key means a shift of 2, and so on. -![Caesar cipher][img-caesar-cipher] +The first letter of the plaintext uses the first letter of the key, the second letter of the plaintext uses the second letter of the key and so on. +If you run out of letters in the key before you run out of letters in the plaintext, start over from the start of the key again. -For example: +If the key only contains one letter, such as `"dddddd"`, then all letters of the plaintext are shifted by the same amount (three in this example), which would make this the same as a rotational cipher or shift cipher (sometimes called a Caesar cipher). +For example, the plaintext `"iamapandabear"` would become `"ldpdsdqgdehdu"`. -Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu". -Obscure enough to keep our message secret in transit. +If the key only contains the letter `"a"` (one or more times), the shift distance is zero and the ciphertext is the same as the plaintext. -When "ldpdsdqgdehdu" is put into the decode function it would return the original "iamapandabear" letting your friend read your original message. +Usually the key is more complicated than that, though! +If the key is `"abcd"` then letters of the plaintext would be shifted by a distance of 0, 1, 2, and 3. +If the plaintext is `"hello"`, we need 5 shifts so the key would wrap around, giving shift distances of 0, 1, 2, 3, and 0. +Applying those shifts to the letters of `"hello"` we get `"hfnoo"`. -## Step 2 +## Random keys -Shift ciphers quickly cease to be useful when the opposition commander figures them out. -So instead, let's try using a substitution cipher. -Try amending the code to allow us to specify a key and use that for the shift distance. +If no key is provided, generate a key which consists of at least 100 random lowercase letters from the Latin alphabet. -Here's an example: - -Given the key "aaaaaaaaaaaaaaaaaa", encoding the string "iamapandabear" -would return the original "iamapandabear". - -Given the key "ddddddddddddddddd", encoding our string "iamapandabear" -would return the obscured "ldpdsdqgdehdu" - -In the example above, we've set a = 0 for the key value. -So when the plaintext is added to the key, we end up with the same message coming out. -So "aaaa" is not an ideal key. -But if we set the key to "dddd", we would get the same thing as the Caesar cipher. - -## Step 3 - -The weakest link in any cipher is the human being. -Let's make your substitution cipher a little more fault tolerant by providing a source of randomness and ensuring that the key contains only lowercase letters. - -If someone doesn't submit a key at all, generate a truly random key of at least 100 lowercase characters in length. - -## Extensions - -Shift ciphers work by making the text slightly odd, but are vulnerable to frequency analysis. -Substitution ciphers help that, but are still very vulnerable when the key is short or if spaces are preserved. -Later on you'll see one solution to this problem in the exercise "crypto-square". - -If you want to go farther in this field, the questions begin to be about how we can exchange keys in a secure way. -Take a look at [Diffie-Hellman on Wikipedia][dh] for one of the first implementations of this scheme. - -[img-caesar-cipher]: https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Caesar_cipher_left_shift_of_3.svg/320px-Caesar_cipher_left_shift_of_3.svg.png -[dh]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange +[wiki]: https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher From 2e0d76b3ece9d2dfd915b1627b1d9c11a32fbfae Mon Sep 17 00:00:00 2001 From: Bahaa Mohamed <107966806+BahaaMohamed98@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:13:39 +0300 Subject: [PATCH 26/67] Distinguish between errors & exceptions (#2954) This is to try to describe the difference between errors and exceptions more accurately. --- concepts/exceptions/.meta/config.json | 2 +- concepts/exceptions/about.md | 30 ++++++++++--------- concepts/exceptions/introduction.md | 30 ++++++++++--------- .../.docs/introduction.md | 30 ++++++++++--------- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/concepts/exceptions/.meta/config.json b/concepts/exceptions/.meta/config.json index 1c6bc8bb3..ef26a0098 100644 --- a/concepts/exceptions/.meta/config.json +++ b/concepts/exceptions/.meta/config.json @@ -1,5 +1,5 @@ { "blurb": "Exceptions are thrown when an error that needs special handling occurs.", "authors": ["sanderploegsma"], - "contributors": [] + "contributors": ["BahaaMohamed98"] } diff --git a/concepts/exceptions/about.md b/concepts/exceptions/about.md index 96e2bc4a5..d3a4f5946 100644 --- a/concepts/exceptions/about.md +++ b/concepts/exceptions/about.md @@ -8,11 +8,12 @@ An exception is an event that occurs during the execution of a program that disr Exceptions are raised explicitly in Java, and the act of raising an exception is called _throwing an exception_. The act of handling an exception is called _catching an exception_. -Java distinguishes three types of exceptions: +In Java, all exceptions are subclasses of the `Exception` class, which itself is a subclass of `Throwable`. + +Java distinguishes two types of exceptions: 1. Checked exceptions 2. Unchecked exceptions -3. Errors ### Checked exceptions @@ -21,7 +22,7 @@ An example of a checked exception is the `FileNotFoundException` which occurs wh This type of exception is checked at compile-time: methods that throw checked exceptions should specify this in their method signature, and code calling a method that might throw a checked exception is required to handle it or the code will not compile. -All exceptions in Java that do not inherit from `RuntimeException` or `Error` are considered checked exceptions. +All checked exceptions are subclasses of `Exception` that do not extend `RuntimeException`. ### Unchecked exceptions @@ -30,17 +31,7 @@ An example of an unchecked exception is the `NullPointerException` which occurs This type of exception is not checked at compile-time: methods that throw unchecked exceptions are not required to specify this in their method signature, and code calling a method that might throw an unchecked exception is not required to handle it. -All exceptions in Java that inherit from `RuntimeException` are considered unchecked exceptions. - -### Errors - -_Errors_ are exceptional conditions that are external to an application. -An example of an error is the `OutOfMemoryError` which occurs when an application is trying to use more memory than is available on the system. - -Like unchecked exceptions, errors are not checked at compile-time. -They are not usually thrown from application code. - -All exceptions in Java that inherit from `Error` are considered errors. +All unchecked exceptions inherit from `RuntimeException`, which itself is an extension of `Exception`. ## Throwing exceptions @@ -135,6 +126,17 @@ Withdrawal failed: Cannot withdraw a negative amount Current balance: 5.0 ``` +## Errors + +Java also has a separate category called _Errors_ which are serious problems that are external to an application. +An example of an error is the `OutOfMemoryError` which occurs when an application is trying to use more memory than is available on the system. + +Like unchecked exceptions, errors are not checked at compile-time. +The difference is that they represent system level problems and are generally thrown by the Java Virtual machine or environment instead of the application. +Applications should generally not attempt to catch or handle them. + +All errors in Java inherit from the `Error` class. + ## When not to use exceptions As stated previously, exceptions are events that disrupt the normal flow of instructions, and are used to handle _exceptional events_. diff --git a/concepts/exceptions/introduction.md b/concepts/exceptions/introduction.md index 586f6f1ea..c50113113 100644 --- a/concepts/exceptions/introduction.md +++ b/concepts/exceptions/introduction.md @@ -8,11 +8,12 @@ An exception is an event that occurs during the execution of a program that disr Exceptions are raised explicitly in Java, and the act of raising an exception is called _throwing an exception_. The act of handling an exception is called _catching an exception_. -Java distinguishes three types of exceptions: +In Java, all exceptions are subclasses of the `Exception` class, which itself is a subclass of `Throwable`. + +Java distinguishes two types of exceptions: 1. Checked exceptions 2. Unchecked exceptions -3. Errors ### Checked exceptions @@ -21,7 +22,7 @@ An example of a checked exception is the `FileNotFoundException` which occurs wh This type of exception is checked at compile-time: methods that throw checked exceptions should specify this in their method signature, and code calling a method that might throw a checked exception is required to handle it or the code will not compile. -All exceptions in Java that do not inherit from `RuntimeException` or `Error` are considered checked exceptions. +All checked exceptions are subclasses of `Exception` that do not extend `RuntimeException`. ### Unchecked exceptions @@ -30,17 +31,7 @@ An example of an unchecked exception is the `NullPointerException` which occurs This type of exception is not checked at compile-time: methods that throw unchecked exceptions are not required to specify this in their method signature, and code calling a method that might throw an unchecked exception is not required to handle it. -All exceptions in Java that inherit from `RuntimeException` are considered unchecked exceptions. - -### Errors - -_Errors_ are exceptional conditions that are external to an application. -An example of an error is the `OutOfMemoryError` which occurs when an application is trying to use more memory than is available on the system. - -Like unchecked exceptions, errors are not checked at compile-time. -They are not usually thrown from application code. - -All exceptions in Java that inherit from `Error` are considered errors. +All unchecked exceptions inherit from `RuntimeException`, which itself is an extension of `Exception`. ## Throwing exceptions @@ -134,3 +125,14 @@ Withdrawing -10.0 Withdrawal failed: Cannot withdraw a negative amount Current balance: 5.0 ``` + +## Errors + +Java also has a separate category called _Errors_ which are serious problems that are external to an application. +An example of an error is the `OutOfMemoryError` which occurs when an application is trying to use more memory than is available on the system. + +Like unchecked exceptions, errors are not checked at compile-time. +The difference is that they represent system level problems and are generally thrown by the Java Virtual machine or environment instead of the application. +Applications should generally not attempt to catch or handle them. + +All errors in Java inherit from the `Error` class. diff --git a/exercises/concept/calculator-conundrum/.docs/introduction.md b/exercises/concept/calculator-conundrum/.docs/introduction.md index e9b19e65f..49103a297 100644 --- a/exercises/concept/calculator-conundrum/.docs/introduction.md +++ b/exercises/concept/calculator-conundrum/.docs/introduction.md @@ -10,11 +10,12 @@ An exception is an event that occurs during the execution of a program that disr Exceptions are raised explicitly in Java, and the act of raising an exception is called _throwing an exception_. The act of handling an exception is called _catching an exception_. -Java distinguishes three types of exceptions: +In Java, all exceptions are subclasses of the `Exception` class, which itself is a subclass of `Throwable`. + +Java distinguishes two types of exceptions: 1. Checked exceptions 2. Unchecked exceptions -3. Errors #### Checked exceptions @@ -23,7 +24,7 @@ An example of a checked exception is the `FileNotFoundException` which occurs wh This type of exception is checked at compile-time: methods that throw checked exceptions should specify this in their method signature, and code calling a method that might throw a checked exception is required to handle it or the code will not compile. -All exceptions in Java that do not inherit from `RuntimeException` or `Error` are considered checked exceptions. +All checked exceptions are subclasses of `Exception` that do not extend `RuntimeException`. #### Unchecked exceptions @@ -32,17 +33,7 @@ An example of an unchecked exception is the `NullPointerException` which occurs This type of exception is not checked at compile-time: methods that throw unchecked exceptions are not required to specify this in their method signature, and code calling a method that might throw an unchecked exception is not required to handle it. -All exceptions in Java that inherit from `RuntimeException` are considered unchecked exceptions. - -#### Errors - -_Errors_ are exceptional conditions that are external to an application. -An example of an error is the `OutOfMemoryError` which occurs when an application is trying to use more memory than is available on the system. - -Like unchecked exceptions, errors are not checked at compile-time. -They are not usually thrown from application code. - -All exceptions in Java that inherit from `Error` are considered errors. +All unchecked exceptions inherit from `RuntimeException`, which itself is an extension of `Exception`. ### Throwing exceptions @@ -136,3 +127,14 @@ Withdrawing -10.0 Withdrawal failed: Cannot withdraw a negative amount Current balance: 5.0 ``` + +### Errors + +Java also has a separate category called _Errors_ which are serious problems that are external to an application. +An example of an error is the `OutOfMemoryError` which occurs when an application is trying to use more memory than is available on the system. + +Like unchecked exceptions, errors are not checked at compile-time. +The difference is that they represent system level problems and are generally thrown by the Java Virtual machine or environment instead of the application. +Applications should generally not attempt to catch or handle them. + +All errors in Java inherit from the `Error` class. From 42e9a5c9f7e06833bc17f3f78cd91a7c34d72f18 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Fri, 20 Jun 2025 16:40:44 +0530 Subject: [PATCH 27/67] Add GitHub actions to resync (#2950) --- .github/workflows/configlet-sync.yml | 93 ++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/configlet-sync.yml diff --git a/.github/workflows/configlet-sync.yml b/.github/workflows/configlet-sync.yml new file mode 100644 index 000000000..6373e5d24 --- /dev/null +++ b/.github/workflows/configlet-sync.yml @@ -0,0 +1,93 @@ +name: Configlet Auto Sync + +on: + workflow_dispatch: + schedule: + - cron: '0 0 15 * *' + +jobs: + sync-docs-metadata: + timeout-minutes: 10 + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch configlet + run: ./bin/fetch-configlet + + - name: Run configlet sync for files + run: ./bin/configlet sync --docs --metadata --filepaths -u -y + + - name: Create pull request if changes + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "🤖 Auto-sync docs, metadata, and filepaths" + title: "🤖 Configlet sync: docs, metadata, and filepaths" + body: | + This PR was generated automatically by a scheduled workflow. + + It includes updates from `configlet sync` for: + - 📄 Documentation + - 🧭 Metadata + - 🗂️ Filepaths + + Please review and merge if everything looks good! + branch: configlet-auto-sync + delete-branch: true + + check-test-sync: + timeout-minutes: 10 + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch configlet + run: ./bin/fetch-configlet + + - name: Run configlet sync for test and capture output + id: sync_test + run: | + configlet_raw_output="$(./bin/configlet sync --tests | tee .github/sync-test-output.txt)" + + echo "configlet output:" + echo "$configlet_raw_output" + + echo '```' > .github/sync-test-output.txt + echo "$configlet_raw_output" >> .github/sync-test-output.txt + echo '```' >> .github/sync-test-output.txt + + echo "output<> "$GITHUB_OUTPUT" + echo "$configlet_raw_output" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - name: Find existing issue for test sync + id: find_issue + if: ${{ !contains(steps.sync_test.outputs.output, 'Every exercise has up-to-date tests!') }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ISSUE_TITLE="🚨 configlet sync --test found unsynced tests" + ISSUE_DATA=$(gh issue list --search "is:issue is:open in:title \"${ISSUE_TITLE}\" repo:${{ github.repository }}" --json number,title --jq '.[0]') + + if [ -z "$ISSUE_DATA" ]; then + echo "No open issue found with title: '${ISSUE_TITLE}'. A new one will be created." + echo "issue_number=" >> $GITHUB_OUTPUT + else + ISSUE_NUMBER=$(echo "$ISSUE_DATA" | jq -r '.number') + echo "Found existing issue number: $ISSUE_NUMBER for title: '$ISSUE_TITLE'" + echo "issue_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT + fi + + - name: Create or Update issue if tests are not synced + if: ${{ !contains(steps.sync_test.outputs.output, 'Every exercise has up-to-date tests!') }} + uses: peter-evans/create-issue-from-file@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + title: "🚨 configlet sync --test found unsynced tests" + content-filepath: .github/sync-test-output.txt + issue-number: ${{ steps.find_issue.outputs.issue_number || '' }} From bb0bca8402a61249f89197f5f7e5a898e60c0edf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 19:14:03 +0800 Subject: [PATCH 28/67] =?UTF-8?q?=F0=9F=A4=96=20Auto-sync=20docs,=20metada?= =?UTF-8?q?ta,=20and=20filepaths=20(#2958)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../protein-translation/.docs/instructions.md | 47 ++++++++----------- .../practice/simple-cipher/.meta/config.json | 2 +- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/exercises/practice/protein-translation/.docs/instructions.md b/exercises/practice/protein-translation/.docs/instructions.md index 44880802c..35c953b11 100644 --- a/exercises/practice/protein-translation/.docs/instructions.md +++ b/exercises/practice/protein-translation/.docs/instructions.md @@ -1,36 +1,17 @@ # Instructions -Translate RNA sequences into proteins. +Your job is to translate RNA sequences into proteins. -RNA can be broken into three-nucleotide sequences called codons, and then translated to a protein like so: +RNA strands are made up of three-nucleotide sequences called **codons**. +Each codon translates to an **amino acid**. +When joined together, those amino acids make a protein. -RNA: `"AUGUUUUCU"` => translates to - -Codons: `"AUG", "UUU", "UCU"` -=> which become a protein with the following sequence => - -Protein: `"Methionine", "Phenylalanine", "Serine"` - -There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise. -If it works for one codon, the program should work for all of them. -However, feel free to expand the list in the test suite to include them all. - -There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated. - -All subsequent codons after are ignored, like this: - -RNA: `"AUGUUUUCUUAAAUG"` => - -Codons: `"AUG", "UUU", "UCU", "UAA", "AUG"` => - -Protein: `"Methionine", "Phenylalanine", "Serine"` - -Note the stop codon `"UAA"` terminates the translation and the final methionine is not translated into the protein sequence. - -Below are the codons and resulting amino acids needed for the exercise. +In the real world, there are 64 codons, which in turn correspond to 20 amino acids. +However, for this exercise, you’ll only use a few of the possible 64. +They are listed below: | Codon | Amino Acid | -| :----------------- | :------------ | +| ------------------ | ------------- | | AUG | Methionine | | UUU, UUC | Phenylalanine | | UUA, UUG | Leucine | @@ -40,6 +21,18 @@ Below are the codons and resulting amino acids needed for the exercise. | UGG | Tryptophan | | UAA, UAG, UGA | STOP | +For example, the RNA string “AUGUUUUCU” has three codons: “AUG”, “UUU” and “UCU”. +These map to Methionine, Phenylalanine, and Serine. + +## “STOP” Codons + +You’ll note from the table above that there are three **“STOP” codons**. +If you encounter any of these codons, ignore the rest of the sequence — the protein is complete. + +For example, “AUGUUUUCUUAAAUG” contains a STOP codon (“UAA”). +Once we reach that point, we stop processing. +We therefore only consider the part before it (i.e. “AUGUUUUCU”), not any further codons after it (i.e. “AUG”). + Learn more about [protein translation on Wikipedia][protein-translation]. [protein-translation]: https://en.wikipedia.org/wiki/Translation_(biology) diff --git a/exercises/practice/simple-cipher/.meta/config.json b/exercises/practice/simple-cipher/.meta/config.json index 6f7ac09b1..de6b37f39 100644 --- a/exercises/practice/simple-cipher/.meta/config.json +++ b/exercises/practice/simple-cipher/.meta/config.json @@ -34,7 +34,7 @@ "build.gradle" ] }, - "blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher.", + "blurb": "Implement the Vigenère cipher, a simple substitution cipher.", "source": "Substitution Cipher at Wikipedia", "source_url": "https://en.wikipedia.org/wiki/Substitution_cipher" } From bb2b8f3c264dac0edb6d5effaaac56710bcbaa44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jegors=20=C4=8Cemisovs?= Date: Sat, 21 Jun 2025 04:59:33 +0300 Subject: [PATCH 29/67] Add test case for Bird Watcher exercise (#2956) Add a test to `hasDayWithoutBirds` for when the last day has zero visits. This checks that the solution correctly checks the entire array. See also a the forum post https://forum.exercism.org/t/bird-watcher-in-java-should-we-add-a-test-case/17933/16. Also make `lastWeek` final in the test, since the field isn't meant to change in the test. Co-authored-by: Jegors Cemisovs --- .../bird-watcher/src/test/java/BirdWatcherTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/exercises/concept/bird-watcher/src/test/java/BirdWatcherTest.java b/exercises/concept/bird-watcher/src/test/java/BirdWatcherTest.java index 80ff5f8e2..5b1746082 100644 --- a/exercises/concept/bird-watcher/src/test/java/BirdWatcherTest.java +++ b/exercises/concept/bird-watcher/src/test/java/BirdWatcherTest.java @@ -16,7 +16,7 @@ public class BirdWatcherTest { private static final int TODAY = 4; private BirdWatcher birdWatcher; - private int lastWeek[] = {DAY1, DAY2, DAY3, DAY4, DAY5, DAY6, TODAY}; + private final int[] lastWeek = {DAY1, DAY2, DAY3, DAY4, DAY5, DAY6, TODAY}; @BeforeEach public void setUp() { @@ -61,6 +61,13 @@ public void itShouldNotHaveDaysWithoutBirds() { assertThat(birdWatcher.hasDayWithoutBirds()).isFalse(); } + @Test + @Tag("task:4") + @DisplayName("The hasDayWithoutBirds method returns true if the last day has zero visits") + public void itHasLastDayWithoutBirds() { + birdWatcher = new BirdWatcher(new int[]{1, 2, 5, 3, 7, 8, 0}); + assertThat(birdWatcher.hasDayWithoutBirds()).isTrue(); + } @Test @Tag("task:5") From fe71fa3b04dcaf3a35652b14e5b213e696e89e83 Mon Sep 17 00:00:00 2001 From: Wolfgang <53096914+wolmoe@users.noreply.github.com> Date: Sat, 21 Jun 2025 15:40:39 +0200 Subject: [PATCH 30/67] Fix spelling in classes concept introduction (#2960) --- concepts/classes/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/classes/introduction.md b/concepts/classes/introduction.md index 7a488babe..08c04a5aa 100644 --- a/concepts/classes/introduction.md +++ b/concepts/classes/introduction.md @@ -33,7 +33,7 @@ class Car { ``` One can optionally assign an initial value to a field. -If a field does _not_ specify an initial value, it wll be set to its type's default value. +If a field does _not_ specify an initial value, it will be set to its type's default value. An instance's field values can be accessed and updated using dot-notation. ```java From fac46204697344c78f0c3fbbffa14f25ef6f383b Mon Sep 17 00:00:00 2001 From: Carlos Uriel Date: Fri, 27 Jun 2025 06:12:19 -0600 Subject: [PATCH 31/67] Add piecing it together practice exercise (#2961) --- config.json | 12 + .../piecing-it-together/.docs/instructions.md | 41 +++ .../piecing-it-together/.docs/introduction.md | 6 + .../piecing-it-together/.meta/config.json | 22 ++ .../.meta/src/reference/java/JigsawInfo.java | 140 ++++++++++ .../src/reference/java/PiecingItTogether.java | 135 ++++++++++ .../piecing-it-together/.meta/tests.toml | 31 +++ .../practice/piecing-it-together/build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../practice/piecing-it-together/gradlew | 249 ++++++++++++++++++ .../practice/piecing-it-together/gradlew.bat | 92 +++++++ .../src/main/java/JigsawInfo.java | 140 ++++++++++ .../src/main/java/PiecingItTogether.java | 5 + .../src/test/java/PiecingItTogetherTest.java | 167 ++++++++++++ exercises/settings.gradle | 1 + 16 files changed, 1072 insertions(+) create mode 100644 exercises/practice/piecing-it-together/.docs/instructions.md create mode 100644 exercises/practice/piecing-it-together/.docs/introduction.md create mode 100644 exercises/practice/piecing-it-together/.meta/config.json create mode 100644 exercises/practice/piecing-it-together/.meta/src/reference/java/JigsawInfo.java create mode 100644 exercises/practice/piecing-it-together/.meta/src/reference/java/PiecingItTogether.java create mode 100644 exercises/practice/piecing-it-together/.meta/tests.toml create mode 100644 exercises/practice/piecing-it-together/build.gradle create mode 100644 exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.properties create mode 100755 exercises/practice/piecing-it-together/gradlew create mode 100644 exercises/practice/piecing-it-together/gradlew.bat create mode 100644 exercises/practice/piecing-it-together/src/main/java/JigsawInfo.java create mode 100644 exercises/practice/piecing-it-together/src/main/java/PiecingItTogether.java create mode 100644 exercises/practice/piecing-it-together/src/test/java/PiecingItTogetherTest.java diff --git a/config.json b/config.json index 3d1ff49af..1623d2b25 100644 --- a/config.json +++ b/config.json @@ -1307,6 +1307,18 @@ ], "difficulty": 6 }, + { + "slug": "piecing-it-together", + "name": "Piecing It Together", + "uuid": "be303729-ad8a-4f4c-a235-6828a6734f05", + "practices": [], + "prerequisites": [ + "exceptions", + "for-loops", + "if-else-statements" + ], + "difficulty": 6 + }, { "slug": "queen-attack", "name": "Queen Attack", diff --git a/exercises/practice/piecing-it-together/.docs/instructions.md b/exercises/practice/piecing-it-together/.docs/instructions.md new file mode 100644 index 000000000..c0c966592 --- /dev/null +++ b/exercises/practice/piecing-it-together/.docs/instructions.md @@ -0,0 +1,41 @@ +# Instructions + +Given partial information about a jigsaw puzzle, add the missing pieces. + +If the information is insufficient to complete the details, or if given parts are in contradiction, the user should be notified. + +The full information about the jigsaw puzzle contains the following parts: + +- `pieces`: Total number of pieces +- `border`: Number of border pieces +- `inside`: Number of inside (non-border) pieces +- `rows`: Number of rows +- `columns`: Number of columns +- `aspectRatio`: Aspect ratio of columns to rows +- `format`: Puzzle format, which can be `portrait`, `square`, or `landscape` + +For this exercise, you may assume square pieces, so that the format can be derived from the aspect ratio: + +- If the aspect ratio is less than 1, it's `portrait` +- If it is equal to 1, it's `square` +- If it is greater than 1, it's `landscape` + +## Three examples + +### Portrait + +A portrait jigsaw puzzle with 6 pieces, all of which are border pieces and none are inside pieces. It has 3 rows and 2 columns. The aspect ratio is 1.5 (3/2). + +![A 2 by 3 jigsaw puzzle](https://assets.exercism.org/images/exercises/piecing-it-together/jigsaw-puzzle-2x3.svg) + +### Square + +A square jigsaw puzzle with 9 pieces, all of which are border pieces except for the one in the center, which is an inside piece. It has 3 rows and 3 columns. The aspect ratio is 1 (3/3). + +![A 3 by 3 jigsaw puzzle](https://assets.exercism.org/images/exercises/piecing-it-together/jigsaw-puzzle-3x3.svg) + +### Landscape + +A landscape jigsaw puzzle with 12 pieces, 10 of which are border pieces and 2 are inside pieces. It has 3 rows and 4 columns. The aspect ratio is 1.333333... (4/3). + +![A 4 by 3 jigsaw puzzle](https://assets.exercism.org/images/exercises/piecing-it-together/jigsaw-puzzle-4x3.svg) diff --git a/exercises/practice/piecing-it-together/.docs/introduction.md b/exercises/practice/piecing-it-together/.docs/introduction.md new file mode 100644 index 000000000..2fa20f6c5 --- /dev/null +++ b/exercises/practice/piecing-it-together/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +Your best friend has started collecting jigsaw puzzles and wants to build a detailed catalog of their collection — recording information such as the total number of pieces, the number of rows and columns, the number of border and inside pieces, aspect ratio, and puzzle format. +Even with your powers combined, it takes multiple hours to solve a single jigsaw puzzle with one thousand pieces — and then you still need to count the rows and columns and calculate the remaining numbers manually. +The even larger puzzles with thousands of pieces look quite daunting now. +"There has to be a better way!" you exclaim and sit down in front of your computer to solve the problem. diff --git a/exercises/practice/piecing-it-together/.meta/config.json b/exercises/practice/piecing-it-together/.meta/config.json new file mode 100644 index 000000000..76e6e6cbf --- /dev/null +++ b/exercises/practice/piecing-it-together/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "zamora-carlos" + ], + "files": { + "solution": [ + "src/main/java/PiecingItTogether.java" + ], + "test": [ + "src/test/java/PiecingItTogetherTest.java" + ], + "example": [ + ".meta/src/reference/java/PiecingItTogether.java" + ], + "editor": [ + "src/main/java/JigsawInfo.java" + ] + }, + "blurb": "Fill in missing jigsaw puzzle details from partial data", + "source": "atk just started another 1000-pieces jigsaw puzzle when this idea hit him", + "source_url": "https://github.com/exercism/problem-specifications/pull/2554" +} diff --git a/exercises/practice/piecing-it-together/.meta/src/reference/java/JigsawInfo.java b/exercises/practice/piecing-it-together/.meta/src/reference/java/JigsawInfo.java new file mode 100644 index 000000000..e08eb8909 --- /dev/null +++ b/exercises/practice/piecing-it-together/.meta/src/reference/java/JigsawInfo.java @@ -0,0 +1,140 @@ +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Represents partial or complete information about a jigsaw puzzle, + * + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public class JigsawInfo { + private final OptionalInt pieces; + private final OptionalInt border; + private final OptionalInt inside; + private final OptionalInt rows; + private final OptionalInt columns; + private final OptionalDouble aspectRatio; + private final Optional format; + + private JigsawInfo(Builder builder) { + this.pieces = builder.pieces; + this.border = builder.border; + this.inside = builder.inside; + this.rows = builder.rows; + this.columns = builder.columns; + this.aspectRatio = builder.aspectRatio; + this.format = builder.format; + } + + public static class Builder { + private OptionalInt pieces = OptionalInt.empty(); + private OptionalInt border = OptionalInt.empty(); + private OptionalInt inside = OptionalInt.empty(); + private OptionalInt rows = OptionalInt.empty(); + private OptionalInt columns = OptionalInt.empty(); + private OptionalDouble aspectRatio = OptionalDouble.empty(); + private Optional format = Optional.empty(); + + public Builder pieces(int pieces) { + this.pieces = OptionalInt.of(pieces); + return this; + } + + public Builder border(int border) { + this.border = OptionalInt.of(border); + return this; + } + + public Builder inside(int inside) { + this.inside = OptionalInt.of(inside); + return this; + } + + public Builder rows(int rows) { + this.rows = OptionalInt.of(rows); + return this; + } + + public Builder columns(int columns) { + this.columns = OptionalInt.of(columns); + return this; + } + + public Builder aspectRatio(double aspectRatio) { + this.aspectRatio = OptionalDouble.of(aspectRatio); + return this; + } + + public Builder format(String format) { + this.format = Optional.of(format); + return this; + } + + public JigsawInfo build() { + return new JigsawInfo(this); + } + } + + public OptionalInt getPieces() { + return pieces; + } + + public OptionalInt getBorder() { + return border; + } + + public OptionalInt getInside() { + return inside; + } + + public OptionalInt getRows() { + return rows; + } + + public OptionalInt getColumns() { + return columns; + } + + public OptionalDouble getAspectRatio() { + return aspectRatio; + } + + public Optional getFormat() { + return format; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + + JigsawInfo that = (JigsawInfo) o; + return Objects.equals(pieces, that.pieces) + && Objects.equals(border, that.border) + && Objects.equals(inside, that.inside) + && Objects.equals(rows, that.rows) + && Objects.equals(columns, that.columns) + && Objects.equals(aspectRatio, that.aspectRatio) + && Objects.equals(format, that.format); + } + + @Override + public int hashCode() { + return Objects.hash(pieces, border, inside, rows, columns, aspectRatio, format); + } + + @Override + public String toString() { + return "JigsawInfo{" + + "pieces=" + pieces + + ", border=" + border + + ", inside=" + inside + + ", rows=" + rows + + ", columns=" + columns + + ", aspectRatio=" + aspectRatio + + ", format=" + format + + '}'; + } +} diff --git a/exercises/practice/piecing-it-together/.meta/src/reference/java/PiecingItTogether.java b/exercises/practice/piecing-it-together/.meta/src/reference/java/PiecingItTogether.java new file mode 100644 index 000000000..ddbb91dc8 --- /dev/null +++ b/exercises/practice/piecing-it-together/.meta/src/reference/java/PiecingItTogether.java @@ -0,0 +1,135 @@ +import java.util.*; + +public class PiecingItTogether { + private static final double DOUBLE_EQUALITY_TOLERANCE = 1e-9; + private static final int MAX_DIMENSION = 1000; + + public static JigsawInfo getCompleteInformation(JigsawInfo input) { + List validGuesses = new ArrayList<>(); + + if (input.getPieces().isPresent()) { + // If pieces is known, we only test divisors of pieces + int pieces = input.getPieces().getAsInt(); + for (int rows = 1; rows <= pieces; rows++) { + if (pieces % rows != 0) { + continue; + } + int columns = pieces / rows; + createValidJigsaw(rows, columns, input).ifPresent(validGuesses::add); + } + } else if (input.getInside().isPresent() && input.getInside().getAsInt() > 0) { + // If inside pieces is non-zero, we only test divisors of inside + int inside = input.getInside().getAsInt(); + for (int innerRows = 1; innerRows <= inside; innerRows++) { + if (inside % innerRows != 0) { + continue; + } + int innerColumns = inside / innerRows; + createValidJigsaw(innerRows + 2, innerColumns + 2, input).ifPresent(validGuesses::add); + } + } else { + // Brute force using border constraint if available + int maxDimension = input.getBorder().isPresent() + ? Math.min(input.getBorder().getAsInt(), MAX_DIMENSION) + : MAX_DIMENSION; + + for (int rows = 1; rows <= maxDimension; rows++) { + for (int columns = 1; columns <= maxDimension; columns++) { + createValidJigsaw(rows, columns, input).ifPresent(validGuesses::add); + } + } + } + + if (validGuesses.size() == 1) { + return validGuesses.get(0); + } else if (validGuesses.size() > 1) { + throw new IllegalArgumentException("Insufficient data"); + } else { + throw new IllegalArgumentException("Contradictory data"); + } + } + + private static String getFormatFromAspect(double aspectRatio) { + if (Math.abs(aspectRatio - 1.0) < DOUBLE_EQUALITY_TOLERANCE) { + return "square"; + } else if (aspectRatio < 1.0) { + return "portrait"; + } else { + return "landscape"; + } + } + + private static JigsawInfo fromRowsAndCols(int rows, int columns) { + int pieces = rows * columns; + int border = (rows == 1 || columns == 1) ? pieces : 2 * (rows + columns - 2); + int inside = pieces - border; + double aspectRatio = (double) columns / rows; + String format = getFormatFromAspect(aspectRatio); + + return new JigsawInfo.Builder() + .pieces(pieces) + .border(border) + .inside(inside) + .rows(rows) + .columns(columns) + .aspectRatio(aspectRatio) + .format(format) + .build(); + } + + /** + * Verifies that all known values in the input match those in the computed result. + * Returns false if any known value conflicts, true if all values are consistent. + * + * @param computed the fully inferred jigsaw information + * @param input the original partial input with possibly empty values + * @return true if all values are consistent, false if any conflict exists + */ + private static boolean isConsistent(JigsawInfo computed, JigsawInfo input) { + return valuesMatch(computed.getPieces(), input.getPieces()) && + valuesMatch(computed.getBorder(), input.getBorder()) && + valuesMatch(computed.getInside(), input.getInside()) && + valuesMatch(computed.getRows(), input.getRows()) && + valuesMatch(computed.getColumns(), input.getColumns()) && + valuesMatch(computed.getAspectRatio(), input.getAspectRatio()) && + valuesMatch(computed.getFormat(), input.getFormat()); + } + + /** + * Attempts to construct a valid jigsaw configuration using the specified number of rows and columns. + * Returns a valid result only if the configuration is consistent with the input. + * + * @param rows number of rows to try + * @param columns number of columns to try + * @param input the original input to check for consistency + * @return an Optional containing a valid configuration, or empty if inconsistent + */ + private static Optional createValidJigsaw(int rows, int columns, JigsawInfo input) { + JigsawInfo candidate = fromRowsAndCols(rows, columns); + return isConsistent(candidate, input) ? Optional.of(candidate) : Optional.empty(); + } + + private static boolean valuesMatch(Optional a, Optional b) { + if (a.isPresent() && b.isPresent()) { + return Objects.equals(a.get(), b.get()); + } + + return true; + } + + private static boolean valuesMatch(OptionalInt a, OptionalInt b) { + if (a.isPresent() && b.isPresent()) { + return a.getAsInt() == b.getAsInt(); + } + + return true; + } + + private static boolean valuesMatch(OptionalDouble a, OptionalDouble b) { + if (a.isPresent() && b.isPresent()) { + return Double.compare(a.getAsDouble(), b.getAsDouble()) == 0; + } + + return true; + } +} diff --git a/exercises/practice/piecing-it-together/.meta/tests.toml b/exercises/practice/piecing-it-together/.meta/tests.toml new file mode 100644 index 000000000..f462c15a3 --- /dev/null +++ b/exercises/practice/piecing-it-together/.meta/tests.toml @@ -0,0 +1,31 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[ad626f23-09a2-4f5f-ba22-eec0671fa2a9] +description = "1000 pieces puzzle with 1.6 aspect ratio" + +[3e0c5919-3561-42f5-b9ed-26d70c20214e] +description = "square puzzle with 32 rows" + +[1126f160-b094-4dc2-bf37-13e36e394867] +description = "400 pieces square puzzle with only inside pieces and aspect ratio" + +[a9743178-5642-4cc0-8fdb-00d6b031c3a0] +description = "1500 pieces landscape puzzle with 30 rows and 1.6 aspect ratio" + +[f6378369-989c-497f-a6e2-f30b1fa76cba] +description = "300 pieces portrait puzzle with 70 border pieces" + +[f53f82ba-5663-4c7e-9e86-57fdbb3e53d2] +description = "puzzle with insufficient data" + +[a3d5c31a-cc74-44bf-b4fc-9e4d65f1ac1a] +description = "puzzle with contradictory data" diff --git a/exercises/practice/piecing-it-together/build.gradle b/exercises/practice/piecing-it-together/build.gradle new file mode 100644 index 000000000..dd3862eb9 --- /dev/null +++ b/exercises/practice/piecing-it-together/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/practice/piecing-it-together/gradlew.bat b/exercises/practice/piecing-it-together/gradlew.bat new file mode 100644 index 000000000..93e3f59f1 --- /dev/null +++ b/exercises/practice/piecing-it-together/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/practice/piecing-it-together/src/main/java/JigsawInfo.java b/exercises/practice/piecing-it-together/src/main/java/JigsawInfo.java new file mode 100644 index 000000000..e08eb8909 --- /dev/null +++ b/exercises/practice/piecing-it-together/src/main/java/JigsawInfo.java @@ -0,0 +1,140 @@ +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +/** + * Represents partial or complete information about a jigsaw puzzle, + * + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public class JigsawInfo { + private final OptionalInt pieces; + private final OptionalInt border; + private final OptionalInt inside; + private final OptionalInt rows; + private final OptionalInt columns; + private final OptionalDouble aspectRatio; + private final Optional format; + + private JigsawInfo(Builder builder) { + this.pieces = builder.pieces; + this.border = builder.border; + this.inside = builder.inside; + this.rows = builder.rows; + this.columns = builder.columns; + this.aspectRatio = builder.aspectRatio; + this.format = builder.format; + } + + public static class Builder { + private OptionalInt pieces = OptionalInt.empty(); + private OptionalInt border = OptionalInt.empty(); + private OptionalInt inside = OptionalInt.empty(); + private OptionalInt rows = OptionalInt.empty(); + private OptionalInt columns = OptionalInt.empty(); + private OptionalDouble aspectRatio = OptionalDouble.empty(); + private Optional format = Optional.empty(); + + public Builder pieces(int pieces) { + this.pieces = OptionalInt.of(pieces); + return this; + } + + public Builder border(int border) { + this.border = OptionalInt.of(border); + return this; + } + + public Builder inside(int inside) { + this.inside = OptionalInt.of(inside); + return this; + } + + public Builder rows(int rows) { + this.rows = OptionalInt.of(rows); + return this; + } + + public Builder columns(int columns) { + this.columns = OptionalInt.of(columns); + return this; + } + + public Builder aspectRatio(double aspectRatio) { + this.aspectRatio = OptionalDouble.of(aspectRatio); + return this; + } + + public Builder format(String format) { + this.format = Optional.of(format); + return this; + } + + public JigsawInfo build() { + return new JigsawInfo(this); + } + } + + public OptionalInt getPieces() { + return pieces; + } + + public OptionalInt getBorder() { + return border; + } + + public OptionalInt getInside() { + return inside; + } + + public OptionalInt getRows() { + return rows; + } + + public OptionalInt getColumns() { + return columns; + } + + public OptionalDouble getAspectRatio() { + return aspectRatio; + } + + public Optional getFormat() { + return format; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + + JigsawInfo that = (JigsawInfo) o; + return Objects.equals(pieces, that.pieces) + && Objects.equals(border, that.border) + && Objects.equals(inside, that.inside) + && Objects.equals(rows, that.rows) + && Objects.equals(columns, that.columns) + && Objects.equals(aspectRatio, that.aspectRatio) + && Objects.equals(format, that.format); + } + + @Override + public int hashCode() { + return Objects.hash(pieces, border, inside, rows, columns, aspectRatio, format); + } + + @Override + public String toString() { + return "JigsawInfo{" + + "pieces=" + pieces + + ", border=" + border + + ", inside=" + inside + + ", rows=" + rows + + ", columns=" + columns + + ", aspectRatio=" + aspectRatio + + ", format=" + format + + '}'; + } +} diff --git a/exercises/practice/piecing-it-together/src/main/java/PiecingItTogether.java b/exercises/practice/piecing-it-together/src/main/java/PiecingItTogether.java new file mode 100644 index 000000000..d0ba9fa81 --- /dev/null +++ b/exercises/practice/piecing-it-together/src/main/java/PiecingItTogether.java @@ -0,0 +1,5 @@ +public class PiecingItTogether { + public static JigsawInfo getCompleteInformation(JigsawInfo input) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } +} diff --git a/exercises/practice/piecing-it-together/src/test/java/PiecingItTogetherTest.java b/exercises/practice/piecing-it-together/src/test/java/PiecingItTogetherTest.java new file mode 100644 index 000000000..b07648c0a --- /dev/null +++ b/exercises/practice/piecing-it-together/src/test/java/PiecingItTogetherTest.java @@ -0,0 +1,167 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class PiecingItTogetherTest { + private static final double DOUBLE_EQUALITY_TOLERANCE = 1e-9; + + @Test + public void test1000PiecesWithAspectRatio() { + JigsawInfo input = new JigsawInfo.Builder() + .pieces(1000) + .aspectRatio(1.6) + .build(); + + JigsawInfo expected = new JigsawInfo.Builder() + .pieces(1000) + .border(126) + .inside(874) + .rows(25) + .columns(40) + .aspectRatio(1.6) + .format("landscape") + .build(); + + JigsawInfo actual = PiecingItTogether.getCompleteInformation(input); + assertJigsawInfoEquals(actual, expected); + } + + @Disabled("Remove to run test") + @Test + public void testSquarePuzzleWith32Rows() { + JigsawInfo input = new JigsawInfo.Builder() + .rows(32) + .format("square") + .build(); + + JigsawInfo expected = new JigsawInfo.Builder() + .pieces(1024) + .border(124) + .inside(900) + .rows(32) + .columns(32) + .aspectRatio(1.0) + .format("square") + .build(); + + JigsawInfo actual = PiecingItTogether.getCompleteInformation(input); + assertJigsawInfoEquals(actual, expected); + } + + @Disabled("Remove to run test") + @Test + public void testInsideAndAspectRatioOnly() { + JigsawInfo input = new JigsawInfo.Builder() + .inside(324) + .aspectRatio(1.0) + .build(); + + JigsawInfo expected = new JigsawInfo.Builder() + .pieces(400) + .border(76) + .inside(324) + .rows(20) + .columns(20) + .aspectRatio(1.0) + .format("square") + .build(); + + JigsawInfo actual = PiecingItTogether.getCompleteInformation(input); + assertJigsawInfoEquals(actual, expected); + } + + @Disabled("Remove to run test") + @Test + public void testLandscape1500WithRowsAndAspect() { + JigsawInfo input = new JigsawInfo.Builder() + .rows(30) + .aspectRatio(1.6666666666666667) + .build(); + + JigsawInfo expected = new JigsawInfo.Builder() + .pieces(1500) + .border(156) + .inside(1344) + .rows(30) + .columns(50) + .aspectRatio(1.6666666666666667) + .format("landscape") + .build(); + + JigsawInfo actual = PiecingItTogether.getCompleteInformation(input); + assertJigsawInfoEquals(actual, expected); + } + + @Disabled("Remove to run test") + @Test + public void test300PiecesPortraitWithBorder() { + JigsawInfo input = new JigsawInfo.Builder() + .pieces(300) + .border(70) + .format("portrait") + .build(); + + JigsawInfo expected = new JigsawInfo.Builder() + .pieces(300) + .border(70) + .inside(230) + .rows(25) + .columns(12) + .aspectRatio(0.48) + .format("portrait") + .build(); + + JigsawInfo actual = PiecingItTogether.getCompleteInformation(input); + assertJigsawInfoEquals(actual, expected); + } + + @Disabled("Remove to run test") + @Test + public void testInsufficientData() { + JigsawInfo input = new JigsawInfo.Builder() + .pieces(1500) + .format("landscape") + .build(); + + assertThatThrownBy(() -> PiecingItTogether.getCompleteInformation(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Insufficient data"); + } + + @Disabled("Remove to run test") + @Test + public void testContradictoryData() { + JigsawInfo input = new JigsawInfo.Builder() + .rows(100) + .columns(1000) + .format("square") + .build(); + + assertThatThrownBy(() -> PiecingItTogether.getCompleteInformation(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Contradictory data"); + } + + /** + * Helper to compare two JigsawInfo objects, + * allowing a small tolerance when comparing aspect ratio doubles. + */ + private void assertJigsawInfoEquals(JigsawInfo actual, JigsawInfo expected) { + assertThat(actual.getPieces()).isEqualTo(expected.getPieces()); + assertThat(actual.getBorder()).isEqualTo(expected.getBorder()); + assertThat(actual.getInside()).isEqualTo(expected.getInside()); + assertThat(actual.getRows()).isEqualTo(expected.getRows()); + assertThat(actual.getColumns()).isEqualTo(expected.getColumns()); + assertThat(actual.getFormat()).isEqualTo(expected.getFormat()); + + Double actualAspect = actual.getAspectRatio() + .orElseThrow(() -> new AssertionError("Missing aspect ratio in actual result")); + Double expectedAspect = expected.getAspectRatio() + .orElseThrow(() -> new AssertionError("Missing aspect ratio in expected result")); + + assertThat(actualAspect).isCloseTo(expectedAspect, + org.assertj.core.api.Assertions.within(DOUBLE_EQUALITY_TOLERANCE)); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 713a0c583..38c04e59a 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -104,6 +104,7 @@ include 'practice:parallel-letter-frequency' include 'practice:pascals-triangle' include 'practice:perfect-numbers' include 'practice:phone-number' +include 'practice:piecing-it-together' include 'practice:pig-latin' include 'practice:poker' include 'practice:eliuds-eggs' From d460a46669483215a000d5810ee24046279ea58f Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Mon, 30 Jun 2025 18:09:10 +0530 Subject: [PATCH 32/67] Update to use exercism run-configlet-sync.yml (#2963) --- .github/workflows/configlet-sync.yml | 93 ------------------------ .github/workflows/run-configlet-sync.yml | 10 +++ 2 files changed, 10 insertions(+), 93 deletions(-) delete mode 100644 .github/workflows/configlet-sync.yml create mode 100644 .github/workflows/run-configlet-sync.yml diff --git a/.github/workflows/configlet-sync.yml b/.github/workflows/configlet-sync.yml deleted file mode 100644 index 6373e5d24..000000000 --- a/.github/workflows/configlet-sync.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Configlet Auto Sync - -on: - workflow_dispatch: - schedule: - - cron: '0 0 15 * *' - -jobs: - sync-docs-metadata: - timeout-minutes: 10 - runs-on: ubuntu-24.04 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Fetch configlet - run: ./bin/fetch-configlet - - - name: Run configlet sync for files - run: ./bin/configlet sync --docs --metadata --filepaths -u -y - - - name: Create pull request if changes - uses: peter-evans/create-pull-request@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "🤖 Auto-sync docs, metadata, and filepaths" - title: "🤖 Configlet sync: docs, metadata, and filepaths" - body: | - This PR was generated automatically by a scheduled workflow. - - It includes updates from `configlet sync` for: - - 📄 Documentation - - 🧭 Metadata - - 🗂️ Filepaths - - Please review and merge if everything looks good! - branch: configlet-auto-sync - delete-branch: true - - check-test-sync: - timeout-minutes: 10 - runs-on: ubuntu-24.04 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Fetch configlet - run: ./bin/fetch-configlet - - - name: Run configlet sync for test and capture output - id: sync_test - run: | - configlet_raw_output="$(./bin/configlet sync --tests | tee .github/sync-test-output.txt)" - - echo "configlet output:" - echo "$configlet_raw_output" - - echo '```' > .github/sync-test-output.txt - echo "$configlet_raw_output" >> .github/sync-test-output.txt - echo '```' >> .github/sync-test-output.txt - - echo "output<> "$GITHUB_OUTPUT" - echo "$configlet_raw_output" >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - - - name: Find existing issue for test sync - id: find_issue - if: ${{ !contains(steps.sync_test.outputs.output, 'Every exercise has up-to-date tests!') }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - ISSUE_TITLE="🚨 configlet sync --test found unsynced tests" - ISSUE_DATA=$(gh issue list --search "is:issue is:open in:title \"${ISSUE_TITLE}\" repo:${{ github.repository }}" --json number,title --jq '.[0]') - - if [ -z "$ISSUE_DATA" ]; then - echo "No open issue found with title: '${ISSUE_TITLE}'. A new one will be created." - echo "issue_number=" >> $GITHUB_OUTPUT - else - ISSUE_NUMBER=$(echo "$ISSUE_DATA" | jq -r '.number') - echo "Found existing issue number: $ISSUE_NUMBER for title: '$ISSUE_TITLE'" - echo "issue_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT - fi - - - name: Create or Update issue if tests are not synced - if: ${{ !contains(steps.sync_test.outputs.output, 'Every exercise has up-to-date tests!') }} - uses: peter-evans/create-issue-from-file@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - title: "🚨 configlet sync --test found unsynced tests" - content-filepath: .github/sync-test-output.txt - issue-number: ${{ steps.find_issue.outputs.issue_number || '' }} diff --git a/.github/workflows/run-configlet-sync.yml b/.github/workflows/run-configlet-sync.yml new file mode 100644 index 000000000..b49cbffe8 --- /dev/null +++ b/.github/workflows/run-configlet-sync.yml @@ -0,0 +1,10 @@ +name: Run Configlet Sync + +on: + workflow_dispatch: + schedule: + - cron: '0 0 15 * *' + +jobs: + call-gha-workflow: + uses: exercism/github-actions/.github/workflows/configlet-sync.yml@main From 2bd408fd320ae1d759d30debebe2d0154d1ffe80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Tue, 1 Jul 2025 07:09:00 -0700 Subject: [PATCH 33/67] Add `flower-field`, deprecating `minesweeper` (#2964) --- config.json | 21 +- .../flower-field/.docs/instructions.md | 26 ++ .../flower-field/.docs/introduction.md | 7 + .../practice/flower-field/.meta/config.json | 41 +++ .../src/reference/java/FlowerFieldBoard.java | 75 ++++++ .../practice/flower-field/.meta/tests.toml | 46 ++++ exercises/practice/flower-field/build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + exercises/practice/flower-field/gradlew | 249 ++++++++++++++++++ exercises/practice/flower-field/gradlew.bat | 92 +++++++ .../src/main/java/FlowerFieldBoard.java | 13 + .../src/test/java/FlowerFieldBoardTest.java | 241 +++++++++++++++++ exercises/settings.gradle | 3 +- 14 files changed, 838 insertions(+), 7 deletions(-) create mode 100644 exercises/practice/flower-field/.docs/instructions.md create mode 100644 exercises/practice/flower-field/.docs/introduction.md create mode 100644 exercises/practice/flower-field/.meta/config.json create mode 100644 exercises/practice/flower-field/.meta/src/reference/java/FlowerFieldBoard.java create mode 100644 exercises/practice/flower-field/.meta/tests.toml create mode 100644 exercises/practice/flower-field/build.gradle create mode 100644 exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.properties create mode 100755 exercises/practice/flower-field/gradlew create mode 100644 exercises/practice/flower-field/gradlew.bat create mode 100644 exercises/practice/flower-field/src/main/java/FlowerFieldBoard.java create mode 100644 exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java diff --git a/config.json b/config.json index 1623d2b25..bf9df02b3 100644 --- a/config.json +++ b/config.json @@ -1239,6 +1239,18 @@ ], "difficulty": 6 }, + { + "slug": "flower-field", + "name": "Flower Field", + "uuid": "bddd180a-d634-454a-af03-4d625f77e1e2", + "practices": [], + "prerequisites": [ + "constructors", + "lists", + "strings" + ], + "difficulty": 6 + }, { "slug": "food-chain", "name": "Food Chain", @@ -1289,12 +1301,9 @@ "name": "Minesweeper", "uuid": "416a1489-12af-4593-8540-0f55285c96b4", "practices": [], - "prerequisites": [ - "constructors", - "lists", - "strings" - ], - "difficulty": 6 + "prerequisites": [], + "difficulty": 6, + "status": "deprecated" }, { "slug": "parallel-letter-frequency", diff --git a/exercises/practice/flower-field/.docs/instructions.md b/exercises/practice/flower-field/.docs/instructions.md new file mode 100644 index 000000000..bbdae0c2c --- /dev/null +++ b/exercises/practice/flower-field/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to add flower counts to empty squares in a completed Flower Field garden. +The garden itself is a rectangle board composed of squares that are either empty (`' '`) or a flower (`'*'`). + +For each empty square, count the number of flowers adjacent to it (horizontally, vertically, diagonally). +If the empty square has no adjacent flowers, leave it empty. +Otherwise replace it with the count of adjacent flowers. + +For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen): + +```text +·*·*· +··*·· +··*·· +····· +``` + +Which your code should transform into this: + +```text +1*3*1 +13*31 +·2*2· +·111· +``` diff --git a/exercises/practice/flower-field/.docs/introduction.md b/exercises/practice/flower-field/.docs/introduction.md new file mode 100644 index 000000000..af9b61536 --- /dev/null +++ b/exercises/practice/flower-field/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +[Flower Field][history] is a compassionate reimagining of the popular game Minesweeper. +The object of the game is to find all the flowers in the garden using numeric hints that indicate how many flowers are directly adjacent (horizontally, vertically, diagonally) to a square. +"Flower Field" shipped in regional versions of Microsoft Windows in Italy, Germany, South Korea, Japan and Taiwan. + +[history]: https://web.archive.org/web/20020409051321fw_/http://rcm.usr.dsi.unimi.it/rcmweb/fnm/ diff --git a/exercises/practice/flower-field/.meta/config.json b/exercises/practice/flower-field/.meta/config.json new file mode 100644 index 000000000..54dc83896 --- /dev/null +++ b/exercises/practice/flower-field/.meta/config.json @@ -0,0 +1,41 @@ +{ + "authors": [ + "stkent" + ], + "contributors": [ + "aadityakulkarni", + "BNAndras", + "FridaTveit", + "hgvanpariya", + "jmrunkle", + "jsertel", + "kytrinyx", + "lemoncurry", + "matthewmorgan", + "matthewstyler", + "morrme", + "msomji", + "muzimuzhi", + "redshirt4", + "SleeplessByte", + "Smarticles101", + "sshine", + "vivshaw", + "Zaldrick" + ], + "files": { + "solution": [ + "src/main/java/FlowerFieldBoard.java" + ], + "test": [ + "src/test/java/FlowerFieldBoardTest.java" + ], + "example": [ + ".meta/src/reference/java/FlowerFieldBoard.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Mark all the flowers in a garden." +} diff --git a/exercises/practice/flower-field/.meta/src/reference/java/FlowerFieldBoard.java b/exercises/practice/flower-field/.meta/src/reference/java/FlowerFieldBoard.java new file mode 100644 index 000000000..0fa97e128 --- /dev/null +++ b/exercises/practice/flower-field/.meta/src/reference/java/FlowerFieldBoard.java @@ -0,0 +1,75 @@ +import java.util.ArrayList; +import java.util.List; + +final class FlowerFieldBoard { + + private static final char FLOWER_CHAR = '*'; + + private static final char SPACE_CHAR = ' '; + + private final List rawRepresentation; + + private final int numberOfRows; + + private final int numberOfColumns; + + FlowerFieldBoard(final List rawRepresentation) { + this.rawRepresentation = rawRepresentation; + this.numberOfRows = rawRepresentation.size(); + this.numberOfColumns = rawRepresentation.isEmpty() ? 0 : rawRepresentation.get(0).length(); + } + + List withNumbers() { + final List result = new ArrayList<>(); + + for (int rowNumber = 0; rowNumber < numberOfRows; rowNumber++) { + result.add(getRowWithNumbers(rowNumber)); + } + + return result; + } + + private String getRowWithNumbers(final int rowNumber) { + StringBuilder result = new StringBuilder(numberOfColumns); + + for (int columnNumber = 0; columnNumber < numberOfColumns; columnNumber++) { + result.append(getCellNumber(rowNumber, columnNumber)); + } + + return result.toString(); + } + + private char getCellNumber(final int rowNumber, final int columnNumber) { + // If (rowNumber, columnNumber) is a flower, we're done. + if (rawRepresentation.get(rowNumber).charAt(columnNumber) == FLOWER_CHAR) { + return FLOWER_CHAR; + } + + final int flowerCount = computeFlowerCountAround(rowNumber, columnNumber); + + // If computed count is positive, add it to the annotated row. Otherwise, add a blank space. + return flowerCount > 0 ? Character.forDigit(flowerCount, 10) : SPACE_CHAR; + } + + private int computeFlowerCountAround(final int rowNumber, final int columnNumber) { + int result = 0; + + // Compute row and column ranges to inspect (respecting board edges). + final int minRowToInspect = Math.max(rowNumber - 1, 0); + final int maxRowToInspect = Math.min(rowNumber + 1, numberOfRows - 1); + final int minColToInspect = Math.max(columnNumber - 1, 0); + final int maxColToInspect = Math.min(columnNumber + 1, numberOfColumns - 1); + + // Count flowers in the cells surrounding (row, col). + for (int rowToInspect = minRowToInspect; rowToInspect <= maxRowToInspect; rowToInspect++) { + for (int colToInspect = minColToInspect; colToInspect <= maxColToInspect; colToInspect++) { + if (rawRepresentation.get(rowToInspect).charAt(colToInspect) == FLOWER_CHAR) { + result += 1; + } + } + } + + return result; + } + +} diff --git a/exercises/practice/flower-field/.meta/tests.toml b/exercises/practice/flower-field/.meta/tests.toml new file mode 100644 index 000000000..c2b24fdaf --- /dev/null +++ b/exercises/practice/flower-field/.meta/tests.toml @@ -0,0 +1,46 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[237ff487-467a-47e1-9b01-8a891844f86c] +description = "no rows" + +[4b4134ec-e20f-439c-a295-664c38950ba1] +description = "no columns" + +[d774d054-bbad-4867-88ae-069cbd1c4f92] +description = "no flowers" + +[225176a0-725e-43cd-aa13-9dced501f16e] +description = "garden full of flowers" + +[3f345495-f1a5-4132-8411-74bd7ca08c49] +description = "flower surrounded by spaces" + +[6cb04070-4199-4ef7-a6fa-92f68c660fca] +description = "space surrounded by flowers" + +[272d2306-9f62-44fe-8ab5-6b0f43a26338] +description = "horizontal line" + +[c6f0a4b2-58d0-4bf6-ad8d-ccf4144f1f8e] +description = "horizontal line, flowers at edges" + +[a54e84b7-3b25-44a8-b8cf-1753c8bb4cf5] +description = "vertical line" + +[b40f42f5-dec5-4abc-b167-3f08195189c1] +description = "vertical line, flowers at edges" + +[58674965-7b42-4818-b930-0215062d543c] +description = "cross" + +[dd9d4ca8-9e68-4f78-a677-a2a70fd7a7b8] +description = "large garden" diff --git a/exercises/practice/flower-field/build.gradle b/exercises/practice/flower-field/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/practice/flower-field/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/practice/flower-field/gradlew.bat b/exercises/practice/flower-field/gradlew.bat new file mode 100644 index 000000000..25da30dbd --- /dev/null +++ b/exercises/practice/flower-field/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/practice/flower-field/src/main/java/FlowerFieldBoard.java b/exercises/practice/flower-field/src/main/java/FlowerFieldBoard.java new file mode 100644 index 000000000..db059db34 --- /dev/null +++ b/exercises/practice/flower-field/src/main/java/FlowerFieldBoard.java @@ -0,0 +1,13 @@ +import java.util.List; + +class FlowerFieldBoard { + + FlowerFieldBoard(List boardRows) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + List withNumbers() { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + +} \ No newline at end of file diff --git a/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java b/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java new file mode 100644 index 000000000..518b6344b --- /dev/null +++ b/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java @@ -0,0 +1,241 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FlowerFieldBoardTest { + + @Test + public void testInputBoardWithNoRowsAndNoColumns() { + List inputBoard = Collections.emptyList(); + List expectedNumberedBoard = Collections.emptyList(); + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithOneRowAndNoColumns() { + List inputBoard = Collections.singletonList(""); + List expectedNumberedBoard = Collections.singletonList(""); + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithNoFlowers() { + List inputBoard = Arrays.asList( + " ", + " ", + " " + ); + + List expectedNumberedBoard = Arrays.asList( + " ", + " ", + " " + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithOnlyFlowers() { + List inputBoard = Arrays.asList( + "***", + "***", + "***" + ); + + List expectedNumberedBoard = Arrays.asList( + "***", + "***", + "***" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithSingleFlowerAtCenter() { + List inputBoard = Arrays.asList( + " ", + " * ", + " " + ); + + List expectedNumberedBoard = Arrays.asList( + "111", + "1*1", + "111" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithFlowersAroundPerimeter() { + List inputBoard = Arrays.asList( + "***", + "* *", + "***" + ); + + List expectedNumberedBoard = Arrays.asList( + "***", + "*8*", + "***" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithSingleRowAndTwoFlowers() { + List inputBoard = Collections.singletonList( + " * * " + ); + + List expectedNumberedBoard = Collections.singletonList( + "1*2*1" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithSingleRowAndTwoFlowersAtEdges() { + List inputBoard = Collections.singletonList( + "* *" + ); + + List expectedNumberedBoard = Collections.singletonList( + "*1 1*" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithSingleColumnAndTwoFlowers() { + List inputBoard = Arrays.asList( + " ", + "*", + " ", + "*", + " " + ); + + List expectedNumberedBoard = Arrays.asList( + "1", + "*", + "2", + "*", + "1" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithSingleColumnAndTwoFlowersAtEdges() { + List inputBoard = Arrays.asList( + "*", + " ", + " ", + " ", + "*" + ); + + List expectedNumberedBoard = Arrays.asList( + "*", + "1", + " ", + "1", + "*" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testInputBoardWithFlowersInCross() { + List inputBoard = Arrays.asList( + " * ", + " * ", + "*****", + " * ", + " * " + ); + + List expectedNumberedBoard = Arrays.asList( + " 2*2 ", + "25*52", + "*****", + "25*52", + " 2*2 " + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + public void testLargeInputBoard() { + List inputBoard = Arrays.asList( + " * * ", + " * ", + " * ", + " * *", + " * * ", + " " + ); + + List expectedNumberedBoard = Arrays.asList( + "1*22*1", + "12*322", + " 123*2", + "112*4*", + "1*22*2", + "111111" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 38c04e59a..61f9b0991 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -62,6 +62,7 @@ include 'practice:dot-dsl' include 'practice:error-handling' include 'practice:etl' include 'practice:flatten-array' +include 'practice:flower-field' include 'practice:food-chain' include 'practice:forth' include 'practice:game-of-life' @@ -93,7 +94,7 @@ include 'practice:matrix' include 'practice:mazy-mice' include 'practice:meetup' include 'practice:micro-blog' -include 'practice:minesweeper' +// include 'practice:minesweeper' // deprecated include 'practice:nth-prime' include 'practice:nucleotide-count' include 'practice:ocr-numbers' From 2238efa8c5d75de57d98034139c96ec9a0814c4a Mon Sep 17 00:00:00 2001 From: Yuliia Baranetska <57073872+kuryuliya@users.noreply.github.com> Date: Thu, 3 Jul 2025 12:49:16 +0200 Subject: [PATCH 34/67] Fix International Calling Connoisseur hints (#2962) Fix hints to match exemplar solution implementation for international-calling-connoisseur Co-authored-by: Kah Goh --- .../concept/international-calling-connoisseur/.docs/hints.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/concept/international-calling-connoisseur/.docs/hints.md b/exercises/concept/international-calling-connoisseur/.docs/hints.md index 056edd53d..8bedc370b 100644 --- a/exercises/concept/international-calling-connoisseur/.docs/hints.md +++ b/exercises/concept/international-calling-connoisseur/.docs/hints.md @@ -22,7 +22,7 @@ ## 5. Find a country's dialing code -- There is a [way][map-values-docs] to get an iterable collection of values in a map. +- There is a [way][map-entry-set-docs] to get an iterable collection of entries in a map, which allows you to go through the key-value pairs. ## 6. Update the country's dialing code @@ -35,5 +35,5 @@ [map-get-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#get(java.lang.Object) [map-contains-key-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsKey(java.lang.Object) [map-contains-value-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#containsValue(java.lang.Object) -[map-values-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#values() +[map-entry-set-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#entrySet() [map-remove-docs]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Map.html#remove(java.lang.Object) From 312ab2cbf36a74da987a497a7690a4c5dc1e1018 Mon Sep 17 00:00:00 2001 From: Carlos Uriel Date: Mon, 7 Jul 2025 06:08:34 -0600 Subject: [PATCH 35/67] Add swift-scheduling practice exercise (#2966) --- config.json | 12 + .../swift-scheduling/.docs/instructions.md | 50 ++++ .../swift-scheduling/.docs/introduction.md | 6 + .../swift-scheduling/.meta/config.json | 19 ++ .../src/reference/java/SwiftScheduling.java | 97 +++++++ .../swift-scheduling/.meta/tests.toml | 58 ++++ .../practice/swift-scheduling/build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + exercises/practice/swift-scheduling/gradlew | 249 ++++++++++++++++++ .../practice/swift-scheduling/gradlew.bat | 92 +++++++ .../src/main/java/SwiftScheduling.java | 7 + .../src/test/java/SwiftSchedulingTest.java | 200 ++++++++++++++ exercises/settings.gradle | 1 + 14 files changed, 822 insertions(+) create mode 100644 exercises/practice/swift-scheduling/.docs/instructions.md create mode 100644 exercises/practice/swift-scheduling/.docs/introduction.md create mode 100644 exercises/practice/swift-scheduling/.meta/config.json create mode 100644 exercises/practice/swift-scheduling/.meta/src/reference/java/SwiftScheduling.java create mode 100644 exercises/practice/swift-scheduling/.meta/tests.toml create mode 100644 exercises/practice/swift-scheduling/build.gradle create mode 100644 exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.properties create mode 100755 exercises/practice/swift-scheduling/gradlew create mode 100644 exercises/practice/swift-scheduling/gradlew.bat create mode 100644 exercises/practice/swift-scheduling/src/main/java/SwiftScheduling.java create mode 100644 exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java diff --git a/config.json b/config.json index bf9df02b3..1356cbf81 100644 --- a/config.json +++ b/config.json @@ -854,6 +854,18 @@ ], "difficulty": 4 }, + { + "slug": "swift-scheduling", + "name": "Swift Scheduling", + "uuid": "7f5388dc-ce0e-40d4-98d1-7a00aeae018d", + "practices": [], + "prerequisites": [ + "if-else-statements", + "datetime", + "strings" + ], + "difficulty": 4 + }, { "slug": "triangle", "name": "Triangle", diff --git a/exercises/practice/swift-scheduling/.docs/instructions.md b/exercises/practice/swift-scheduling/.docs/instructions.md new file mode 100644 index 000000000..6423a1066 --- /dev/null +++ b/exercises/practice/swift-scheduling/.docs/instructions.md @@ -0,0 +1,50 @@ +# Instructions + +Your task is to convert delivery date descriptions to _actual_ delivery dates, based on when the meeting started. + +There are two types of delivery date descriptions: + +1. Fixed: a predefined set of words. +2. Variable: words that have a variable component, but follow a predefined set of patterns. + +## Fixed delivery date descriptions + +There are three fixed delivery date descriptions: + +- `"NOW"` +- `"ASAP"` (As Soon As Possible) +- `"EOW"` (End Of Week) + +The following table shows how to translate them: + +| Description | Meeting start | Delivery date | +| ----------- | ----------------------------- | ----------------------------------- | +| `"NOW"` | - | Two hours after the meeting started | +| `"ASAP"` | Before 13:00 | Today at 17:00 | +| `"ASAP"` | After or at 13:00 | Tomorrow at 13:00 | +| `"EOW"` | Monday, Tuesday, or Wednesday | Friday at 17:00 | +| `"EOW"` | Thursday or Friday | Sunday at 20:00 | + +## Variable delivery date descriptions + +There are two variable delivery date description patterns: + +- `"M"` (N-th month) +- `"Q"` (N-th quarter) + +| Description | Meeting start | Delivery date | +| ----------- | ------------------------- | --------------------------------------------------------- | +| `"M"` | Before N-th month | At 8:00 on the _first_ workday of this year's N-th month | +| `"M"` | After or in N-th month | At 8:00 on the _first_ workday of next year's N-th month | +| `"Q"` | Before or in N-th quarter | At 8:00 on the _last_ workday of this year's N-th quarter | +| `"Q"` | After N-th quarter | At 8:00 on the _last_ workday of next year's N-th quarter | + +~~~~exercism/note +A workday is a Monday, Tuesday, Wednesday, Thursday, or Friday. + +A year has four quarters, each with three months: +1. January/February/March +2. April/May/June +3. July/August/September +4. October/November/December. +~~~~ diff --git a/exercises/practice/swift-scheduling/.docs/introduction.md b/exercises/practice/swift-scheduling/.docs/introduction.md new file mode 100644 index 000000000..2322f813f --- /dev/null +++ b/exercises/practice/swift-scheduling/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +This week, it is your turn to take notes in the department's planning meeting. +In this meeting, your boss will set delivery dates for all open work items. +Annoyingly, instead of specifying the _actual_ delivery dates, your boss will only _describe them_ in an abbreviated format. +As many of your colleagues won't be familiar with this corporate lingo, you'll need to convert these delivery date descriptions to actual delivery dates. diff --git a/exercises/practice/swift-scheduling/.meta/config.json b/exercises/practice/swift-scheduling/.meta/config.json new file mode 100644 index 000000000..d555a97e2 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "zamora-carlos" + ], + "files": { + "solution": [ + "src/main/java/SwiftScheduling.java" + ], + "test": [ + "src/test/java/SwiftSchedulingTest.java" + ], + "example": [ + ".meta/src/reference/java/SwiftScheduling.java" + ] + }, + "blurb": "Convert delivery date descriptions to actual delivery dates.", + "source": "Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/pull/2536" +} diff --git a/exercises/practice/swift-scheduling/.meta/src/reference/java/SwiftScheduling.java b/exercises/practice/swift-scheduling/.meta/src/reference/java/SwiftScheduling.java new file mode 100644 index 000000000..8bff6e3d3 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/src/reference/java/SwiftScheduling.java @@ -0,0 +1,97 @@ +import java.time.DayOfWeek; +import java.time.LocalDateTime; + +import static java.time.DayOfWeek.*; + +public class SwiftScheduling { + public static LocalDateTime convertToDeliveryDate(LocalDateTime meetingStart, String description) { + if ("NOW".equals(description)) { + return meetingStart.plusHours(2); + } + + if ("ASAP".equals(description)) { + LocalDateTime sameDayAt1pm = toStartOfDay(meetingStart).withHour(13); + + if (meetingStart.isBefore(sameDayAt1pm)) { + return toStartOfDay(meetingStart).withHour(17); + } else { + return toStartOfDay(meetingStart).plusDays(1).withHour(13); + } + } + + if ("EOW".equals(description)) { + DayOfWeek day = meetingStart.getDayOfWeek(); + LocalDateTime deliveryDate = toStartOfDay(meetingStart); + + if (day == MONDAY || day == TUESDAY || day == WEDNESDAY) { + deliveryDate = deliveryDate.withHour(17); + while (deliveryDate.getDayOfWeek() != FRIDAY) { + deliveryDate = deliveryDate.plusDays(1); + } + } else if (day == THURSDAY || day == FRIDAY) { + deliveryDate = deliveryDate.withHour(20); + while (deliveryDate.getDayOfWeek() != SUNDAY) { + deliveryDate = deliveryDate.plusDays(1); + } + } else { + throw new IllegalArgumentException("Invalid day of week"); + } + + return deliveryDate; + } + + if (description.matches("\\d+M")) { + int month = Integer.parseInt(description.substring(0, description.length() - 1)); + LocalDateTime targetMonth = toStartOfDay(meetingStart) + .withMonth(month) + .withDayOfMonth(1); + + if (!meetingStart.isBefore(targetMonth)) { + targetMonth = targetMonth.plusYears(1); + } + + LocalDateTime deliveryDate = targetMonth.withHour(8); + while (isWeekend(deliveryDate)) { + deliveryDate = deliveryDate.plusDays(1); + } + + return deliveryDate; + } + + if (description.matches("Q\\d")) { + int quarter = Integer.parseInt(description.substring(1)); + LocalDateTime lastDayOfQuarter = getLastDayOfQuarter(meetingStart, quarter); + + if (!meetingStart.isBefore(lastDayOfQuarter.plusDays(1))) { + lastDayOfQuarter = lastDayOfQuarter.plusYears(1); + } + + LocalDateTime deliveryDate = lastDayOfQuarter.withHour(8); + while (isWeekend(deliveryDate)) { + deliveryDate = deliveryDate.minusDays(1); + } + + return deliveryDate; + } + + throw new IllegalArgumentException("Invalid description"); + } + + private static LocalDateTime toStartOfDay(LocalDateTime dateTime) { + return dateTime.toLocalDate().atStartOfDay(); + } + + private static LocalDateTime getLastDayOfQuarter(LocalDateTime dateTime, int quarter) { + int lastMonthOfQuarter = quarter * 3; + return toStartOfDay(dateTime) + .withMonth(lastMonthOfQuarter) + .withDayOfMonth(1) + .plusMonths(1) + .minusDays(1); + } + + private static boolean isWeekend(LocalDateTime date) { + DayOfWeek dayOfWeek = date.getDayOfWeek(); + return dayOfWeek == SATURDAY || dayOfWeek == SUNDAY; + } +} diff --git a/exercises/practice/swift-scheduling/.meta/tests.toml b/exercises/practice/swift-scheduling/.meta/tests.toml new file mode 100644 index 000000000..7cc3e4158 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/tests.toml @@ -0,0 +1,58 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[1d0e6e72-f370-408c-bc64-5dafa9c6da73] +description = "NOW translates to two hours later" + +[93325e7b-677d-4d96-b017-2582af879dc2] +description = "ASAP before one in the afternoon translates to today at five in the afternoon" + +[cb4252a3-c4c1-41f6-8b8c-e7269733cef8] +description = "ASAP at one in the afternoon translates to tomorrow at one in the afternoon" + +[6fddc1ea-2fe9-4c60-81f7-9220d2f45537] +description = "ASAP after one in the afternoon translates to tomorrow at one in the afternoon" + +[25f46bf9-6d2a-4e95-8edd-f62dd6bc8a6e] +description = "EOW on Monday translates to Friday at five in the afternoon" + +[0b375df5-d198-489e-acee-fd538a768616] +description = "EOW on Tuesday translates to Friday at five in the afternoon" + +[4afbb881-0b5c-46be-94e1-992cdc2a8ca4] +description = "EOW on Wednesday translates to Friday at five in the afternoon" + +[e1341c2b-5e1b-4702-a95c-a01e8e96e510] +description = "EOW on Thursday translates to Sunday at eight in the evening" + +[bbffccf7-97f7-4244-888d-bdd64348fa2e] +description = "EOW on Friday translates to Sunday at eight in the evening" + +[d651fcf4-290e-407c-8107-36b9076f39b2] +description = "EOW translates to leap day" + +[439bf09f-3a0e-44e7-bad5-b7b6d0c4505a] +description = "2M before the second month of this year translates to the first workday of the second month of this year" + +[86d82e83-c481-4fb4-9264-625de7521340] +description = "11M in the eleventh month translates to the first workday of the eleventh month of next year" + +[0d0b8f6a-1915-46f5-a630-1ff06af9da08] +description = "4M in the ninth month translates to the first workday of the fourth month of next year" + +[06d401e3-8461-438f-afae-8d26aa0289e0] +description = "Q1 in the first quarter translates to the last workday of the first quarter of this year" + +[eebd5f32-b16d-4ecd-91a0-584b0364b7ed] +description = "Q4 in the second quarter translates to the last workday of the fourth quarter of this year" + +[c920886c-44ad-4d34-a156-dc4176186581] +description = "Q3 in the fourth quarter translates to the last workday of the third quarter of next year" diff --git a/exercises/practice/swift-scheduling/build.gradle b/exercises/practice/swift-scheduling/build.gradle new file mode 100644 index 000000000..dd3862eb9 --- /dev/null +++ b/exercises/practice/swift-scheduling/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/practice/swift-scheduling/gradlew.bat b/exercises/practice/swift-scheduling/gradlew.bat new file mode 100644 index 000000000..93e3f59f1 --- /dev/null +++ b/exercises/practice/swift-scheduling/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/practice/swift-scheduling/src/main/java/SwiftScheduling.java b/exercises/practice/swift-scheduling/src/main/java/SwiftScheduling.java new file mode 100644 index 000000000..e5b85bec9 --- /dev/null +++ b/exercises/practice/swift-scheduling/src/main/java/SwiftScheduling.java @@ -0,0 +1,7 @@ +import java.time.LocalDateTime; + +public class SwiftScheduling { + public static LocalDateTime convertToDeliveryDate(LocalDateTime meetingStart, String description) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } +} diff --git a/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java b/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java new file mode 100644 index 000000000..03f6c6c9c --- /dev/null +++ b/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java @@ -0,0 +1,200 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.assertThat; + +class SwiftSchedulingTest { + @Test + @DisplayName("NOW at 9 AM") + void testNowAtNineAm() { + LocalDateTime meetingStart = LocalDateTime.parse("2012-02-13T09:00:00"); + LocalDateTime expected = LocalDateTime.parse("2012-02-13T11:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "NOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("ASAP before 1 PM") + void testAsapBeforeOnePm() { + LocalDateTime meetingStart = LocalDateTime.parse("1999-06-03T09:45:00"); + LocalDateTime expected = LocalDateTime.parse("1999-06-03T17:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "ASAP"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("ASAP at 1 PM") + void testAsapAtOnePm() { + LocalDateTime meetingStart = LocalDateTime.parse("2008-12-21T13:00:00"); + LocalDateTime expected = LocalDateTime.parse("2008-12-22T13:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "ASAP"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("ASAP after 1 PM") + void testAsapAfterOnePm() { + LocalDateTime meetingStart = LocalDateTime.parse("2008-12-21T14:50:00"); + LocalDateTime expected = LocalDateTime.parse("2008-12-22T13:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "ASAP"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("EOW on Monday") + void testEowOnMonday() { + LocalDateTime meetingStart = LocalDateTime.parse("2025-02-03T16:00:00"); + LocalDateTime expected = LocalDateTime.parse("2025-02-07T17:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("EOW on Tuesday") + void testEowOnTuesday() { + LocalDateTime meetingStart = LocalDateTime.parse("1997-04-29T10:50:00"); + LocalDateTime expected = LocalDateTime.parse("1997-05-02T17:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("EOW on Wednesday") + void testEowOnWednesday() { + LocalDateTime meetingStart = LocalDateTime.parse("2005-09-14T11:00:00"); + LocalDateTime expected = LocalDateTime.parse("2005-09-16T17:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("EOW on Thursday") + void testEowOnThursday() { + LocalDateTime meetingStart = LocalDateTime.parse("2011-05-19T08:30:00"); + LocalDateTime expected = LocalDateTime.parse("2011-05-22T20:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("EOW on Friday") + void testEowOnFriday() { + LocalDateTime meetingStart = LocalDateTime.parse("2022-08-05T14:00:00"); + LocalDateTime expected = LocalDateTime.parse("2022-08-07T20:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("EOW in leap year") + void testEowDuringLeapYear() { + LocalDateTime meetingStart = LocalDateTime.parse("2008-02-25T10:30:00"); + LocalDateTime expected = LocalDateTime.parse("2008-02-29T17:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("2M in January") + void test2MInJanuary() { + LocalDateTime meetingStart = LocalDateTime.parse("2007-01-02T14:15:00"); + LocalDateTime expected = LocalDateTime.parse("2007-02-01T08:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "2M"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("11M in November") + void test11MInNovember() { + LocalDateTime meetingStart = LocalDateTime.parse("2013-11-21T15:30:00"); + LocalDateTime expected = LocalDateTime.parse("2014-11-03T08:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "11M"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("4M in November") + void test4MInNovember() { + LocalDateTime meetingStart = LocalDateTime.parse("2019-11-18T15:15:00"); + LocalDateTime expected = LocalDateTime.parse("2020-04-01T08:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "4M"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Q1 in Q1") + void testQ1InQ1() { + LocalDateTime meetingStart = LocalDateTime.parse("2003-01-01T10:45:00"); + LocalDateTime expected = LocalDateTime.parse("2003-03-31T08:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "Q1"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Q4 in Q2") + void testQ4InQ2() { + LocalDateTime meetingStart = LocalDateTime.parse("2001-04-09T09:00:00"); + LocalDateTime expected = LocalDateTime.parse("2001-12-31T08:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "Q4"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Q3 in Q4") + void testQ3InQ4() { + LocalDateTime meetingStart = LocalDateTime.parse("2022-10-06T11:00:00"); + LocalDateTime expected = LocalDateTime.parse("2023-09-29T08:00:00"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "Q3"); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 61f9b0991..2955a15dc 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -149,6 +149,7 @@ include 'practice:state-of-tic-tac-toe' // include 'practice:strain' // deprecated include 'practice:sublist' include 'practice:sum-of-multiples' +include 'practice:swift-scheduling' include 'practice:tournament' include 'practice:transpose' include 'practice:tree-building' From 9fd29ee8234b078a879e4254d867f03e62dc8a66 Mon Sep 17 00:00:00 2001 From: keiravillekode Date: Tue, 15 Jul 2025 14:44:49 +1000 Subject: [PATCH 36/67] ci uses ubuntu-24.04 (#2974) --- .github/workflows/java.yml | 6 +++--- .github/workflows/markdown.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 1a4953152..b1efb49ed 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -13,7 +13,7 @@ on: jobs: build: name: Check if tests compile cleanly with starter sources - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 @@ -27,7 +27,7 @@ jobs: lint: name: Lint Java files using Checkstyle - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 @@ -41,7 +41,7 @@ jobs: test: name: Test all exercises using java-test-runner - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Test all exercises using java-test-runner diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index abe9683a5..cb23011e0 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -15,7 +15,7 @@ permissions: jobs: lint: name: Lint Markdown files - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Lint markdown From ae8a886fe21cffca13d1c5ee45845b3060f70884 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:24:46 +0530 Subject: [PATCH 37/67] =?UTF-8?q?=F0=9F=A4=96=20Auto-sync=20docs,=20metada?= =?UTF-8?q?ta,=20and=20filepaths=20(#2973)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kahgoh <773175+kahgoh@users.noreply.github.com> Co-authored-by: Jagdish Prajapati --- exercises/practice/luhn/.docs/instructions.md | 2 +- exercises/practice/piecing-it-together/.meta/config.json | 3 +++ exercises/practice/swift-scheduling/.meta/config.json | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md index df2e304a3..7702c6bbb 100644 --- a/exercises/practice/luhn/.docs/instructions.md +++ b/exercises/practice/luhn/.docs/instructions.md @@ -41,7 +41,7 @@ If the sum is evenly divisible by 10, the original number is valid. ### Invalid Canadian SIN -The number to be checked is `066 123 468`. +The number to be checked is `066 123 478`. We start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. diff --git a/exercises/practice/piecing-it-together/.meta/config.json b/exercises/practice/piecing-it-together/.meta/config.json index 76e6e6cbf..b58751cb9 100644 --- a/exercises/practice/piecing-it-together/.meta/config.json +++ b/exercises/practice/piecing-it-together/.meta/config.json @@ -14,6 +14,9 @@ ], "editor": [ "src/main/java/JigsawInfo.java" + ], + "invalidator": [ + "build.gradle" ] }, "blurb": "Fill in missing jigsaw puzzle details from partial data", diff --git a/exercises/practice/swift-scheduling/.meta/config.json b/exercises/practice/swift-scheduling/.meta/config.json index d555a97e2..2c2db5875 100644 --- a/exercises/practice/swift-scheduling/.meta/config.json +++ b/exercises/practice/swift-scheduling/.meta/config.json @@ -11,6 +11,9 @@ ], "example": [ ".meta/src/reference/java/SwiftScheduling.java" + ], + "invalidator": [ + "build.gradle" ] }, "blurb": "Convert delivery date descriptions to actual delivery dates.", From f02bae3461ba3e18d6902212f8838731088d0986 Mon Sep 17 00:00:00 2001 From: Emmanuel <156767266+EmmanuelBerkowicz@users.noreply.github.com> Date: Tue, 22 Jul 2025 21:05:35 +1000 Subject: [PATCH 38/67] Add @DisplayName to Test Methods in Practice Exercises #2971 (#2972) * Add @DisplayName to Test Methods in Practice Exercises #2971 Add @DisplayName annotations to swift-scheduling tests Replace existing @DisplayName annotations with canonical descriptions from problem-specifications to ensure consistency across tracks. * Update exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java Co-authored-by: Jagdish Prajapati --------- Co-authored-by: Jagdish Prajapati --- .../src/test/java/SwiftSchedulingTest.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java b/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java index 03f6c6c9c..ffde7b7a0 100644 --- a/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java +++ b/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java @@ -8,7 +8,7 @@ class SwiftSchedulingTest { @Test - @DisplayName("NOW at 9 AM") + @DisplayName("NOW translates to two hours later") void testNowAtNineAm() { LocalDateTime meetingStart = LocalDateTime.parse("2012-02-13T09:00:00"); LocalDateTime expected = LocalDateTime.parse("2012-02-13T11:00:00"); @@ -20,7 +20,7 @@ void testNowAtNineAm() { @Disabled("Remove to run test") @Test - @DisplayName("ASAP before 1 PM") + @DisplayName("ASAP before one in the afternoon translates to today at five in the afternoon") void testAsapBeforeOnePm() { LocalDateTime meetingStart = LocalDateTime.parse("1999-06-03T09:45:00"); LocalDateTime expected = LocalDateTime.parse("1999-06-03T17:00:00"); @@ -32,7 +32,7 @@ void testAsapBeforeOnePm() { @Disabled("Remove to run test") @Test - @DisplayName("ASAP at 1 PM") + @DisplayName("ASAP at one in the afternoon translates to tomorrow at one in the afternoon") void testAsapAtOnePm() { LocalDateTime meetingStart = LocalDateTime.parse("2008-12-21T13:00:00"); LocalDateTime expected = LocalDateTime.parse("2008-12-22T13:00:00"); @@ -44,7 +44,7 @@ void testAsapAtOnePm() { @Disabled("Remove to run test") @Test - @DisplayName("ASAP after 1 PM") + @DisplayName("ASAP after one in the afternoon translates to tomorrow at one in the afternoon") void testAsapAfterOnePm() { LocalDateTime meetingStart = LocalDateTime.parse("2008-12-21T14:50:00"); LocalDateTime expected = LocalDateTime.parse("2008-12-22T13:00:00"); @@ -56,7 +56,7 @@ void testAsapAfterOnePm() { @Disabled("Remove to run test") @Test - @DisplayName("EOW on Monday") + @DisplayName("EOW on Monday translates to Friday at five in the afternoon") void testEowOnMonday() { LocalDateTime meetingStart = LocalDateTime.parse("2025-02-03T16:00:00"); LocalDateTime expected = LocalDateTime.parse("2025-02-07T17:00:00"); @@ -68,7 +68,7 @@ void testEowOnMonday() { @Disabled("Remove to run test") @Test - @DisplayName("EOW on Tuesday") + @DisplayName("EOW on Tuesday translates to Friday at five in the afternoon") void testEowOnTuesday() { LocalDateTime meetingStart = LocalDateTime.parse("1997-04-29T10:50:00"); LocalDateTime expected = LocalDateTime.parse("1997-05-02T17:00:00"); @@ -80,7 +80,7 @@ void testEowOnTuesday() { @Disabled("Remove to run test") @Test - @DisplayName("EOW on Wednesday") + @DisplayName("EOW on Wednesday translates to Friday at five in the afternoon") void testEowOnWednesday() { LocalDateTime meetingStart = LocalDateTime.parse("2005-09-14T11:00:00"); LocalDateTime expected = LocalDateTime.parse("2005-09-16T17:00:00"); @@ -92,7 +92,7 @@ void testEowOnWednesday() { @Disabled("Remove to run test") @Test - @DisplayName("EOW on Thursday") + @DisplayName("EOW on Thursday translates to Sunday at eight in the evening") void testEowOnThursday() { LocalDateTime meetingStart = LocalDateTime.parse("2011-05-19T08:30:00"); LocalDateTime expected = LocalDateTime.parse("2011-05-22T20:00:00"); @@ -104,7 +104,7 @@ void testEowOnThursday() { @Disabled("Remove to run test") @Test - @DisplayName("EOW on Friday") + @DisplayName("EOW on Friday translates to Sunday at eight in the evening") void testEowOnFriday() { LocalDateTime meetingStart = LocalDateTime.parse("2022-08-05T14:00:00"); LocalDateTime expected = LocalDateTime.parse("2022-08-07T20:00:00"); @@ -116,7 +116,7 @@ void testEowOnFriday() { @Disabled("Remove to run test") @Test - @DisplayName("EOW in leap year") + @DisplayName("EOW translates to leap day") void testEowDuringLeapYear() { LocalDateTime meetingStart = LocalDateTime.parse("2008-02-25T10:30:00"); LocalDateTime expected = LocalDateTime.parse("2008-02-29T17:00:00"); @@ -128,7 +128,9 @@ void testEowDuringLeapYear() { @Disabled("Remove to run test") @Test - @DisplayName("2M in January") + @DisplayName( + "2M before the second month of this year translates to the first workday of the second month of this year" + ) void test2MInJanuary() { LocalDateTime meetingStart = LocalDateTime.parse("2007-01-02T14:15:00"); LocalDateTime expected = LocalDateTime.parse("2007-02-01T08:00:00"); @@ -140,7 +142,7 @@ void test2MInJanuary() { @Disabled("Remove to run test") @Test - @DisplayName("11M in November") + @DisplayName("11M in the eleventh month translates to the first workday of the eleventh month of next year") void test11MInNovember() { LocalDateTime meetingStart = LocalDateTime.parse("2013-11-21T15:30:00"); LocalDateTime expected = LocalDateTime.parse("2014-11-03T08:00:00"); @@ -152,7 +154,7 @@ void test11MInNovember() { @Disabled("Remove to run test") @Test - @DisplayName("4M in November") + @DisplayName("4M in the ninth month translates to the first workday of the fourth month of next year") void test4MInNovember() { LocalDateTime meetingStart = LocalDateTime.parse("2019-11-18T15:15:00"); LocalDateTime expected = LocalDateTime.parse("2020-04-01T08:00:00"); @@ -164,7 +166,7 @@ void test4MInNovember() { @Disabled("Remove to run test") @Test - @DisplayName("Q1 in Q1") + @DisplayName("Q1 in the first quarter translates to the last workday of the first quarter of this year") void testQ1InQ1() { LocalDateTime meetingStart = LocalDateTime.parse("2003-01-01T10:45:00"); LocalDateTime expected = LocalDateTime.parse("2003-03-31T08:00:00"); @@ -176,7 +178,7 @@ void testQ1InQ1() { @Disabled("Remove to run test") @Test - @DisplayName("Q4 in Q2") + @DisplayName("Q4 in the second quarter translates to the last workday of the fourth quarter of this year") void testQ4InQ2() { LocalDateTime meetingStart = LocalDateTime.parse("2001-04-09T09:00:00"); LocalDateTime expected = LocalDateTime.parse("2001-12-31T08:00:00"); @@ -188,7 +190,7 @@ void testQ4InQ2() { @Disabled("Remove to run test") @Test - @DisplayName("Q3 in Q4") + @DisplayName("Q3 in the fourth quarter translates to the last workday of the third quarter of next year") void testQ3InQ4() { LocalDateTime meetingStart = LocalDateTime.parse("2022-10-06T11:00:00"); LocalDateTime expected = LocalDateTime.parse("2023-09-29T08:00:00"); From 3d300eeb4ce4ffbcf257fd616a07e5e659001fa4 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Sat, 26 Jul 2025 17:05:29 +0530 Subject: [PATCH 39/67] Add intergalactic transmission (#2969) Co-authored-by: Kah Goh --- config.json | 8 + .../.docs/instructions.append.md | 3 + .../.docs/instructions.md | 54 ++++ .../.docs/introduction.md | 23 ++ .../.meta/config.json | 19 ++ .../java/IntergalacticTransmission.java | 82 ++++++ .../.meta/tests.toml | 88 ++++++ .../intergalactic-transmission/build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../intergalactic-transmission/gradlew | 249 +++++++++++++++++ .../intergalactic-transmission/gradlew.bat | 92 +++++++ .../main/java/IntergalacticTransmission.java | 13 + .../java/IntergalacticTransmissionTest.java | 250 ++++++++++++++++++ exercises/settings.gradle | 1 + 15 files changed, 913 insertions(+) create mode 100644 exercises/practice/intergalactic-transmission/.docs/instructions.append.md create mode 100644 exercises/practice/intergalactic-transmission/.docs/instructions.md create mode 100644 exercises/practice/intergalactic-transmission/.docs/introduction.md create mode 100644 exercises/practice/intergalactic-transmission/.meta/config.json create mode 100644 exercises/practice/intergalactic-transmission/.meta/src/reference/java/IntergalacticTransmission.java create mode 100644 exercises/practice/intergalactic-transmission/.meta/tests.toml create mode 100644 exercises/practice/intergalactic-transmission/build.gradle create mode 100644 exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.properties create mode 100644 exercises/practice/intergalactic-transmission/gradlew create mode 100644 exercises/practice/intergalactic-transmission/gradlew.bat create mode 100644 exercises/practice/intergalactic-transmission/src/main/java/IntergalacticTransmission.java create mode 100644 exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java diff --git a/config.json b/config.json index 1356cbf81..642132dee 100644 --- a/config.json +++ b/config.json @@ -1442,6 +1442,14 @@ ], "difficulty": 6 }, + { + "slug": "intergalactic-transmission", + "name": "Intergalactic Transmission", + "uuid": "b1c6dfc2-414b-45b2-9277-8b9f8bb3bcf3", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, { "slug": "anagram", "name": "Anagram", diff --git a/exercises/practice/intergalactic-transmission/.docs/instructions.append.md b/exercises/practice/intergalactic-transmission/.docs/instructions.append.md new file mode 100644 index 000000000..a5f1cad0d --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.docs/instructions.append.md @@ -0,0 +1,3 @@ +# Instructions append + +Although we're dealing with bytes of data, the inputs and outputs are `List` (instead of `byte[]`) to avoid the need to cast or convert negative values for bytes 128 to 255. diff --git a/exercises/practice/intergalactic-transmission/.docs/instructions.md b/exercises/practice/intergalactic-transmission/.docs/instructions.md new file mode 100644 index 000000000..549708817 --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.docs/instructions.md @@ -0,0 +1,54 @@ +# Instructions + +Your job is to help implement + +- the transmitter, which calculates the transmission sequence, and +- the receiver, which decodes it. + +A parity bit is simple way of detecting transmission errors. +The transmitters and receivers can only transmit and receive _exactly_ eight bits at a time (including the parity bit). +The parity bit is set so that there is an _even_ number of 1 bits in each transmission, and the parity bit is always the first bit from the right. +So if the receiver receives `11000001`, `01110101` or `01000000` (i.e. a transmission with an odd number of 1 bits), it knows there is an error. + +However, messages are rarely this short, and need to be transmitted in a sequence when they are longer. + +For example, consider the message `11000000 00000001 11000000 11011110` (or `C0 01 C0 DE` in hex). + +Since each transmission contains exactly eight bits, it can only contain seven bits of data and the parity bit. +A parity bit must then be inserted after every seven bits of data: + +```text +11000000 00000001 11000000 11011110 + ↑ ↑ ↑ ↑ (7th bits) +``` + +The transmission sequence for this message looks like this: + +```text +1100000_ 0000000_ 0111000_ 0001101_ 1110 + ↑ ↑ ↑ ↑ (parity bits) +``` + +The data in the first transmission in the sequence (`1100000`) has two 1 bits (an even number), so the parity bit is 0. +The first transmission becomes `11000000` (or `C0` in hex). + +The data in the next transmission (`0000000`) has zero 1 bits (an even number again), so the parity bit is 0 again. +The second transmission thus becomes `00000000` (or `00` in hex). + +The data for the next two transmissions (`0111000` and `0001101`) have three 1 bits. +Their parity bits are set to 1 so that they have an even number of 1 bits in the transmission. +They are transmitted as `01110001` and `00011011` (or `71` and `1B` in hex). + +The last transmission (`1110`) has only four bits of data. +Since exactly eight bits are transmitted at a time and the parity bit is the rightmost bit, three 0 bits and then the parity bit are added to make up eight bits. +It now looks like this (where `_` is the parity bit): + +```text +1110 000_ + ↑↑↑ (added 0 bits) +``` + +There is an odd number of 1 bits again, so the parity bit is 1. +The last transmission in the sequence becomes `11100001` (or `E1` in hex). + +The entire transmission sequence for this message is `11000000 00000000 01110001 00011011 11100001` (or `C0 00 71 1B E1` in hex). diff --git a/exercises/practice/intergalactic-transmission/.docs/introduction.md b/exercises/practice/intergalactic-transmission/.docs/introduction.md new file mode 100644 index 000000000..f19dffbea --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.docs/introduction.md @@ -0,0 +1,23 @@ +# Introduction + +Trillions upon trillions of messages zip between Earth and neighboring galaxies every millisecond. +But transmitting over such long distances is tricky. +Pesky solar flares, temporal distortions, stray forces, and even the flap of a space butterfly's wing can cause a random bit to change during transmission. + +Now imagine the consequences: + +- Crashing the Intergalactic Share Market when "buy low" turns to "sell now". +- Losing contact with the Kepler Whirl system when "save new worm hole" becomes "cave new worm hole". +- Or plunging the universe into existential horror by replacing a cowboy emoji 🤠 with a clown emoji 🤡. + +Detecting corrupted messages isn't just important — it's critical. +The receiver _must_ know when something has gone wrong before disaster strikes. + +But how? +Scientists and engineers from across the universe have been battling this problem for eons. +Entire cosmic AI superclusters churn through the data. +And then, one day, a legend resurfaces — an ancient, powerful method, whispered in debugging forums, muttered by engineers who've seen too much... + +The Parity Bit! + +A method so simple, so powerful, that it might just save interstellar communication. diff --git a/exercises/practice/intergalactic-transmission/.meta/config.json b/exercises/practice/intergalactic-transmission/.meta/config.json new file mode 100644 index 000000000..6d97d1430 --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "jagdish-15" + ], + "files": { + "solution": [ + "src/main/java/IntergalacticTransmission.java" + ], + "test": [ + "src/test/java/IntergalacticTransmissionTest.java" + ], + "example": [ + ".meta/src/reference/java/IntergalacticTransmission.java" + ] + }, + "blurb": "Add parity bits to a message for transmission", + "source": "Kah Goh", + "source_url": "https://github.com/exercism/problem-specifications/pull/2543" +} diff --git a/exercises/practice/intergalactic-transmission/.meta/src/reference/java/IntergalacticTransmission.java b/exercises/practice/intergalactic-transmission/.meta/src/reference/java/IntergalacticTransmission.java new file mode 100644 index 000000000..d27721e1d --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.meta/src/reference/java/IntergalacticTransmission.java @@ -0,0 +1,82 @@ +import java.util.ArrayList; +import java.util.List; + +public class IntergalacticTransmission { + + private static final byte INIT_UPPER_MASK = (byte) 0xFE; + + public static List getTransmitSequence(List message) { + List transmitSeq = new ArrayList<>(); + byte carry = 0; + byte upperMask = INIT_UPPER_MASK; + + for (int i = 0; i < message.size(); i++) { + byte currentByte = message.get(i).byteValue(); + + if (upperMask == 0) { + transmitSeq.add((int) addParity(carry) & 0xFF); + carry = 0; + upperMask = (byte) 0xFE; + } + + int shiftPlaces = Integer.numberOfTrailingZeros(upperMask & 0xFF); + int current = ((carry & 0xFF) << (8 - shiftPlaces)) | ((currentByte & 0xFF) >>> shiftPlaces); + transmitSeq.add((int) addParity((byte) current) & 0xFF); + + carry = (byte) (currentByte & ~upperMask); + upperMask = (byte) (upperMask << 1); + } + + if (upperMask != INIT_UPPER_MASK) { + byte lastGroup = (byte) ((carry & 0xFF) << Integer.bitCount(upperMask & 0xFF)); + transmitSeq.add((int) addParity(lastGroup) & 0xFF); + } + + return transmitSeq; + } + + private static byte addParity(byte source) { + if (Integer.bitCount(source & 0x7F) % 2 == 0) { + return (byte) (source << 1); + } else { + return (byte) ((source << 1) | 1); + } + } + + public static List decodeSequence(List receivedSeq) { + if (receivedSeq.isEmpty()) { + return new ArrayList<>(); + } + + List decodedMessage = new ArrayList<>(); + byte byteToAdd = 0x00; + byte upperMask = (byte) 0xFF; + + for (int i = 0; i < receivedSeq.size(); i++) { + byte currentByte = receivedSeq.get(i).byteValue(); + + if (upperMask == (byte) 0xFF) { + byteToAdd = getByteData(currentByte); + upperMask = (byte) 0x80; + continue; + } + + byte currentByteData = getByteData(currentByte); + int shiftPlaces = Integer.numberOfTrailingZeros(upperMask & 0xFF); + byte contribution = (byte) ((currentByteData & 0xFF) >>> shiftPlaces); + decodedMessage.add((byteToAdd | contribution) & 0xFF); + + byteToAdd = (byte) (((currentByteData & ~(upperMask | 0x01)) & 0xFF) << Integer.bitCount(upperMask & 0xFF)); + upperMask = (byte) (((upperMask & 0xFF) >>> 1) | 0x80); + } + + return decodedMessage; + } + + private static byte getByteData(byte data) { + if (Integer.bitCount(data & 0xFF) % 2 != 0) { + throw new IllegalArgumentException("Byte has incorrect parity"); + } + return (byte) (data & 0xFE); + } +} diff --git a/exercises/practice/intergalactic-transmission/.meta/tests.toml b/exercises/practice/intergalactic-transmission/.meta/tests.toml new file mode 100644 index 000000000..64a8aaced --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.meta/tests.toml @@ -0,0 +1,88 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[f99d4046-b429-4582-9324-f0bcac7ab51c] +description = "calculate transmit sequences -> empty message" + +[ee27ea2d-8999-4f23-9275-8f6879545f86] +description = "calculate transmit sequences -> 0x00 is transmitted as 0x0000" + +[97f27f98-8020-402d-be85-f21ba54a6df0] +description = "calculate transmit sequences -> 0x02 is transmitted as 0x0300" + +[24712fb9-0336-4e2f-835e-d2350f29c420] +description = "calculate transmit sequences -> 0x06 is transmitted as 0x0600" + +[7630b5a9-dba1-4178-b2a0-4a376f7414e0] +description = "calculate transmit sequences -> 0x05 is transmitted as 0x0581" + +[ab4fe80b-ef8e-4a99-b4fb-001937af415d] +description = "calculate transmit sequences -> 0x29 is transmitted as 0x2881" + +[4e200d84-593b-4449-b7c0-4de1b6a0955e] +description = "calculate transmit sequences -> 0xc001c0de is transmitted as 0xc000711be1" + +[fbc537e9-6b21-4f4a-8c2b-9cf9b702a9b7] +description = "calculate transmit sequences -> six byte message" + +[d5b75adf-b5fc-4f77-b4ab-77653e30f07c] +description = "calculate transmit sequences -> seven byte message" + +[6d8b297b-da1d-435e-bcd7-55fbb1400e73] +description = "calculate transmit sequences -> eight byte message" + +[54a0642a-d5aa-490c-be89-8e171a0cab6f] +description = "calculate transmit sequences -> twenty byte message" + +[9a8084dd-3336-474c-90cb-8a852524604d] +description = "decode received messages -> empty message" + +[879af739-0094-4736-9127-bd441b1ddbbf] +description = "decode received messages -> zero message" + +[7a89eeef-96c5-4329-a246-ec181a8e959a] +description = "decode received messages -> 0x0300 is decoded to 0x02" + +[3e515af7-8b62-417f-960c-3454bca7f806] +description = "decode received messages -> 0x0581 is decoded to 0x05" + +[a1b4a3f7-9f05-4b7a-b86e-d7c6fc3f16a9] +description = "decode received messages -> 0x2881 is decoded to 0x29" + +[2e99d617-4c91-4ad5-9217-e4b2447d6e4a] +description = "decode received messages -> first byte has wrong parity" + +[507e212d-3dae-42e8-88b4-2223838ff8d2] +description = "decode received messages -> second byte has wrong parity" + +[b985692e-6338-46c7-8cea-bc38996d4dfd] +description = "decode received messages -> 0xcf4b00 is decoded to 0xce94" + +[7a1f4d48-696d-4679-917c-21b7da3ff3fd] +description = "decode received messages -> 0xe2566500 is decoded to 0xe2ad90" + +[467549dc-a558-443b-80c5-ff3d4eb305d4] +description = "decode received messages -> six byte message" + +[1f3be5fb-093a-4661-9951-c1c4781c71ea] +description = "decode received messages -> seven byte message" + +[6065b8b3-9dcd-45c9-918c-b427cfdb28c1] +description = "decode received messages -> last byte has wrong parity" + +[98af97b7-9cca-4c4c-9de3-f70e227a4cb1] +description = "decode received messages -> eight byte message" + +[aa7d4785-2bb9-43a4-a38a-203325c464fb] +description = "decode received messages -> twenty byte message" + +[4c86e034-b066-42ac-8497-48f9bc1723c1] +description = "decode received messages -> wrong parity on 16th byte" diff --git a/exercises/practice/intergalactic-transmission/build.gradle b/exercises/practice/intergalactic-transmission/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/practice/intergalactic-transmission/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/practice/intergalactic-transmission/gradlew.bat b/exercises/practice/intergalactic-transmission/gradlew.bat new file mode 100644 index 000000000..25da30dbd --- /dev/null +++ b/exercises/practice/intergalactic-transmission/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/practice/intergalactic-transmission/src/main/java/IntergalacticTransmission.java b/exercises/practice/intergalactic-transmission/src/main/java/IntergalacticTransmission.java new file mode 100644 index 000000000..57541078f --- /dev/null +++ b/exercises/practice/intergalactic-transmission/src/main/java/IntergalacticTransmission.java @@ -0,0 +1,13 @@ +import java.util.List; + +public class IntergalacticTransmission { + + public static List getTransmitSequence(List message) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public static List decodeSequence(List sequence) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + +} diff --git a/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java b/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java new file mode 100644 index 000000000..c4ecdef3a --- /dev/null +++ b/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java @@ -0,0 +1,250 @@ +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class IntergalacticTransmissionTest { + + @Test + public void calculateTransmitSequencesEmptyMessage() { + List input = List.of(); + List expected = List.of(); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequences0x00IsTransmittedAs0x0000() { + List input = List.of(0x00); + List expected = List.of(0x00, 0x00); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequences0x02IsTransmittedAs0x0300() { + List input = List.of(0x02); + List expected = List.of(0x03, 0x00); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequences0x06IsTransmittedAs0x0600() { + List input = List.of(0x06); + List expected = List.of(0x06, 0x00); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequences0x05IsTransmittedAs0x0581() { + List input = List.of(0x05); + List expected = List.of(0x05, 0x81); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequences0x29IsTransmittedAs0x2881() { + List input = List.of(0x29); + List expected = List.of(0x28, 0x81); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequences0xc001c0deIsTransmittedAs0xc000711be1() { + List input = List.of(0xc0, 0x01, 0xc0, 0xde); + List expected = List.of(0xc0, 0x00, 0x71, 0x1b, 0xe1); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequencesSixByteMessage() { + List input = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x21); + List expected = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0x84); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequencesSevenByteMessage() { + List input = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x31, 0x21); + List expected = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x42); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequencesEightByteMessage() { + List input = List.of(0xc0, 0x01, 0x13, 0x37, 0xc0, 0xde, 0x21, 0x21); + List expected = List.of(0xc0, 0x00, 0x44, 0x66, 0x7d, 0x06, 0x78, 0x42, 0x21, 0x81); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void calculateTransmitSequencesTwentyByteMessage() { + List input = List.of( + 0x45, 0x78, 0x65, 0x72, 0x63, 0x69, 0x73, 0x6d, 0x20, 0x69, + 0x73, 0x20, 0x61, 0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x21); + List expected = List.of( + 0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, + 0x1b, 0x2e, 0x33, 0x03, 0x84, 0xee, 0x65, 0xb8, 0xdb, 0xed, + 0xd7, 0x28, 0x84); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeReceivedMessagesEmptyMessage() { + List input = List.of(); + List expected = List.of(); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeReceivedMessagesZeroMessage() { + List input = List.of(0x00, 0x00); + List expected = List.of(0x00); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeReceivedMessages0x0300IsDecodedTo0x02() { + List input = List.of(0x03, 0x00); + List expected = List.of(0x02); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeReceivedMessages0x0581IsDecodedTo0x05() { + List input = List.of(0x05, 0x81); + List expected = List.of(0x05); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeReceivedMessages0x2881IsDecodedTo0x29() { + List input = List.of(0x28, 0x81); + List expected = List.of(0x29); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeFirstByteWrongParity() { + List input = List.of(0x07, 0x00); + assertThrows(IllegalArgumentException.class, () + -> IntergalacticTransmission.decodeSequence(input)); + } + + @Disabled("Remove to run test") + @Test + public void decodeSecondByteWrongParity() { + List input = List.of(0x03, 0x68); + assertThrows(IllegalArgumentException.class, () + -> IntergalacticTransmission.decodeSequence(input)); + } + + @Disabled("Remove to run test") + @Test + public void decode0xcf4b00To0xce94() { + List input = List.of(0xcf, 0x4b, 0x00); + List expected = List.of(0xce, 0x94); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decode0xe2566500To0xe2ad90() { + List input = List.of(0xe2, 0x56, 0x65, 0x00); + List expected = List.of(0xe2, 0xad, 0x90); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeSixByteMessage() { + List input = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0x84); + List expected = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x21); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeSevenByteMessage() { + List input = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x42); + List expected = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x31, 0x21); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeLastByteWrongParity() { + List input = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x43); + assertThrows(IllegalArgumentException.class, () + -> IntergalacticTransmission.decodeSequence(input)); + } + + @Disabled("Remove to run test") + @Test + public void decodeEightByteMessage() { + List input = List.of(0xc0, 0x00, 0x44, 0x66, 0x7d, 0x06, 0x78, 0x42, 0x21, 0x81); + List expected = List.of(0xc0, 0x01, 0x13, 0x37, 0xc0, 0xde, 0x21, 0x21); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeTwentyByteMessage() { + List input = List.of( + 0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, 0x1b, + 0x2e, 0x33, 0x03, 0x84, 0xee, 0x65, 0xb8, 0xdb, 0xed, 0xd7, 0x28, 0x84); + List expected = List.of( + 0x45, 0x78, 0x65, 0x72, 0x63, 0x69, 0x73, 0x6d, 0x20, 0x69, + 0x73, 0x20, 0x61, 0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x21); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + public void decodeWrongParityOn16thByte() { + List input = List.of( + 0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, + 0x1b, 0x2e, 0x33, 0x03, 0x84, 0xef, 0x65, 0xb8, 0xdb, 0xed, 0xd7, 0x28, 0x84); + assertThrows(IllegalArgumentException.class, () + -> IntergalacticTransmission.decodeSequence(input)); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index 2955a15dc..c46f79877 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -77,6 +77,7 @@ include 'practice:hangman' include 'practice:hello-world' include 'practice:high-scores' include 'practice:house' +include 'practice:intergalactic-transmission' include 'practice:isbn-verifier' include 'practice:isogram' include 'practice:killer-sudoku-helper' From 51f3dd7e426c37113348fc047f7f2c483561fa29 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Thu, 31 Jul 2025 18:26:34 +0530 Subject: [PATCH 40/67] Fix position of intergalactic transmission in config.json (#2979) This is sort the exercises by difficulty, then exercise name. --- config.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config.json b/config.json index 642132dee..9805391b2 100644 --- a/config.json +++ b/config.json @@ -1296,6 +1296,14 @@ ], "difficulty": 6 }, + { + "slug": "intergalactic-transmission", + "name": "Intergalactic Transmission", + "uuid": "b1c6dfc2-414b-45b2-9277-8b9f8bb3bcf3", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, { "slug": "linked-list", "name": "Linked List", @@ -1442,14 +1450,6 @@ ], "difficulty": 6 }, - { - "slug": "intergalactic-transmission", - "name": "Intergalactic Transmission", - "uuid": "b1c6dfc2-414b-45b2-9277-8b9f8bb3bcf3", - "practices": [], - "prerequisites": [], - "difficulty": 6 - }, { "slug": "anagram", "name": "Anagram", From 342b6a857449fab9ed567e9804f8c11ee49493cb Mon Sep 17 00:00:00 2001 From: Emmanuel <156767266+EmmanuelBerkowicz@users.noreply.github.com> Date: Thu, 31 Jul 2025 23:07:49 +1000 Subject: [PATCH 41/67] Add split-second-stopwatch exercise #2968 (#2977) * Add split-second-stopwatch exercise #2968 Add split-second-stopwatch exercise Implement the split-second-stopwatch practice exercise with: - State management (ready/running/stopped) - Time tracking for current lap and total elapsed time - Lap functionality with previous laps history - Start, stop, reset operations with proper state validation - Error handling for invalid state transitions - Time formatting in HH:MM:SS format - Mock time advancement for testing Includes reference solution, starter implementation, and comprehensive test suite covering all canonical data test cases. * Add split-second-stopwatch exercise #2968 git add . git commit -m "Add split-second-stopwatch exercise Implement the split-second-stopwatch practice exercise with: - State management (ready/running/stopped) - Time tracking for current lap and total elapsed time - Lap functionality with previous laps history - Start, stop, reset operations with proper state validation - Error handling for invalid state transitions - Time formatting in HH:MM:SS format - Mock time advancement for testing Includes reference solution, starter implementation, and comprehensive test suite covering all canonical data test cases." * Add split-second-stopwatch exercise #2968 #2977 Gradle files added to new practice exercise. Gradle/wrapper directories and files copied into split-second-stopwatch exercise as requested. * Add split-second-stopwatch exercise #2968 #2977 Prerequisites and difficulty updated in main config.json file. * Add split-second-stopwatch exercise #2968 #2977 config.json format updated. * Add split-second-stopwatch exercise #2968 #2977 Add remaining gradle files to split-second-stopwatch exercise. * Update exercises/practice/split-second-stopwatch/src/test/java/SplitSecondStopwatchTest.java Co-authored-by: Kah Goh * Update exercises/practice/split-second-stopwatch/.meta/src/reference/java/SplitSecondStopwatch.java Co-authored-by: Kah Goh * Add split-second-stopwatch exercise #2968 #2977 main config.json updated to reorder new practice exercise. --------- Co-authored-by: Kah Goh Co-authored-by: Jagdish Prajapati --- config.json | 11 + .../.docs/instructions.md | 22 ++ .../.docs/introduction.md | 6 + .../split-second-stopwatch/.meta/config.json | 19 + .../reference/java/SplitSecondStopwatch.java | 125 +++++++ .../split-second-stopwatch/.meta/tests.toml | 97 +++++ .../split-second-stopwatch/build.gradle | 25 ++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../practice/split-second-stopwatch/gradlew | 249 +++++++++++++ .../split-second-stopwatch/gradlew.bat | 92 +++++ .../src/main/java/SplitSecondStopwatch.java | 37 ++ .../test/java/SplitSecondStopwatchTest.java | 339 ++++++++++++++++++ exercises/settings.gradle | 1 + 14 files changed, 1029 insertions(+) create mode 100644 exercises/practice/split-second-stopwatch/.docs/instructions.md create mode 100644 exercises/practice/split-second-stopwatch/.docs/introduction.md create mode 100644 exercises/practice/split-second-stopwatch/.meta/config.json create mode 100644 exercises/practice/split-second-stopwatch/.meta/src/reference/java/SplitSecondStopwatch.java create mode 100644 exercises/practice/split-second-stopwatch/.meta/tests.toml create mode 100644 exercises/practice/split-second-stopwatch/build.gradle create mode 100644 exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.jar create mode 100644 exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.properties create mode 100644 exercises/practice/split-second-stopwatch/gradlew create mode 100644 exercises/practice/split-second-stopwatch/gradlew.bat create mode 100644 exercises/practice/split-second-stopwatch/src/main/java/SplitSecondStopwatch.java create mode 100644 exercises/practice/split-second-stopwatch/src/test/java/SplitSecondStopwatchTest.java diff --git a/config.json b/config.json index 9805391b2..fa08384ff 100644 --- a/config.json +++ b/config.json @@ -842,6 +842,17 @@ ], "difficulty": 4 }, + { + "slug": "split-second-stopwatch", + "name": "Split-Second Stopwatch", + "uuid": "9510c0ae-9977-4260-8991-0e8e849094b0", + "practices": [], + "prerequisites": [ + "exceptions", + "if-else-statements" + ], + "difficulty": 4 + }, { "slug": "sum-of-multiples", "name": "Sum of Multiples", diff --git a/exercises/practice/split-second-stopwatch/.docs/instructions.md b/exercises/practice/split-second-stopwatch/.docs/instructions.md new file mode 100644 index 000000000..30bdc988d --- /dev/null +++ b/exercises/practice/split-second-stopwatch/.docs/instructions.md @@ -0,0 +1,22 @@ +# Instructions + +Your task is to build a stopwatch to keep precise track of lap times. + +The stopwatch uses four commands (start, stop, lap, and reset) to keep track of: + +1. The current lap's tracked time +2. Previously recorded lap times + +What commands can be used depends on which state the stopwatch is in: + +1. Ready: initial state +2. Running: tracking time +3. Stopped: not tracking time + +| Command | Begin state | End state | Effect | +| ------- | ----------- | --------- | -------------------------------------------------------- | +| Start | Ready | Running | Start tracking time | +| Start | Stopped | Running | Resume tracking time | +| Stop | Running | Stopped | Stop tracking time | +| Lap | Running | Running | Add current lap to previous laps, then reset current lap | +| Reset | Stopped | Ready | Reset current lap and clear previous laps | diff --git a/exercises/practice/split-second-stopwatch/.docs/introduction.md b/exercises/practice/split-second-stopwatch/.docs/introduction.md new file mode 100644 index 000000000..a84322477 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +You've always run for the thrill of it — no schedules, no timers, just the sound of your feet on the pavement. +But now that you've joined a competitive running crew, things are getting serious. +Training sessions are timed to the second, and every split second counts. +To keep pace, you've picked up the _Split-Second Stopwatch_ — a sleek, high-tech gadget that's about to become your new best friend. diff --git a/exercises/practice/split-second-stopwatch/.meta/config.json b/exercises/practice/split-second-stopwatch/.meta/config.json new file mode 100644 index 000000000..cde93a410 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "EmmanuelBerkowicz" + ], + "files": { + "solution": [ + "src/main/java/SplitSecondStopwatch.java" + ], + "test": [ + "src/test/java/SplitSecondStopwatchTest.java" + ], + "example": [ + ".meta/src/reference/java/SplitSecondStopwatch.java" + ] + }, + "blurb": "Keep track of time through a digital stopwatch.", + "source": "Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/pull/2547" +} diff --git a/exercises/practice/split-second-stopwatch/.meta/src/reference/java/SplitSecondStopwatch.java b/exercises/practice/split-second-stopwatch/.meta/src/reference/java/SplitSecondStopwatch.java new file mode 100644 index 000000000..c00a58cea --- /dev/null +++ b/exercises/practice/split-second-stopwatch/.meta/src/reference/java/SplitSecondStopwatch.java @@ -0,0 +1,125 @@ +import java.util.ArrayList; +import java.util.List; + +public class SplitSecondStopwatch { + + /** + * A split-second stopwatch that tracks elapsed time with lap functionality. + * Supports start, stop, reset, and lap operations with precise time tracking. + * Times are formatted in HH:MM:SS format with two-digit precision. + * + * @see + * Problem Specifications + * + */ + + private enum State { READY, RUNNING, STOPPED } + + private State state; + private long totalCompletedLaps; // Total time from completed laps + private long currentLapStart; // When current lap started + private long accumulated; // Accumulated time for current lap when stopped + private List previousLaps; + private long mockTime; + + public SplitSecondStopwatch() { + this.state = State.READY; + this.totalCompletedLaps = 0; + this.currentLapStart = 0; + this.accumulated = 0; + this.previousLaps = new ArrayList<>(); + this.mockTime = 0; + } + + public void start() { + if (state == State.RUNNING) { + throw new IllegalStateException("cannot start an already running stopwatch"); + } + + currentLapStart = mockTime; + state = State.RUNNING; + } + + public void stop() { + if (state != State.RUNNING) { + throw new IllegalStateException("cannot stop a stopwatch that is not running"); + } + + accumulated += mockTime - currentLapStart; + state = State.STOPPED; + } + + public void reset() { + if (state != State.STOPPED) { + throw new IllegalStateException("cannot reset a stopwatch that is not stopped"); + } + + state = State.READY; + totalCompletedLaps = 0; + currentLapStart = 0; + accumulated = 0; + previousLaps.clear(); + } + + public void lap() { + if (state != State.RUNNING) { + throw new IllegalStateException("cannot lap a stopwatch that is not running"); + } + + long currentLapTime = getCurrentLapTime(); + totalCompletedLaps += currentLapTime; + previousLaps.add(formatTime(currentLapTime)); + + // Reset current lap and restart + accumulated = 0; + currentLapStart = mockTime; + } + + public String state() { + return state.name().toLowerCase(); + } + + public String currentLap() { + return formatTime(getCurrentLapTime()); + } + + public String total() { + return formatTime(totalCompletedLaps + getCurrentLapTime()); + } + + public List previousLaps() { + return new ArrayList<>(previousLaps); + } + + public void advanceTime(String timeString) { + String[] parts = timeString.split(":"); + long hours = Long.parseLong(parts[0]); + long minutes = Long.parseLong(parts[1]); + long seconds = Long.parseLong(parts[2]); + + long milliseconds = (hours * 3600 + minutes * 60 + seconds) * 1000; + mockTime += milliseconds; + } + + private long getCurrentLapTime() { + switch (state) { + case READY: + return 0; + case RUNNING: + return accumulated + (mockTime - currentLapStart); + case STOPPED: + return accumulated; + default: + throw new IllegalStateException("Invalid state"); + } + } + + private String formatTime(long milliseconds) { + long totalSeconds = milliseconds / 1000; + long hours = totalSeconds / 3600; + long minutes = (totalSeconds % 3600) / 60; + long seconds = totalSeconds % 60; + + return String.format("%02d:%02d:%02d", hours, minutes, seconds); + } +} diff --git a/exercises/practice/split-second-stopwatch/.meta/tests.toml b/exercises/practice/split-second-stopwatch/.meta/tests.toml new file mode 100644 index 000000000..323cb7ae8 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/.meta/tests.toml @@ -0,0 +1,97 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[ddb238ea-99d4-4eaa-a81d-3c917a525a23] +description = "new stopwatch starts in ready state" + +[b19635d4-08ad-4ac3-b87f-aca10e844071] +description = "new stopwatch's current lap has no elapsed time" + +[492eb532-268d-43ea-8a19-2a032067d335] +description = "new stopwatch's total has no elapsed time" + +[8a892c1e-9ef7-4690-894e-e155a1fe4484] +description = "new stopwatch does not have previous laps" + +[5b2705b6-a584-4042-ba3a-4ab8d0ab0281] +description = "start from ready state changes state to running" + +[748235ce-1109-440b-9898-0a431ea179b6] +description = "start does not change previous laps" + +[491487b1-593d-423e-a075-aa78d449ff1f] +description = "start initiates time tracking for current lap" + +[a0a7ba2c-8db6-412c-b1b6-cb890e9b72ed] +description = "start initiates time tracking for total" + +[7f558a17-ef6d-4a5b-803a-f313af7c41d3] +description = "start cannot be called from running state" + +[32466eef-b2be-4d60-a927-e24fce52dab9] +description = "stop from running state changes state to stopped" + +[621eac4c-8f43-4d99-919c-4cad776d93df] +description = "stop pauses time tracking for current lap" + +[465bcc82-7643-41f2-97ff-5e817cef8db4] +description = "stop pauses time tracking for total" + +[b1ba7454-d627-41ee-a078-891b2ed266fc] +description = "stop cannot be called from ready state" + +[5c041078-0898-44dc-9d5b-8ebb5352626c] +description = "stop cannot be called from stopped state" + +[3f32171d-8fbf-46b6-bc2b-0810e1ec53b7] +description = "start from stopped state changes state to running" + +[626997cb-78d5-4fe8-b501-29fdef804799] +description = "start from stopped state resumes time tracking for current lap" + +[58487c53-ab26-471c-a171-807ef6363319] +description = "start from stopped state resumes time tracking for total" + +[091966e3-ed25-4397-908b-8bb0330118f8] +description = "lap adds current lap to previous laps" + +[1aa4c5ee-a7d5-4d59-9679-419deef3c88f] +description = "lap resets current lap and resumes time tracking" + +[4b46b92e-1b3f-46f6-97d2-0082caf56e80] +description = "lap continues time tracking for total" + +[ea75d36e-63eb-4f34-97ce-8c70e620bdba] +description = "lap cannot be called from ready state" + +[63731154-a23a-412d-a13f-c562f208eb1e] +description = "lap cannot be called from stopped state" + +[e585ee15-3b3f-4785-976b-dd96e7cc978b] +description = "stop does not change previous laps" + +[fc3645e2-86cf-4d11-97c6-489f031103f6] +description = "reset from stopped state changes state to ready" + +[20fbfbf7-68ad-4310-975a-f5f132886c4e] +description = "reset resets current lap" + +[00a8f7bb-dd5c-43e5-8705-3ef124007662] +description = "reset clears previous laps" + +[76cea936-6214-4e95-b6d1-4d4edcf90499] +description = "reset cannot be called from ready state" + +[ba4d8e69-f200-4721-b59e-90d8cf615153] +description = "reset cannot be called from running state" + +[0b01751a-cb57-493f-bb86-409de6e84306] +description = "supports very long laps" diff --git a/exercises/practice/split-second-stopwatch/build.gradle b/exercises/practice/split-second-stopwatch/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/practice/split-second-stopwatch/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "java" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation platform("org.junit:junit-bom:5.10.0") + testImplementation "org.junit.jupiter:junit-jupiter" + testImplementation "org.assertj:assertj-core:3.25.1" + + testRuntimeOnly "org.junit.platform:junit-platform-launcher" +} + +test { + useJUnitPlatform() + + testLogging { + exceptionFormat = "full" + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c z&{(nX_ht=QbJ%DzAya}!&uNu!4V0xI)QE$SY__m)SAKcN0P(&JcoK*Lxr@P zY&P=}&B3*UWNlc|&$Oh{BEqwK2+N2U$4WB7Fd|aIal`FGANUa9E-O)!gV`((ZGCc$ zBJA|FFrlg~9OBp#f7aHodCe{6= zay$6vN~zj1ddMZ9gQ4p32(7wD?(dE>KA2;SOzXRmPBiBc6g`eOsy+pVcHu=;Yd8@{ zSGgXf@%sKKQz~;!J;|2fC@emm#^_rnO0esEn^QxXgJYd`#FPWOUU5b;9eMAF zZhfiZb|gk8aJIw*YLp4!*(=3l8Cp{(%p?ho22*vN9+5NLV0TTazNY$B5L6UKUrd$n zjbX%#m7&F#U?QNOBXkiiWB*_tk+H?N3`vg;1F-I+83{M2!8<^nydGr5XX}tC!10&e z7D36bLaB56WrjL&HiiMVtpff|K%|*{t*ltt^5ood{FOG0<>k&1h95qPio)2`eL${YAGIx(b4VN*~nKn6E~SIQUuRH zQ+5zP6jfnP$S0iJ@~t!Ai3o`X7biohli;E zT#yXyl{bojG@-TGZzpdVDXhbmF%F9+-^YSIv|MT1l3j zrxOFq>gd2%U}?6}8mIj?M zc077Zc9fq(-)4+gXv?Az26IO6eV`RAJz8e3)SC7~>%rlzDwySVx*q$ygTR5kW2ds- z!HBgcq0KON9*8Ff$X0wOq$`T7ml(@TF)VeoF}x1OttjuVHn3~sHrMB++}f7f9H%@f z=|kP_?#+fve@{0MlbkC9tyvQ_R?lRdRJ@$qcB(8*jyMyeME5ns6ypVI1Xm*Zr{DuS zZ!1)rQfa89c~;l~VkCiHI|PCBd`S*2RLNQM8!g9L6?n`^evQNEwfO@&JJRme+uopQX0%Jo zgd5G&#&{nX{o?TQwQvF1<^Cg3?2co;_06=~Hcb6~4XWpNFL!WU{+CK;>gH%|BLOh7@!hsa(>pNDAmpcuVO-?;Bic17R}^|6@8DahH)G z!EmhsfunLL|3b=M0MeK2vqZ|OqUqS8npxwge$w-4pFVXFq$_EKrZY?BuP@Az@(k`L z`ViQBSk`y+YwRT;&W| z2e3UfkCo^uTA4}Qmmtqs+nk#gNr2W4 zTH%hhErhB)pkXR{B!q5P3-OM+M;qu~f>}IjtF%>w{~K-0*jPVLl?Chz&zIdxp}bjx zStp&Iufr58FTQ36AHU)0+CmvaOpKF;W@sMTFpJ`j;3d)J_$tNQI^c<^1o<49Z(~K> z;EZTBaVT%14(bFw2ob@?JLQ2@(1pCdg3S%E4*dJ}dA*v}_a4_P(a`cHnBFJxNobAv zf&Zl-Yt*lhn-wjZsq<9v-IsXxAxMZ58C@e0!rzhJ+D@9^3~?~yllY^s$?&oNwyH!#~6x4gUrfxplCvK#!f z$viuszW>MFEcFL?>ux*((!L$;R?xc*myjRIjgnQX79@UPD$6Dz0jutM@7h_pq z0Zr)#O<^y_K6jfY^X%A-ip>P%3saX{!v;fxT-*0C_j4=UMH+Xth(XVkVGiiKE#f)q z%Jp=JT)uy{&}Iq2E*xr4YsJ5>w^=#-mRZ4vPXpI6q~1aFwi+lQcimO45V-JXP;>(Q zo={U`{=_JF`EQj87Wf}{Qy35s8r1*9Mxg({CvOt}?Vh9d&(}iI-quvs-rm~P;eRA@ zG5?1HO}puruc@S{YNAF3vmUc2B4!k*yi))<5BQmvd3tr}cIs#9)*AX>t`=~{f#Uz0 z0&Nk!7sSZwJe}=)-R^$0{yeS!V`Dh7w{w5rZ9ir!Z7Cd7dwZcK;BT#V0bzTt>;@Cl z#|#A!-IL6CZ@eHH!CG>OO8!%G8&8t4)Ro@}USB*k>oEUo0LsljsJ-%5Mo^MJF2I8- z#v7a5VdJ-Cd%(a+y6QwTmi+?f8Nxtm{g-+WGL>t;s#epv7ug>inqimZCVm!uT5Pf6 ziEgQt7^%xJf#!aPWbuC_3Nxfb&CFbQy!(8ANpkWLI4oSnH?Q3f?0k1t$3d+lkQs{~(>06l&v|MpcFsyAv zin6N!-;pggosR*vV=DO(#+}4ps|5$`udE%Kdmp?G7B#y%H`R|i8skKOd9Xzx8xgR$>Zo2R2Ytktq^w#ul4uicxW#{ zFjG_RNlBroV_n;a7U(KIpcp*{M~e~@>Q#Av90Jc5v%0c>egEdY4v3%|K1XvB{O_8G zkTWLC>OZKf;XguMH2-Pw{BKbFzaY;4v2seZV0>^7Q~d4O=AwaPhP3h|!hw5aqOtT@ z!SNz}$of**Bl3TK209@F=Tn1+mgZa8yh(Png%Zd6Mt}^NSjy)etQrF zme*llAW=N_8R*O~d2!apJnF%(JcN??=`$qs3Y+~xs>L9x`0^NIn!8mMRFA_tg`etw z3k{9JAjnl@ygIiJcNHTy02GMAvBVqEss&t2<2mnw!; zU`J)0>lWiqVqo|ex7!+@0i>B~BSU1A_0w#Ee+2pJx0BFiZ7RDHEvE*ptc9md(B{&+ zKE>TM)+Pd>HEmdJao7U@S>nL(qq*A)#eLOuIfAS@j`_sK0UEY6OAJJ-kOrHG zjHx`g!9j*_jRcJ%>CE9K2MVf?BUZKFHY?EpV6ai7sET-tqk=nDFh-(65rhjtlKEY% z@G&cQ<5BKatfdA1FKuB=i>CCC5(|9TMW%K~GbA4}80I5%B}(gck#Wlq@$nO3%@QP_ z8nvPkJFa|znk>V92cA!K1rKtr)skHEJD;k8P|R8RkCq1Rh^&}Evwa4BUJz2f!2=MH zo4j8Y$YL2313}H~F7@J7mh>u%556Hw0VUOz-Un@ZASCL)y8}4XXS`t1AC*^>PLwIc zUQok5PFS=*#)Z!3JZN&eZ6ZDP^-c@StY*t20JhCnbMxXf=LK#;`4KHEqMZ-Ly9KsS zI2VUJGY&PmdbM+iT)zek)#Qc#_i4uH43 z@T5SZBrhNCiK~~esjsO9!qBpaWK<`>!-`b71Y5ReXQ4AJU~T2Njri1CEp5oKw;Lnm)-Y@Z3sEY}XIgSy%xo=uek(kAAH5MsV$V3uTUsoTzxp_rF=tx zV07vlJNKtJhCu`b}*#m&5LV4TAE&%KtHViDAdv#c^x`J7bg z&N;#I2GkF@SIGht6p-V}`!F_~lCXjl1BdTLIjD2hH$J^YFN`7f{Q?OHPFEM$65^!u zNwkelo*5+$ZT|oQ%o%;rBX$+?xhvjb)SHgNHE_yP%wYkkvXHS{Bf$OiKJ5d1gI0j< zF6N}Aq=(WDo(J{e-uOecxPD>XZ@|u-tgTR<972`q8;&ZD!cep^@B5CaqFz|oU!iFj zU0;6fQX&~15E53EW&w1s9gQQ~Zk16X%6 zjG`j0yq}4deX2?Tr(03kg>C(!7a|b9qFI?jcE^Y>-VhudI@&LI6Qa}WQ>4H_!UVyF z((cm&!3gmq@;BD#5P~0;_2qgZhtJS|>WdtjY=q zLnHH~Fm!cxw|Z?Vw8*~?I$g#9j&uvgm7vPr#&iZgPP~v~BI4jOv;*OQ?jYJtzO<^y z7-#C={r7CO810!^s(MT!@@Vz_SVU)7VBi(e1%1rvS!?PTa}Uv`J!EP3s6Y!xUgM^8 z4f!fq<3Wer_#;u!5ECZ|^c1{|q_lh3m^9|nsMR1#Qm|?4Yp5~|er2?W^7~cl;_r4WSme_o68J9p03~Hc%X#VcX!xAu%1`R!dfGJCp zV*&m47>s^%Ib0~-2f$6oSgn3jg8m%UA;ArcdcRyM5;}|r;)?a^D*lel5C`V5G=c~k zy*w_&BfySOxE!(~PI$*dwG><+-%KT5p?whOUMA*k<9*gi#T{h3DAxzAPxN&Xws8o9Cp*`PA5>d9*Z-ynV# z9yY*1WR^D8|C%I@vo+d8r^pjJ$>eo|j>XiLWvTWLl(^;JHCsoPgem6PvegHb-OTf| zvTgsHSa;BkbG=(NgPO|CZu9gUCGr$8*EoH2_Z#^BnxF0yM~t`|9ws_xZ8X8iZYqh! zAh;HXJ)3P&)Q0(&F>!LN0g#bdbis-cQxyGn9Qgh`q+~49Fqd2epikEUw9caM%V6WgP)532RMRW}8gNS%V%Hx7apSz}tn@bQy!<=lbhmAH=FsMD?leawbnP5BWM0 z5{)@EEIYMu5;u)!+HQWhQ;D3_Cm_NADNeb-f56}<{41aYq8p4=93d=-=q0Yx#knGYfXVt z+kMxlus}t2T5FEyCN~!}90O_X@@PQpuy;kuGz@bWft%diBTx?d)_xWd_-(!LmVrh**oKg!1CNF&LX4{*j|) zIvjCR0I2UUuuEXh<9}oT_zT#jOrJAHNLFT~Ilh9hGJPI1<5`C-WA{tUYlyMeoy!+U zhA#=p!u1R7DNg9u4|QfED-2TuKI}>p#2P9--z;Bbf4Op*;Q9LCbO&aL2i<0O$ByoI z!9;Ght733FC>Pz>$_mw(F`zU?`m@>gE`9_p*=7o=7av`-&ifU(^)UU`Kg3Kw`h9-1 z6`e6+im=|m2v`pN(2dE%%n8YyQz;#3Q-|x`91z?gj68cMrHl}C25|6(_dIGk*8cA3 zRHB|Nwv{@sP4W+YZM)VKI>RlB`n=Oj~Rzx~M+Khz$N$45rLn6k1nvvD^&HtsMA4`s=MmuOJID@$s8Ph4E zAmSV^+s-z8cfv~Yd(40Sh4JG#F~aB>WFoX7ykaOr3JaJ&Lb49=B8Vk-SQT9%7TYhv z?-Pprt{|=Y5ZQ1?od|A<_IJU93|l4oAfBm?3-wk{O<8ea+`}u%(kub(LFo2zFtd?4 zwpN|2mBNywv+d^y_8#<$r>*5+$wRTCygFLcrwT(qc^n&@9r+}Kd_u@Ithz(6Qb4}A zWo_HdBj#V$VE#l6pD0a=NfB0l^6W^g`vm^sta>Tly?$E&{F?TTX~DsKF~poFfmN%2 z4x`Dc{u{Lkqz&y!33;X}weD}&;7p>xiI&ZUb1H9iD25a(gI|`|;G^NwJPv=1S5e)j z;U;`?n}jnY6rA{V^ zxTd{bK)Gi^odL3l989DQlN+Zs39Xe&otGeY(b5>rlIqfc7Ap4}EC?j<{M=hlH{1+d zw|c}}yx88_xQr`{98Z!d^FNH77=u(p-L{W6RvIn40f-BldeF-YD>p6#)(Qzf)lfZj z?3wAMtPPp>vMehkT`3gToPd%|D8~4`5WK{`#+}{L{jRUMt zrFz+O$C7y8$M&E4@+p+oV5c%uYzbqd2Y%SSgYy#xh4G3hQv>V*BnuKQhBa#=oZB~w{azUB+q%bRe_R^ z>fHBilnRTUfaJ201czL8^~Ix#+qOHSO)A|xWLqOxB$dT2W~)e-r9;bm=;p;RjYahB z*1hegN(VKK+ztr~h1}YP@6cfj{e#|sS`;3tJhIJK=tVJ-*h-5y9n*&cYCSdg#EHE# zSIx=r#qOaLJoVVf6v;(okg6?*L_55atl^W(gm^yjR?$GplNP>BZsBYEf_>wM0Lc;T zhf&gpzOWNxS>m+mN92N0{;4uw`P+9^*|-1~$uXpggj4- z^SFc4`uzj2OwdEVT@}Q`(^EcQ_5(ZtXTql*yGzdS&vrS_w>~~ra|Nb5abwf}Y!uq6R5f&6g2ge~2p(%c< z@O)cz%%rr4*cRJ5f`n@lvHNk@lE1a*96Kw6lJ~B-XfJW%?&-y?;E&?1AacU@`N`!O z6}V>8^%RZ7SQnZ-z$(jsX`amu*5Fj8g!3RTRwK^`2_QHe;_2y_n|6gSaGyPmI#kA0sYV<_qOZc#-2BO%hX)f$s-Z3xlI!ub z^;3ru11DA`4heAu%}HIXo&ctujzE2!6DIGE{?Zs>2}J+p&C$rc7gJC35gxhflorvsb%sGOxpuWhF)dL_&7&Z99=5M0b~Qa;Mo!j&Ti_kXW!86N%n= zSC@6Lw>UQ__F&+&Rzv?gscwAz8IP!n63>SP)^62(HK98nGjLY2*e^OwOq`3O|C92? z;TVhZ2SK%9AGW4ZavTB9?)mUbOoF`V7S=XM;#3EUpR+^oHtdV!GK^nXzCu>tpR|89 zdD{fnvCaN^^LL%amZ^}-E+214g&^56rpdc@yv0b<3}Ys?)f|fXN4oHf$six)-@<;W&&_kj z-B}M5U*1sb4)77aR=@%I?|Wkn-QJVuA96an25;~!gq(g1@O-5VGo7y&E_srxL6ZfS z*R%$gR}dyONgju*D&?geiSj7SZ@ftyA|}(*Y4KbvU!YLsi1EDQQCnb+-cM=K1io78o!v*);o<XwjaQH%)uIP&Zm?)Nfbfn;jIr z)d#!$gOe3QHp}2NBak@yYv3m(CPKkwI|{;d=gi552u?xj9ObCU^DJFQp4t4e1tPzM zvsRIGZ6VF+{6PvqsplMZWhz10YwS={?`~O0Ec$`-!klNUYtzWA^f9m7tkEzCy<_nS z=&<(awFeZvt51>@o_~>PLs05CY)$;}Oo$VDO)?l-{CS1Co=nxjqben*O1BR>#9`0^ zkwk^k-wcLCLGh|XLjdWv0_Hg54B&OzCE^3NCP}~OajK-LuRW53CkV~Su0U>zN%yQP zH8UH#W5P3-!ToO-2k&)}nFe`t+mdqCxxAHgcifup^gKpMObbox9LFK;LP3}0dP-UW z?Zo*^nrQ6*$FtZ(>kLCc2LY*|{!dUn$^RW~m9leoF|@Jy|M5p-G~j%+P0_#orRKf8 zvuu5<*XO!B?1E}-*SY~MOa$6c%2cM+xa8}_8x*aVn~57v&W(0mqN1W`5a7*VN{SUH zXz98DDyCnX2EPl-`Lesf`=AQT%YSDb`$%;(jUTrNen$NPJrlpPDP}prI>Ml!r6bCT;mjsg@X^#&<}CGf0JtR{Ecwd&)2zuhr#nqdgHj+g2n}GK9CHuwO zk>oZxy{vcOL)$8-}L^iVfJHAGfwN$prHjYV0ju}8%jWquw>}_W6j~m<}Jf!G?~r5&Rx)!9JNX!ts#SGe2HzobV5); zpj@&`cNcO&q+%*<%D7za|?m5qlmFK$=MJ_iv{aRs+BGVrs)98BlN^nMr{V_fcl_;jkzRju+c-y?gqBC_@J0dFLq-D9@VN&-`R9U;nv$Hg?>$oe4N&Ht$V_(JR3TG^! zzJsbQbi zFE6-{#9{G{+Z}ww!ycl*7rRdmU#_&|DqPfX3CR1I{Kk;bHwF6jh0opI`UV2W{*|nn zf_Y@%wW6APb&9RrbEN=PQRBEpM(N1w`81s=(xQj6 z-eO0k9=Al|>Ej|Mw&G`%q8e$2xVz1v4DXAi8G};R$y)ww638Y=9y$ZYFDM$}vzusg zUf+~BPX>(SjA|tgaFZr_e0{)+z9i6G#lgt=F_n$d=beAt0Sa0a7>z-?vcjl3e+W}+ z1&9=|vC=$co}-Zh*%3588G?v&U7%N1Qf-wNWJ)(v`iO5KHSkC5&g7CrKu8V}uQGcfcz zmBz#Lbqwqy#Z~UzHgOQ;Q-rPxrRNvl(&u6ts4~0=KkeS;zqURz%!-ERppmd%0v>iRlEf+H$yl{_8TMJzo0 z>n)`On|7=WQdsqhXI?#V{>+~}qt-cQbokEbgwV3QvSP7&hK4R{Z{aGHVS3;+h{|Hz z6$Js}_AJr383c_+6sNR|$qu6dqHXQTc6?(XWPCVZv=)D#6_;D_8P-=zOGEN5&?~8S zl5jQ?NL$c%O)*bOohdNwGIKM#jSAC?BVY={@A#c9GmX0=T(0G}xs`-%f3r=m6-cpK z!%waekyAvm9C3%>sixdZj+I(wQlbB4wv9xKI*T13DYG^T%}zZYJ|0$Oj^YtY+d$V$ zAVudSc-)FMl|54n=N{BnZTM|!>=bhaja?o7s+v1*U$!v!qQ%`T-6fBvmdPbVmro&d zk07TOp*KuxRUSTLRrBj{mjsnF8`d}rMViY8j`jo~Hp$fkv9F_g(jUo#Arp;Xw0M$~ zRIN!B22~$kx;QYmOkos@%|5k)!QypDMVe}1M9tZfkpXKGOxvKXB!=lo`p?|R1l=tA zp(1}c6T3Fwj_CPJwVsYtgeRKg?9?}%oRq0F+r+kdB=bFUdVDRPa;E~~>2$w}>O>v=?|e>#(-Lyx?nbg=ckJ#5U6;RT zNvHhXk$P}m9wSvFyU3}=7!y?Y z=fg$PbV8d7g25&-jOcs{%}wTDKm>!Vk);&rr;O1nvO0VrU&Q?TtYVU=ir`te8SLlS zKSNmV=+vF|ATGg`4$N1uS|n??f}C_4Sz!f|4Ly8#yTW-FBfvS48Tef|-46C(wEO_%pPhUC5$-~Y?!0vFZ^Gu`x=m7X99_?C-`|h zfmMM&Y@zdfitA@KPw4Mc(YHcY1)3*1xvW9V-r4n-9ZuBpFcf{yz+SR{ zo$ZSU_|fgwF~aakGr(9Be`~A|3)B=9`$M-TWKipq-NqRDRQc}ABo*s_5kV%doIX7LRLRau_gd@Rd_aLFXGSU+U?uAqh z8qusWWcvgQ&wu{|sRXmv?sl=xc<$6AR$+cl& zFNh5q1~kffG{3lDUdvEZu5c(aAG~+64FxdlfwY^*;JSS|m~CJusvi-!$XR`6@XtY2 znDHSz7}_Bx7zGq-^5{stTRy|I@N=>*y$zz>m^}^{d&~h;0kYiq8<^Wq7Dz0w31ShO^~LUfW6rfitR0(=3;Uue`Y%y@ex#eKPOW zO~V?)M#AeHB2kovn1v=n^D?2{2jhIQd9t|_Q+c|ZFaWt+r&#yrOu-!4pXAJuxM+Cx z*H&>eZ0v8Y`t}8{TV6smOj=__gFC=eah)mZt9gwz>>W$!>b3O;Rm^Ig*POZP8Rl0f zT~o=Nu1J|lO>}xX&#P58%Yl z83`HRs5#32Qm9mdCrMlV|NKNC+Z~ z9OB8xk5HJ>gBLi+m@(pvpw)1(OaVJKs*$Ou#@Knd#bk+V@y;YXT?)4eP9E5{J%KGtYinNYJUH9PU3A}66c>Xn zZ{Bn0<;8$WCOAL$^NqTjwM?5d=RHgw3!72WRo0c;+houoUA@HWLZM;^U$&sycWrFd zE7ekt9;kb0`lps{>R(}YnXlyGY}5pPd9zBpgXeJTY_jwaJGSJQC#-KJqmh-;ad&F- z-Y)E>!&`Rz!HtCz>%yOJ|v(u7P*I$jqEY3}(Z-orn4 zlI?CYKNl`6I){#2P1h)y(6?i;^z`N3bxTV%wNvQW+eu|x=kbj~s8rhCR*0H=iGkSj zk23lr9kr|p7#qKL=UjgO`@UnvzU)`&fI>1Qs7ubq{@+lK{hH* zvl6eSb9%yngRn^T<;jG1SVa)eA>T^XX=yUS@NCKpk?ovCW1D@!=@kn;l_BrG;hOTC z6K&H{<8K#dI(A+zw-MWxS+~{g$tI7|SfP$EYKxA}LlVO^sT#Oby^grkdZ^^lA}uEF zBSj$weBJG{+Bh@Yffzsw=HyChS(dtLE3i*}Zj@~!_T-Ay7z=B)+*~3|?w`Zd)Co2t zC&4DyB!o&YgSw+fJn6`sn$e)29`kUwAc+1MND7YjV%lO;H2}fNy>hD#=gT ze+-aFNpyKIoXY~Vq-}OWPBe?Rfu^{ps8>Xy%42r@RV#*QV~P83jdlFNgkPN=T|Kt7 zV*M`Rh*30&AWlb$;ae130e@}Tqi3zx2^JQHpM>j$6x`#{mu%tZlwx9Gj@Hc92IuY* zarmT|*d0E~vt6<+r?W^UW0&#U&)8B6+1+;k^2|FWBRP9?C4Rk)HAh&=AS8FS|NQaZ z2j!iZ)nbEyg4ZTp-zHwVlfLC~tXIrv(xrP8PAtR{*c;T24ycA-;auWsya-!kF~CWZ zw_uZ|%urXgUbc@x=L=_g@QJ@m#5beS@6W195Hn7>_}z@Xt{DIEA`A&V82bc^#!q8$ zFh?z_Vn|ozJ;NPd^5uu(9tspo8t%&-U9Ckay-s@DnM*R5rtu|4)~e)`z0P-sy?)kc zs_k&J@0&0!q4~%cKL)2l;N*T&0;mqX5T{Qy60%JtKTQZ-xb%KOcgqwJmb%MOOKk7N zgq})R_6**{8A|6H?fO+2`#QU)p$Ei2&nbj6TpLSIT^D$|`TcSeh+)}VMb}LmvZ{O| ze*1IdCt3+yhdYVxcM)Q_V0bIXLgr6~%JS<<&dxIgfL=Vnx4YHuU@I34JXA|+$_S3~ zy~X#gO_X!cSs^XM{yzDGNM>?v(+sF#<0;AH^YrE8smx<36bUsHbN#y57K8WEu(`qHvQ6cAZPo=J5C(lSmUCZ57Rj6cx!e^rfaI5%w}unz}4 zoX=nt)FVNV%QDJH`o!u9olLD4O5fl)xp+#RloZlaA92o3x4->?rB4`gS$;WO{R;Z3>cG3IgFX2EA?PK^M}@%1%A;?f6}s&CV$cIyEr#q5;yHdNZ9h{| z-=dX+a5elJoDo?Eq&Og!nN6A)5yYpnGEp}?=!C-V)(*~z-+?kY1Q7qs#Rsy%hu_60rdbB+QQNr?S1 z?;xtjUv|*E3}HmuNyB9aFL5H~3Ho0UsmuMZELp1a#CA1g`P{-mT?BchuLEtK}!QZ=3AWakRu~?f9V~3F;TV`5%9Pcs_$gq&CcU}r8gOO zC2&SWPsSG{&o-LIGTBqp6SLQZPvYKp$$7L4WRRZ0BR$Kf0I0SCFkqveCp@f)o8W)! z$%7D1R`&j7W9Q9CGus_)b%+B#J2G;l*FLz#s$hw{BHS~WNLODV#(!u_2Pe&tMsq={ zdm7>_WecWF#D=?eMjLj=-_z`aHMZ=3_-&E8;ibPmM}61i6J3is*=dKf%HC>=xbj4$ zS|Q-hWQ8T5mWde6h@;mS+?k=89?1FU<%qH9B(l&O>k|u_aD|DY*@~(`_pb|B#rJ&g zR0(~(68fpUPz6TdS@4JT5MOPrqDh5_H(eX1$P2SQrkvN8sTxwV>l0)Qq z0pzTuvtEAKRDkKGhhv^jk%|HQ1DdF%5oKq5BS>szk-CIke{%js?~%@$uaN3^Uz6Wf z_iyx{bZ(;9y4X&>LPV=L=d+A}7I4GkK0c1Xts{rrW1Q7apHf-))`BgC^0^F(>At1* za@e7{lq%yAkn*NH8Q1{@{lKhRg*^TfGvv!Sn*ed*x@6>M%aaqySxR|oNadYt1mpUZ z6H(rupHYf&Z z29$5g#|0MX#aR6TZ$@eGxxABRKakDYtD%5BmKp;HbG_ZbT+=81E&=XRk6m_3t9PvD zr5Cqy(v?gHcYvYvXkNH@S#Po~q(_7MOuCAB8G$a9BC##gw^5mW16cML=T=ERL7wsk zzNEayTG?mtB=x*wc@ifBCJ|irFVMOvH)AFRW8WE~U()QT=HBCe@s$dA9O!@`zAAT) zaOZ7l6vyR+Nk_OOF!ZlZmjoImKh)dxFbbR~z(cMhfeX1l7S_`;h|v3gI}n9$sSQ>+3@AFAy9=B_y$)q;Wdl|C-X|VV3w8 z2S#>|5dGA8^9%Bu&fhmVRrTX>Z7{~3V&0UpJNEl0=N32euvDGCJ>#6dUSi&PxFW*s zS`}TB>?}H(T2lxBJ!V#2taV;q%zd6fOr=SGHpoSG*4PDaiG0pdb5`jelVipkEk%FV zThLc@Hc_AL1#D&T4D=w@UezYNJ%0=f3iVRuVL5H?eeZM}4W*bomebEU@e2d`M<~uW zf#Bugwf`VezG|^Qbt6R_=U0}|=k;mIIakz99*>FrsQR{0aQRP6ko?5<7bkDN8evZ& zB@_KqQG?ErKL=1*ZM9_5?Pq%lcS4uLSzN(Mr5=t6xHLS~Ym`UgM@D&VNu8e?_=nSFtF$u@hpPSmI4Vo_t&v?>$~K4y(O~Rb*(MFy_igM7 z*~yYUyR6yQgzWnWMUgDov!!g=lInM+=lOmOk4L`O?{i&qxy&D*_qorRbDwj6?)!ef z#JLd7F6Z2I$S0iYI={rZNk*<{HtIl^mx=h>Cim*04K4+Z4IJtd*-)%6XV2(MCscPiw_a+y*?BKbTS@BZ3AUao^%Zi#PhoY9Vib4N>SE%4>=Jco0v zH_Miey{E;FkdlZSq)e<{`+S3W=*ttvD#hB8w=|2aV*D=yOV}(&p%0LbEWH$&@$X3x~CiF-?ejQ*N+-M zc8zT@3iwkdRT2t(XS`d7`tJQAjRmKAhiw{WOqpuvFp`i@Q@!KMhwKgsA}%@sw8Xo5Y=F zhRJZg)O4uqNWj?V&&vth*H#je6T}}p_<>!Dr#89q@uSjWv~JuW(>FqoJ5^ho0%K?E z9?x_Q;kmcsQ@5=}z@tdljMSt9-Z3xn$k)kEjK|qXS>EfuDmu(Z8|(W?gY6-l z@R_#M8=vxKMAoi&PwnaIYw2COJM@atcgfr=zK1bvjW?9B`-+Voe$Q+H$j!1$Tjn+* z&LY<%)L@;zhnJlB^Og6I&BOR-m?{IW;tyYC%FZ!&Z>kGjHJ6cqM-F z&19n+e1=9AH1VrVeHrIzqlC`w9=*zfmrerF?JMzO&|Mmv;!4DKc(sp+jy^Dx?(8>1 zH&yS_4yL7m&GWX~mdfgH*AB4{CKo;+egw=PrvkTaoBU+P-4u?E|&!c z)DKc;>$$B6u*Zr1SjUh2)FeuWLWHl5TH(UHWkf zLs>7px!c5n;rbe^lO@qlYLzlDVp(z?6rPZel=YB)Uv&n!2{+Mb$-vQl=xKw( zve&>xYx+jW_NJh!FV||r?;hdP*jOXYcLCp>DOtJ?2S^)DkM{{Eb zS$!L$e_o0(^}n3tA1R3-$SNvgBq;DOEo}fNc|tB%%#g4RA3{|euq)p+xd3I8^4E&m zFrD%}nvG^HUAIKe9_{tXB;tl|G<%>yk6R;8L2)KUJw4yHJXUOPM>(-+jxq4R;z8H#>rnJy*)8N+$wA$^F zN+H*3t)eFEgxLw+Nw3};4WV$qj&_D`%ADV2%r zJCPCo%{=z7;`F98(us5JnT(G@sKTZ^;2FVitXyLe-S5(hV&Ium+1pIUB(CZ#h|g)u zSLJJ<@HgrDiA-}V_6B^x1>c9B6%~847JkQ!^KLZ2skm;q*edo;UA)~?SghG8;QbHh z_6M;ouo_1rq9=x$<`Y@EA{C%6-pEV}B(1#sDoe_e1s3^Y>n#1Sw;N|}8D|s|VPd+g z-_$QhCz`vLxxrVMx3ape1xu3*wjx=yKSlM~nFgkNWb4?DDr*!?U)L_VeffF<+!j|b zZ$Wn2$TDv3C3V@BHpSgv3JUif8%hk%OsGZ=OxH@8&4`bbf$`aAMchl^qN>Eyu3JH} z9-S!x8-s4fE=lad%Pkp8hAs~u?|uRnL48O|;*DEU! zuS0{cpk%1E0nc__2%;apFsTm0bKtd&A0~S3Cj^?72-*Owk3V!ZG*PswDfS~}2<8le z5+W^`Y(&R)yVF*tU_s!XMcJS`;(Tr`J0%>p=Z&InR%D3@KEzzI+-2)HK zuoNZ&o=wUC&+*?ofPb0a(E6(<2Amd6%uSu_^-<1?hsxs~0K5^f(LsGqgEF^+0_H=uNk9S0bb!|O8d?m5gQjUKevPaO+*VfSn^2892K~%crWM8+6 z25@V?Y@J<9w%@NXh-2!}SK_(X)O4AM1-WTg>sj1{lj5@=q&dxE^9xng1_z9w9DK>| z6Iybcd0e zyi;Ew!KBRIfGPGytQ6}z}MeXCfLY0?9%RiyagSp_D1?N&c{ zyo>VbJ4Gy`@Fv+5cKgUgs~na$>BV{*em7PU3%lloy_aEovR+J7TfQKh8BJXyL6|P8un-Jnq(ghd!_HEOh$zlv2$~y3krgeH;9zC}V3f`uDtW(%mT#944DQa~^8ZI+zAUu4U(j0YcDfKR$bK#gvn_{JZ>|gZ5+)u?T$w7Q%F^;!Wk?G z(le7r!ufT*cxS}PR6hIVtXa)i`d$-_1KkyBU>qmgz-=T};uxx&sKgv48akIWQ89F{ z0XiY?WM^~;|T8zBOr zs#zuOONzH?svv*jokd5SK8wG>+yMC)LYL|vLqm^PMHcT=`}V$=nIRHe2?h)8WQa6O zPAU}d`1y(>kZiP~Gr=mtJLMu`i<2CspL|q2DqAgAD^7*$xzM`PU4^ga`ilE134XBQ z99P(LhHU@7qvl9Yzg$M`+dlS=x^(m-_3t|h>S}E0bcFMn=C|KamQ)=w2^e)35p`zY zRV8X?d;s^>Cof2SPR&nP3E+-LCkS0J$H!eh8~k0qo$}00b=7!H_I2O+Ro@3O$nPdm ztmbOO^B+IHzQ5w>@@@J4cKw5&^_w6s!s=H%&byAbUtczPQ7}wfTqxxtQNfn*u73Qw zGuWsrky_ajPx-5`R<)6xHf>C(oqGf_Fw|-U*GfS?xLML$kv;h_pZ@Kk$y0X(S+K80 z6^|z)*`5VUkawg}=z`S;VhZhxyDfrE0$(PMurAxl~<>lfZa>JZ288ULK7D` zl9|#L^JL}Y$j*j`0-K6kH#?bRmg#5L3iB4Z)%iF@SqT+Lp|{i`m%R-|ZE94Np7Pa5 zCqC^V3}B(FR340pmF*qaa}M}+h6}mqE~7Sh!9bDv9YRT|>vBNAqv09zXHMlcuhKD| zcjjA(b*XCIwJ33?CB!+;{)vX@9xns_b-VO{i0y?}{!sdXj1GM8+$#v>W7nw;+O_9B z_{4L;C6ol?(?W0<6taGEn1^uG=?Q3i29sE`RfYCaV$3DKc_;?HsL?D_fSYg}SuO5U zOB_f4^vZ_x%o`5|C@9C5+o=mFy@au{s)sKw!UgC&L35aH(sgDxRE2De%(%OT=VUdN ziVLEmdOvJ&5*tCMKRyXctCwQu_RH%;m*$YK&m;jtbdH#Ak~13T1^f89tn`A%QEHWs~jnY~E}p_Z$XC z=?YXLCkzVSK+Id`xZYTegb@W8_baLt-Fq`Tv|=)JPbFsKRm)4UW;yT+J`<)%#ue9DPOkje)YF2fsCilK9MIIK>p*`fkoD5nGfmLwt)!KOT+> zOFq*VZktDDyM3P5UOg`~XL#cbzC}eL%qMB=Q5$d89MKuN#$6|4gx_Jt0Gfn8w&q}%lq4QU%6#jT*MRT% zrLz~C8FYKHawn-EQWN1B75O&quS+Z81(zN)G>~vN8VwC+e+y(`>HcxC{MrJ;H1Z4k zZWuv$w_F0-Ub%MVcpIc){4PGL^I7M{>;hS?;eH!;gmcOE66z3;Z1Phqo(t zVP(Hg6q#0gIKgsg7L7WE!{Y#1nI(45tx2{$34dDd#!Z0NIyrm)HOn5W#7;f4pQci# zDW!FI(g4e668kI9{2+mLwB+=#9bfqgX%!B34V-$wwSN(_cm*^{y0jQtv*4}eO^sOV z*9xoNvX)c9isB}Tgx&ZRjp3kwhTVK?r9;n!x>^XYT z@Q^7zp{rkIs{2mUSE^2!Gf6$6;j~&4=-0cSJJDizZp6LTe8b45;{AKM%v99}{{FfC zz709%u0mC=1KXTo(=TqmZQ;c?$M3z(!xah>aywrj40sc2y3rKFw4jCq+Y+u=CH@_V zxz|qeTwa>+<|H%8Dz5u>ZI5MmjTFwXS-Fv!TDd*`>3{krWoNVx$<133`(ftS?ZPyY z&4@ah^3^i`vL$BZa>O|Nt?ucewzsF)0zX3qmM^|waXr=T0pfIb0*$AwU=?Ipl|1Y; z*Pk6{C-p4MY;j@IJ|DW>QHZQJcp;Z~?8(Q+Kk3^0qJ}SCk^*n4W zu9ZFwLHUx-$6xvaQ)SUQcYd6fF8&x)V`1bIuX@>{mE$b|Yd(qomn3;bPwnDUc0F=; zh*6_((%bqAYQWQ~odER?h>1mkL4kpb3s7`0m@rDKGU*oyF)$j~Ffd4fXV$?`f~rHf zB%Y)@5SXZvfwm10RY5X?TEo)PK_`L6qgBp=#>fO49$D zDq8Ozj0q6213tV5Qq=;fZ0$|KroY{Dz=l@lU^J)?Ko@ti20TRplXzphBi>XGx4bou zEWrkNjz0t5j!_ke{g5I#PUlEU$Km8g8TE|XK=MkU@PT4T><2OVamoK;wJ}3X0L$vX zgd7gNa359*nc)R-0!`2X@FOTB`+oETOPc=ubp5R)VQgY+5BTZZJ2?9QwnO=dnulIUF3gFn;BODC2)65)HeVd%t86sL7Rv^Y+nbn+&l z6BAJY(ETvwI)Ts$aiE8rht4KD*qNyE{8{x6R|%akbTBzw;2+6Echkt+W+`u^XX z_z&x%n '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/exercises/practice/split-second-stopwatch/gradlew.bat b/exercises/practice/split-second-stopwatch/gradlew.bat new file mode 100644 index 000000000..25da30dbd --- /dev/null +++ b/exercises/practice/split-second-stopwatch/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/exercises/practice/split-second-stopwatch/src/main/java/SplitSecondStopwatch.java b/exercises/practice/split-second-stopwatch/src/main/java/SplitSecondStopwatch.java new file mode 100644 index 000000000..36847004c --- /dev/null +++ b/exercises/practice/split-second-stopwatch/src/main/java/SplitSecondStopwatch.java @@ -0,0 +1,37 @@ +public class SplitSecondStopwatch { + public void start() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.start method."); + } + + public void stop() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.stop method."); + } + + public void reset() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.reset method."); + } + + public void lap() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.lap method."); + } + + public String state() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.state method."); + } + + public String currentLap() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.currentLap method."); + } + + public String total() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.total method."); + } + + public java.util.List previousLaps() { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.previousLaps method."); + } + + public void advanceTime(String timeString) { + throw new UnsupportedOperationException("Please implement the SplitSecondStopwatch.advanceTime method."); + } +} \ No newline at end of file diff --git a/exercises/practice/split-second-stopwatch/src/test/java/SplitSecondStopwatchTest.java b/exercises/practice/split-second-stopwatch/src/test/java/SplitSecondStopwatchTest.java new file mode 100644 index 000000000..1f54a9907 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/src/test/java/SplitSecondStopwatchTest.java @@ -0,0 +1,339 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class SplitSecondStopwatchTest { + @Test + @DisplayName("new stopwatch starts in ready state") + public void newStopwatchStartsInReadyState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThat(stopwatch.state()).isEqualTo("ready"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("new stopwatch's current lap has no elapsed time") + public void newStopwatchCurrentLapHasNoElapsedTime() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThat(stopwatch.currentLap()).isEqualTo("00:00:00"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("new stopwatch's total has no elapsed time") + public void newStopwatchTotalHasNoElapsedTime() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThat(stopwatch.total()).isEqualTo("00:00:00"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("new stopwatch does not have previous laps") + public void newStopwatchDoesNotHavePreviousLaps() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThat(stopwatch.previousLaps()).isEmpty(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start from ready state changes state to running") + public void startFromReadyStateChangesStateToRunning() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + assertThat(stopwatch.state()).isEqualTo("running"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start does not change previous laps") + public void startDoesNotChangePreviousLaps() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + assertThat(stopwatch.previousLaps()).isEmpty(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start initiates time tracking for current lap") + public void startInitiatesTimeTrackingForCurrentLap() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:05"); + assertThat(stopwatch.currentLap()).isEqualTo("00:00:05"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start initiates time tracking for total") + public void startInitiatesTimeTrackingForTotal() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:23"); + assertThat(stopwatch.total()).isEqualTo("00:00:23"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start cannot be called from running state") + public void startCannotBeCalledFromRunningState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::start) + .withMessage("cannot start an already running stopwatch"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("stop from running state changes state to stopped") + public void stopFromRunningStateChangesStateToStopped() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.stop(); + assertThat(stopwatch.state()).isEqualTo("stopped"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("stop pauses time tracking for current lap") + public void stopPausesTimeTrackingForCurrentLap() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:05"); + stopwatch.stop(); + stopwatch.advanceTime("00:00:08"); + assertThat(stopwatch.currentLap()).isEqualTo("00:00:05"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("stop pauses time tracking for total") + public void stopPausesTimeTrackingForTotal() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:13"); + stopwatch.stop(); + stopwatch.advanceTime("00:00:44"); + assertThat(stopwatch.total()).isEqualTo("00:00:13"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("stop cannot be called from ready state") + public void stopCannotBeCalledFromReadyState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::stop) + .withMessage("cannot stop a stopwatch that is not running"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("stop cannot be called from stopped state") + public void stopCannotBeCalledFromStoppedState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.stop(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::stop) + .withMessage("cannot stop a stopwatch that is not running"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start from stopped state changes state to running") + public void startFromStoppedStateChangesStateToRunning() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.stop(); + stopwatch.start(); + assertThat(stopwatch.state()).isEqualTo("running"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start from stopped state resumes time tracking for current lap") + public void startFromStoppedStateResumesTimeTrackingForCurrentLap() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:01:20"); + stopwatch.stop(); + stopwatch.advanceTime("00:00:20"); + stopwatch.start(); + stopwatch.advanceTime("00:00:08"); + assertThat(stopwatch.currentLap()).isEqualTo("00:01:28"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("start from stopped state resumes time tracking for total") + public void startFromStoppedStateResumesTimeTrackingForTotal() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:23"); + stopwatch.stop(); + stopwatch.advanceTime("00:00:44"); + stopwatch.start(); + stopwatch.advanceTime("00:00:09"); + assertThat(stopwatch.total()).isEqualTo("00:00:32"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("lap adds current lap to previous laps") + public void lapAddsCurrentLapToPreviousLaps() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:01:38"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("00:01:38"); + stopwatch.advanceTime("00:00:44"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("00:01:38", "00:00:44"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("lap resets current lap and resumes time tracking") + public void lapResetsCurrentLapAndResumesTimeTracking() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:08:22"); + stopwatch.lap(); + assertThat(stopwatch.currentLap()).isEqualTo("00:00:00"); + stopwatch.advanceTime("00:00:15"); + assertThat(stopwatch.currentLap()).isEqualTo("00:00:15"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("lap continues time tracking for total") + public void lapContinuesTimeTrackingForTotal() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:22"); + stopwatch.lap(); + stopwatch.advanceTime("00:00:33"); + assertThat(stopwatch.total()).isEqualTo("00:00:55"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("lap cannot be called from ready state") + public void lapCannotBeCalledFromReadyState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::lap) + .withMessage("cannot lap a stopwatch that is not running"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("lap cannot be called from stopped state") + public void lapCannotBeCalledFromStoppedState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.stop(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::lap) + .withMessage("cannot lap a stopwatch that is not running"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("stop does not change previous laps") + public void stopDoesNotChangePreviousLaps() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:11:22"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("00:11:22"); + stopwatch.stop(); + assertThat(stopwatch.previousLaps()).containsExactly("00:11:22"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("reset from stopped state changes state to ready") + public void resetFromStoppedStateChangesStateToReady() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.stop(); + stopwatch.reset(); + assertThat(stopwatch.state()).isEqualTo("ready"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("reset resets current lap") + public void resetResetsCurrentLap() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:10"); + stopwatch.stop(); + stopwatch.reset(); + assertThat(stopwatch.currentLap()).isEqualTo("00:00:00"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("reset clears previous laps") + public void resetClearsPreviousLaps() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("00:00:10"); + stopwatch.lap(); + stopwatch.advanceTime("00:00:20"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("00:00:10", "00:00:20"); + stopwatch.stop(); + stopwatch.reset(); + assertThat(stopwatch.previousLaps()).isEmpty(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("reset cannot be called from ready state") + public void resetCannotBeCalledFromReadyState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::reset) + .withMessage("cannot reset a stopwatch that is not stopped"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("reset cannot be called from running state") + public void resetCannotBeCalledFromRunningState() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(stopwatch::reset) + .withMessage("cannot reset a stopwatch that is not stopped"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("supports very long laps") + public void supportsVeryLongLaps() { + SplitSecondStopwatch stopwatch = new SplitSecondStopwatch(); + stopwatch.start(); + stopwatch.advanceTime("01:23:45"); + assertThat(stopwatch.currentLap()).isEqualTo("01:23:45"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("01:23:45"); + stopwatch.advanceTime("04:01:40"); + assertThat(stopwatch.currentLap()).isEqualTo("04:01:40"); + assertThat(stopwatch.total()).isEqualTo("05:25:25"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("01:23:45", "04:01:40"); + stopwatch.advanceTime("08:43:05"); + assertThat(stopwatch.currentLap()).isEqualTo("08:43:05"); + assertThat(stopwatch.total()).isEqualTo("14:08:30"); + stopwatch.lap(); + assertThat(stopwatch.previousLaps()).containsExactly("01:23:45", "04:01:40", "08:43:05"); + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index c46f79877..e17e28e81 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -145,6 +145,7 @@ include 'practice:simple-linked-list' include 'practice:sgf-parsing' include 'practice:space-age' include 'practice:spiral-matrix' +include 'practice:split-second-stopwatch' include 'practice:square-root' include 'practice:state-of-tic-tac-toe' // include 'practice:strain' // deprecated From 50ab52c6a92f232c7c81e8f8916c3a9e6b397564 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 10:20:35 +0530 Subject: [PATCH 42/67] =?UTF-8?q?=F0=9F=A4=96=20Auto-sync=20docs,=20metada?= =?UTF-8?q?ta,=20and=20filepaths=20(#2981)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kahgoh <773175+kahgoh@users.noreply.github.com> --- .../.meta/config.json | 3 ++ exercises/practice/say/.docs/instructions.md | 52 +++---------------- exercises/practice/say/.docs/introduction.md | 6 +++ .../split-second-stopwatch/.meta/config.json | 3 ++ .../practice/triangle/.docs/instructions.md | 5 ++ 5 files changed, 25 insertions(+), 44 deletions(-) create mode 100644 exercises/practice/say/.docs/introduction.md diff --git a/exercises/practice/intergalactic-transmission/.meta/config.json b/exercises/practice/intergalactic-transmission/.meta/config.json index 6d97d1430..f0bc8ae9d 100644 --- a/exercises/practice/intergalactic-transmission/.meta/config.json +++ b/exercises/practice/intergalactic-transmission/.meta/config.json @@ -11,6 +11,9 @@ ], "example": [ ".meta/src/reference/java/IntergalacticTransmission.java" + ], + "invalidator": [ + "build.gradle" ] }, "blurb": "Add parity bits to a message for transmission", diff --git a/exercises/practice/say/.docs/instructions.md b/exercises/practice/say/.docs/instructions.md index ad3d34778..3251c519a 100644 --- a/exercises/practice/say/.docs/instructions.md +++ b/exercises/practice/say/.docs/instructions.md @@ -1,48 +1,12 @@ # Instructions -Given a number from 0 to 999,999,999,999, spell out that number in English. +Given a number, your task is to express it in English words exactly as your friend should say it out loud. +Yaʻqūb expects to use numbers from 0 up to 999,999,999,999. -## Step 1 +Examples: -Handle the basic case of 0 through 99. - -If the input to the program is `22`, then the output should be `'twenty-two'`. - -Your program should complain loudly if given a number outside the blessed range. - -Some good test cases for this program are: - -- 0 -- 14 -- 50 -- 98 -- -1 -- 100 - -### Extension - -If you're on a Mac, shell out to Mac OS X's `say` program to talk out loud. -If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`. - -## Step 2 - -Implement breaking a number up into chunks of thousands. - -So `1234567890` should yield a list like 1, 234, 567, and 890, while the far simpler `1000` should yield just 1 and 0. - -## Step 3 - -Now handle inserting the appropriate scale word between those chunks. - -So `1234567890` should yield `'1 billion 234 million 567 thousand 890'` - -The program must also report any values that are out of range. -It's fine to stop at "trillion". - -## Step 4 - -Put it all together to get nothing but plain English. - -`12345` should give `twelve thousand three hundred forty-five`. - -The program must also report any values that are out of range. +- 0 → zero +- 1 → one +- 12 → twelve +- 123 → one hundred twenty-three +- 1,234 → one thousand two hundred thirty-four diff --git a/exercises/practice/say/.docs/introduction.md b/exercises/practice/say/.docs/introduction.md new file mode 100644 index 000000000..abd22851e --- /dev/null +++ b/exercises/practice/say/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +Your friend Yaʻqūb works the counter at the busiest deli in town, slicing, weighing, and wrapping orders for a never-ending line of hungry customers. +To keep things moving, each customer takes a numbered ticket when they arrive. + +When it’s time to call the next person, Yaʻqūb reads their number out loud, always in full English words to make sure everyone hears it clearly. diff --git a/exercises/practice/split-second-stopwatch/.meta/config.json b/exercises/practice/split-second-stopwatch/.meta/config.json index cde93a410..6fb187621 100644 --- a/exercises/practice/split-second-stopwatch/.meta/config.json +++ b/exercises/practice/split-second-stopwatch/.meta/config.json @@ -11,6 +11,9 @@ ], "example": [ ".meta/src/reference/java/SplitSecondStopwatch.java" + ], + "invalidator": [ + "build.gradle" ] }, "blurb": "Keep track of time through a digital stopwatch.", diff --git a/exercises/practice/triangle/.docs/instructions.md b/exercises/practice/triangle/.docs/instructions.md index ac3900872..755cb8d19 100644 --- a/exercises/practice/triangle/.docs/instructions.md +++ b/exercises/practice/triangle/.docs/instructions.md @@ -13,6 +13,11 @@ A _scalene_ triangle has all sides of different lengths. For a shape to be a triangle at all, all sides have to be of length > 0, and the sum of the lengths of any two sides must be greater than or equal to the length of the third side. +~~~~exercism/note +We opted to not include tests for degenerate triangles (triangles that violate these rules) to keep things simpler. +You may handle those situations if you wish to do so, or safely ignore them. +~~~~ + In equations: Let `a`, `b`, and `c` be sides of the triangle. From 5ea692614c76afadfec9b26efcbc19531cf679f3 Mon Sep 17 00:00:00 2001 From: Kah Goh Date: Wed, 20 Aug 2025 01:23:44 +0800 Subject: [PATCH 43/67] Add recursive & bottom up approaches for Change (#2984) Also fixed a spelling mistake in collatz conjecture exercise. --- .../practice/change/.approaches/config.json | 24 +++- .../dynamic-programming-bottom-up/content.md | 84 +++++++++++++ .../dynamic-programming-bottom-up/snippet.txt | 8 ++ .../content.md | 6 +- .../dynamic-programming-top-down/snippet.txt | 8 ++ .../dynamic-programming/snippet.txt | 8 -- .../change/.approaches/introduction.md | 119 ++++++++++++++++-- .../change/.approaches/recursive/content.md | 56 +++++++++ .../change/.approaches/recursive/snippet.txt | 7 ++ .../.approaches/introduction.md | 2 +- 10 files changed, 300 insertions(+), 22 deletions(-) create mode 100644 exercises/practice/change/.approaches/dynamic-programming-bottom-up/content.md create mode 100644 exercises/practice/change/.approaches/dynamic-programming-bottom-up/snippet.txt rename exercises/practice/change/.approaches/{dynamic-programming => dynamic-programming-top-down}/content.md (98%) create mode 100644 exercises/practice/change/.approaches/dynamic-programming-top-down/snippet.txt delete mode 100644 exercises/practice/change/.approaches/dynamic-programming/snippet.txt create mode 100644 exercises/practice/change/.approaches/recursive/content.md create mode 100644 exercises/practice/change/.approaches/recursive/snippet.txt diff --git a/exercises/practice/change/.approaches/config.json b/exercises/practice/change/.approaches/config.json index 00716db81..46b784e67 100644 --- a/exercises/practice/change/.approaches/config.json +++ b/exercises/practice/change/.approaches/config.json @@ -7,15 +7,33 @@ "approaches": [ { "uuid": "d0b615ca-3a02-4d66-ad10-e0c513062189", - "slug": "dynamic-programming", - "title": "Dynamic Programming Approach", - "blurb": "Use dynamic programming to find the most efficient change combination.", + "slug": "dynamic-programming-top-down", + "title": "Dynamic Programming: Top Down", + "blurb": "Break the required amount into smaller amounts and reuse saved results to quickly find the final result.", "authors": [ "jagdish-15" ], "contributors": [ "kahgoh" ] + }, + { + "uuid": "daf47878-1607-4f22-b2df-1049f3d6802c", + "slug": "dynamic-programming-bottom-up", + "title": "Dynamic Programming: Bottom Up", + "blurb": "Start from the available coins and calculate the amounts that can be made from them.", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "06ae63ec-5bf3-41a0-89e3-2772e4cdbf5d", + "slug": "recursive", + "title": "Recursive", + "blurb": "Use recursion to recursively find the most efficient change for a given amount.", + "authors": [ + "kahgoh" + ] } ] } diff --git a/exercises/practice/change/.approaches/dynamic-programming-bottom-up/content.md b/exercises/practice/change/.approaches/dynamic-programming-bottom-up/content.md new file mode 100644 index 000000000..84983df5e --- /dev/null +++ b/exercises/practice/change/.approaches/dynamic-programming-bottom-up/content.md @@ -0,0 +1,84 @@ +# Dynamic Programming - Bottom up + +```java +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +class ChangeCalculator { + + private final List currencyCoins; + + ChangeCalculator(List currencyCoins) { + this.currencyCoins = List.copyOf(currencyCoins); + } + + List computeMostEfficientChange(int grandTotal) { + if (grandTotal < 0) { + throw new IllegalArgumentException("Negative totals are not allowed."); + } + if (grandTotal == 0) { + return Collections.emptyList(); + } + Set reachableTotals = new HashSet<>(); + ArrayDeque> queue = new ArrayDeque<>(currencyCoins.stream().map(List::of).toList()); + + while (!queue.isEmpty()) { + List next = queue.poll(); + int total = next.stream().mapToInt(Integer::intValue).sum(); + if (total == grandTotal) { + return next; + } + if (total < grandTotal && reachableTotals.add(total)) { + for (Integer coin : currencyCoins) { + List toCheck = new ArrayList<>(next); + toCheck.add(coin); + queue.offer(toCheck); + } + } + } + + throw new IllegalArgumentException("The total " + grandTotal + " cannot be represented in the given currency."); + } +} +``` + +This approach starts from the coins and calculates which amounts can be made up by the coins. + +The `grandTotal` is first validated to ensure that it is a positive number greater than 0. +Two data structures are then created: + +- a queue to maintain a combination of coins to check +- a set to keep track of the totals from the combinations that have been seen + +The queue is initialized with a number of combinations that consist just each of the coins. +For example, if the available coins are 5, 10 and 20, then the queue begins with three combinations: + +- the first combination has just 5 +- the second has just 10 +- the third has just 20 + +Thus, the queue contains `[[5], [10], [20]]`. + +For each combination in the queue, the loop calculates the sum of the combination. +If the sum equals the desired total, it has found the combination. +Otherwise new combinations are added to the queue by adding each of the coins to the end of the combination: + +- less than the desired total, and: +- the total has _not_ yet been "seen" (the Set's [add][set-add] method returns `true` if a new item is being added and `false` if it is already in the Set) + +~~~~exercism/note +If the total has been "seen", there is no need to recheck the amounts because shorter combinations are always checked before longer combinations. +So, if the total is encountered again, we must have found a shorter combination to reach the same amount earlier. +~~~~ + +Continuing with the above example, the first combination contains just `5`. +When this is processed, the combinations `[5, 5]`, `[5, 10]` and `[5, 20]` would be added to the end of the queue and the queue becomes `[[10], [20],[5 ,5], [5, 10], [5, 20]]` for the next iteration. +Adding to the end of the queue ensures that the shorter combinations are checked first and allows the combination to simply be returned when the total is reached. + +The total can not be reached when there are no combinations in the queue. + +[set-add]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Set.html#add(E) diff --git a/exercises/practice/change/.approaches/dynamic-programming-bottom-up/snippet.txt b/exercises/practice/change/.approaches/dynamic-programming-bottom-up/snippet.txt new file mode 100644 index 000000000..354ea1739 --- /dev/null +++ b/exercises/practice/change/.approaches/dynamic-programming-bottom-up/snippet.txt @@ -0,0 +1,8 @@ +while (!queue.isEmpty()) { + int total = next.stream().mapToInt(Integer::intValue).sum(); + if (total < grandTotal && reachableTotals.add(total)) { + for (Integer coin : currencyCoins) { + queue.add(append(next, coin)); + } + } +} \ No newline at end of file diff --git a/exercises/practice/change/.approaches/dynamic-programming/content.md b/exercises/practice/change/.approaches/dynamic-programming-top-down/content.md similarity index 98% rename from exercises/practice/change/.approaches/dynamic-programming/content.md rename to exercises/practice/change/.approaches/dynamic-programming-top-down/content.md index 640c58a47..06dbf9ba8 100644 --- a/exercises/practice/change/.approaches/dynamic-programming/content.md +++ b/exercises/practice/change/.approaches/dynamic-programming-top-down/content.md @@ -1,4 +1,4 @@ -# Dynamic Programming Approach +# Dynamic Programming - Top Down ```java import java.util.List; @@ -12,7 +12,7 @@ class ChangeCalculator { } List computeMostEfficientChange(int grandTotal) { - if (grandTotal < 0) + if (grandTotal < 0) throw new IllegalArgumentException("Negative totals are not allowed."); List> coinsUsed = new ArrayList<>(grandTotal + 1); @@ -64,5 +64,5 @@ It minimizes the number of coins needed by breaking down the problem into smalle ## Time and Space Complexity The time complexity of this approach is **O(n * m)**, where `n` is the `grandTotal` and `m` is the number of available coin denominations. This is because we iterate over all coin denominations for each amount up to `grandTotal`. - + The space complexity is **O(n)** due to the list `coinsUsed`, which stores the most efficient coin combination for each total up to `grandTotal`. diff --git a/exercises/practice/change/.approaches/dynamic-programming-top-down/snippet.txt b/exercises/practice/change/.approaches/dynamic-programming-top-down/snippet.txt new file mode 100644 index 000000000..d79e5e5f6 --- /dev/null +++ b/exercises/practice/change/.approaches/dynamic-programming-top-down/snippet.txt @@ -0,0 +1,8 @@ +for (int i = 1; i <= grandTotal; i++) { + for (int coin: currencyCoins) { + List currentCombination = coinsUsed.get(i - coin).add(coin); + if (bestCombination == null || currentCombination.size() < bestCombination.size()) + bestCombination = currentCombination; + } + coinsUsed.add(bestCombination); +} \ No newline at end of file diff --git a/exercises/practice/change/.approaches/dynamic-programming/snippet.txt b/exercises/practice/change/.approaches/dynamic-programming/snippet.txt deleted file mode 100644 index 25f90e6f5..000000000 --- a/exercises/practice/change/.approaches/dynamic-programming/snippet.txt +++ /dev/null @@ -1,8 +0,0 @@ -class ChangeCalculator { - private final List currencyCoins; - - ChangeCalculator(List currencyCoins) { - this.currencyCoins = currencyCoins; - } - // computeMostEfficientChange method -} diff --git a/exercises/practice/change/.approaches/introduction.md b/exercises/practice/change/.approaches/introduction.md index 672aae038..60f5e2937 100644 --- a/exercises/practice/change/.approaches/introduction.md +++ b/exercises/practice/change/.approaches/introduction.md @@ -1,14 +1,62 @@ -# Introduction +# Introduction -There is an idiomatic approach to solving "Change." -You can use [dynamic programming][dynamic-programming] to calculate the minimum number of coins required for a given total. +There are a couple of different ways to solve "Change". +The [recursive approach][approach-recursive] uses recursion to find most efficient change for remaining amounts assuming a coin is included. +[Dynamic programming][dynamic-programming] calculates the solution starting from the required total ([the top][approach-dynamic-programming-top-down]) or from the amounts that can be covered by the coins ([the bottom][approach-dynamic-programming-bottom-up]). ## General guidance The key to solving "Change" is understanding that not all totals can be reached with the available coin denominations. The solution needs to figure out which totals can be achieved and how to combine the coins optimally. -## Approach: Dynamic Programming +## Approach: Recursive + +```java +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +class ChangeCalculator { + + private final List currencyCoins; + + ChangeCalculator(List currencyCoins) { + this.currencyCoins = List.copyOf(currencyCoins); + } + + List computeMostEfficientChange(int grandTotal) { + if (grandTotal < 0) { + throw new IllegalArgumentException("Negative totals are not allowed."); + } + if (grandTotal == 0) { + return Collections.emptyList(); + } + + return currencyCoins.stream().map(coin -> { + int remaining = grandTotal - coin; + if (remaining == 0) { + return List.of(coin); + } + + try { + List result = new ArrayList<>(computeMostEfficientChange(remaining)); + result.add(coin); + result.sort(Integer::compare); + return result; + } catch (IllegalArgumentException e) { + return Collections.emptyList(); + } + }) + .filter(c -> !c.isEmpty()) + .min(Comparator.comparingInt(List::size)) + .orElseThrow(() -> new IllegalArgumentException("The total " + grandTotal + " cannot be represented in the given currency.")); + + } +} +``` + +## Approach: Dynamic Programming - Top down ```java import java.util.List; @@ -22,7 +70,7 @@ class ChangeCalculator { } List computeMostEfficientChange(int grandTotal) { - if (grandTotal < 0) + if (grandTotal < 0) throw new IllegalArgumentException("Negative totals are not allowed."); List> coinsUsed = new ArrayList<>(grandTotal + 1); @@ -49,7 +97,64 @@ class ChangeCalculator { } ``` -For a detailed look at the code and logic, see the full explanation in the [Dynamic Programming Approach][approach-dynamic-programming]. +For a detailed look at the code and logic, see the full explanation in the [Dynamic Programming - Top Down][approach-dynamic-programming-top-down]. + +## Approach: Dyanmic Programming - Bottom up + +```java +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +class ChangeCalculator { + + private final List currencyCoins; + + ChangeCalculator(List currencyCoins) { + this.currencyCoins = List.copyOf(currencyCoins); + } + + List computeMostEfficientChange(int grandTotal) { + if (grandTotal < 0) { + throw new IllegalArgumentException("Negative totals are not allowed."); + } + if (grandTotal == 0) { + return Collections.emptyList(); + } + Set reachableTotals = new HashSet<>(); + ArrayDeque> queue = new ArrayDeque<>(currencyCoins.stream().map(List::of).toList()); + + while (!queue.isEmpty()) { + List next = queue.poll(); + int total = next.stream().mapToInt(Integer::intValue).sum(); + if (total == grandTotal) { + return next; + } + if (total < grandTotal && reachableTotals.add(total)) { + for (Integer coin : currencyCoins) { + List toCheck = new ArrayList<>(next); + toCheck.add(coin); + queue.offer(toCheck); + } + } + } + + throw new IllegalArgumentException("The total " + grandTotal + " cannot be represented in the given currency."); + } +} +``` + +For a detailed look at the code and logic, see the full explanation in the [Dynamic Programming - Bottom Up][approach-dynamic-programming-bottom-up]. + +## Which approach to use? + +The recursive approach is generally inefficient compared to either dynamic programming approach because the recursion requires recalculating the most efficient change for certain amounts. +Both dynamic programming approaches avoids this by building on the results computed previously at each step. -[approach-dynamic-programming]: https://exercism.org/tracks/java/exercises/change/approaches/dynamic-programming +[approach-recursive]: https://exercism.org/tracks/java/exercises/change/approaches/recursive +[approach-dynamic-programming-top-down]: https://exercism.org/tracks/java/exercises/change/approaches/dynamic-programming-top-down +[approach-dynamic-programming-bottom-up]: https://exercism.org/tracks/java/exercises/change/approaches/dynamic-programming-bottom-up [dynamic-programming]: https://en.wikipedia.org/wiki/Dynamic_programming diff --git a/exercises/practice/change/.approaches/recursive/content.md b/exercises/practice/change/.approaches/recursive/content.md new file mode 100644 index 000000000..25ef36dd4 --- /dev/null +++ b/exercises/practice/change/.approaches/recursive/content.md @@ -0,0 +1,56 @@ +# Recursive + +```java +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +class ChangeCalculator { + + private final List currencyCoins; + + ChangeCalculator(List currencyCoins) { + this.currencyCoins = List.copyOf(currencyCoins); + } + + List computeMostEfficientChange(int grandTotal) { + if (grandTotal < 0) { + throw new IllegalArgumentException("Negative totals are not allowed."); + } + if (grandTotal == 0) { + return Collections.emptyList(); + } + + return currencyCoins.stream().map(coin -> { + int remaining = grandTotal - coin; + if (remaining == 0) { + return List.of(coin); + } + + try { + List result = new ArrayList<>(computeMostEfficientChange(remaining)); + result.add(coin); + result.sort(Integer::compare); + return result; + } catch (IllegalArgumentException e) { + return Collections.emptyList(); + } + }) + .filter(c -> !c.isEmpty()) + .min(Comparator.comparingInt(List::size)) + .orElseThrow(() -> new IllegalArgumentException("The total " + grandTotal + " cannot be represented in the given currency.")); + + } +} +``` + +The recursive approach works by iterating through the available coins and recursively calling itself to find the most efficient change with it. +It starts by validating the `grandTotal` argument. +If valid, use a stream to go through the available coins and determines how much change is still required if the coin is included. +If no more change is required, the most efficient change consists simply of the coin on its own. +Otherwise it will recursively call itself to find the most efficient change for the remaining amount. +The recursive call is done in a `try-catch` block because the method throws an `IllegalArgumentionException` if the change can not be made. +An empty list is used to indicate when the change can not be made in the stream. +The stream filters out the empty list in the next step before finding the smallest list. +If the stream is empty, an `IllegalArgumentException` is thrown to indicate the change could not be made. diff --git a/exercises/practice/change/.approaches/recursive/snippet.txt b/exercises/practice/change/.approaches/recursive/snippet.txt new file mode 100644 index 000000000..64c6f9df8 --- /dev/null +++ b/exercises/practice/change/.approaches/recursive/snippet.txt @@ -0,0 +1,7 @@ +List computeMostEfficientChange(int grandTotal) { + if (remaining == 0) + return List.of(coin); + + return currencyCoins.stream().map(coin -> + new ArrayList<>(computeMostEfficientChange(remaining)).add(coin)); +} \ No newline at end of file diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md index 9965c8ffc..6717d2046 100644 --- a/exercises/practice/collatz-conjecture/.approaches/introduction.md +++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md @@ -1,6 +1,6 @@ # Introduction -There are at east a couple of ways to solve Collatz Conjecture. +There are at least a couple of ways to solve Collatz Conjecture. One approach is to use a [`while`][while-loop] loop to iterate to the answer. Another approach is to use `IntStream.iterate()` to iterate to the answer. From 301df04cae7393928a7c41d4165688990e4690a9 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Wed, 20 Aug 2025 04:49:59 +0530 Subject: [PATCH 44/67] Add approach link (#2985) Also add "kahgoh" to contributor for 5ea692614c. --- exercises/practice/change/.approaches/config.json | 3 +++ exercises/practice/change/.approaches/introduction.md | 2 ++ 2 files changed, 5 insertions(+) diff --git a/exercises/practice/change/.approaches/config.json b/exercises/practice/change/.approaches/config.json index 46b784e67..c24280ab1 100644 --- a/exercises/practice/change/.approaches/config.json +++ b/exercises/practice/change/.approaches/config.json @@ -2,6 +2,9 @@ "introduction": { "authors": [ "jagdish-15" + ], + "contributors": [ + "kahgoh" ] }, "approaches": [ diff --git a/exercises/practice/change/.approaches/introduction.md b/exercises/practice/change/.approaches/introduction.md index 60f5e2937..de92106d8 100644 --- a/exercises/practice/change/.approaches/introduction.md +++ b/exercises/practice/change/.approaches/introduction.md @@ -56,6 +56,8 @@ class ChangeCalculator { } ``` +For a detailed look at the code and logic, see the full explanation in the [Recursive Approach][approach-recursive]. + ## Approach: Dynamic Programming - Top down ```java From 067d3ce71c91aaf415955e27ddaeb8723d61aef0 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Mon, 25 Aug 2025 17:52:02 +0530 Subject: [PATCH 45/67] acronym display-tags (#2986) --- .../practice/acronym/src/test/java/AcronymTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/exercises/practice/acronym/src/test/java/AcronymTest.java b/exercises/practice/acronym/src/test/java/AcronymTest.java index 4cc09f4cf..967c3f05a 100644 --- a/exercises/practice/acronym/src/test/java/AcronymTest.java +++ b/exercises/practice/acronym/src/test/java/AcronymTest.java @@ -1,16 +1,20 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; import static org.assertj.core.api.Assertions.assertThat; public class AcronymTest { + + @DisplayName("basic") @Test public void basic() { assertThat(new Acronym("Portable Network Graphics").get()) .isEqualTo("PNG"); } + @DisplayName("lowercase words") @Disabled("Remove to run test") @Test public void lowercaseWords() { @@ -18,6 +22,7 @@ public void lowercaseWords() { .isEqualTo("ROR"); } + @DisplayName("punctuation") @Disabled("Remove to run test") @Test public void punctuation() { @@ -25,6 +30,7 @@ public void punctuation() { .isEqualTo("FIFO"); } + @DisplayName("all caps word") @Disabled("Remove to run test") @Test public void nonAcronymAllCapsWord() { @@ -32,6 +38,7 @@ public void nonAcronymAllCapsWord() { .isEqualTo("GIMP"); } + @DisplayName("punctuation without whitespace") @Disabled("Remove to run test") @Test public void punctuationWithoutWhitespace() { @@ -39,6 +46,7 @@ public void punctuationWithoutWhitespace() { .isEqualTo("CMOS"); } + @DisplayName("very long abbreviation") @Disabled("Remove to run test") @Test public void veryLongAbbreviation() { @@ -46,6 +54,7 @@ public void veryLongAbbreviation() { .isEqualTo("ROTFLSHTMDCOALM"); } + @DisplayName("consecutive delimiters") @Disabled("Remove to run test") @Test public void consecutiveDelimiters() { @@ -53,6 +62,7 @@ public void consecutiveDelimiters() { .isEqualTo("SIMUFTA"); } + @DisplayName("apostrophes") @Disabled("Remove to run test") @Test public void apostrophes() { @@ -60,6 +70,7 @@ public void apostrophes() { .isEqualTo("HC"); } + @DisplayName("underscore emphasis") @Disabled("Remove to run test") @Test public void underscoreEmphasis() { From 544d22e48f4624c029f68b0d8fcc2e1aa48b31a3 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Wed, 27 Aug 2025 15:10:53 +0530 Subject: [PATCH 46/67] affine-cipher, all-your-base, allergies (#2987) * affine-cipher,all-your-base,allergies * acronym, affine-cipher, all-your-base, allergies * Change import order * Update AcronymTest.java [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../acronym/src/test/java/AcronymTest.java | 21 ++++---- .../src/test/java/AffineCipherTest.java | 18 ++++++- .../src/test/java/BaseConverterTest.java | 25 ++++++++- .../src/test/java/AllergiesTest.java | 51 +++++++++++++++++++ 4 files changed, 101 insertions(+), 14 deletions(-) diff --git a/exercises/practice/acronym/src/test/java/AcronymTest.java b/exercises/practice/acronym/src/test/java/AcronymTest.java index 967c3f05a..09b419c4a 100644 --- a/exercises/practice/acronym/src/test/java/AcronymTest.java +++ b/exercises/practice/acronym/src/test/java/AcronymTest.java @@ -1,78 +1,77 @@ import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class AcronymTest { - - @DisplayName("basic") @Test + @DisplayName("basic") public void basic() { assertThat(new Acronym("Portable Network Graphics").get()) .isEqualTo("PNG"); } - @DisplayName("lowercase words") @Disabled("Remove to run test") @Test + @DisplayName("lowercase words") public void lowercaseWords() { assertThat(new Acronym("Ruby on Rails").get()) .isEqualTo("ROR"); } - @DisplayName("punctuation") @Disabled("Remove to run test") @Test + @DisplayName("punctuation") public void punctuation() { assertThat(new Acronym("First In, First Out").get()) .isEqualTo("FIFO"); } - @DisplayName("all caps word") @Disabled("Remove to run test") @Test + @DisplayName("all caps word") public void nonAcronymAllCapsWord() { assertThat(new Acronym("GNU Image Manipulation Program").get()) .isEqualTo("GIMP"); } - @DisplayName("punctuation without whitespace") @Disabled("Remove to run test") @Test + @DisplayName("punctuation without whitespace") public void punctuationWithoutWhitespace() { assertThat(new Acronym("Complementary metal-oxide semiconductor").get()) .isEqualTo("CMOS"); } - @DisplayName("very long abbreviation") @Disabled("Remove to run test") @Test + @DisplayName("very long abbreviation") public void veryLongAbbreviation() { assertThat(new Acronym("Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me").get()) .isEqualTo("ROTFLSHTMDCOALM"); } - @DisplayName("consecutive delimiters") @Disabled("Remove to run test") @Test + @DisplayName("consecutive delimiters") public void consecutiveDelimiters() { assertThat(new Acronym("Something - I made up from thin air").get()) .isEqualTo("SIMUFTA"); } - @DisplayName("apostrophes") @Disabled("Remove to run test") @Test + @DisplayName("apostrophes") public void apostrophes() { assertThat(new Acronym("Halley's Comet").get()) .isEqualTo("HC"); } - @DisplayName("underscore emphasis") @Disabled("Remove to run test") @Test + @DisplayName("underscore emphasis") public void underscoreEmphasis() { assertThat(new Acronym("The Road _Not_ Taken").get()) .isEqualTo("TRNT"); diff --git a/exercises/practice/affine-cipher/src/test/java/AffineCipherTest.java b/exercises/practice/affine-cipher/src/test/java/AffineCipherTest.java index d37c96ade..668631a5c 100644 --- a/exercises/practice/affine-cipher/src/test/java/AffineCipherTest.java +++ b/exercises/practice/affine-cipher/src/test/java/AffineCipherTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -9,18 +10,20 @@ public class AffineCipherTest { private AffineCipher affineCipher = new AffineCipher(); @Test + @DisplayName("encode yes") public void testEncodeYes() { assertThat(affineCipher.encode("yes", 5, 7)).isEqualTo("xbt"); } @Disabled("Remove to run test") @Test + @DisplayName("encode no") public void testEncodeNo() { assertThat(affineCipher.encode("no", 15, 18)).isEqualTo("fu"); } - @Disabled("Remove to run test") + @DisplayName("encode OMG") @Test public void testEncodeOMG() { assertThat(affineCipher.encode("OMG", 21, 3)).isEqualTo("lvz"); @@ -28,18 +31,21 @@ public void testEncodeOMG() { @Disabled("Remove to run test") @Test + @DisplayName("encode O M G") public void testEncodeO_M_G() { assertThat(affineCipher.encode("O M G", 25, 47)).isEqualTo("hjp"); } @Disabled("Remove to run test") @Test + @DisplayName("encode mindblowingly") public void testEncodeMindBlowingly() { assertThat(affineCipher.encode("mindblowingly", 11, 15)).isEqualTo("rzcwa gnxzc dgt"); } @Disabled("Remove to run test") @Test + @DisplayName("encode numbers") public void testEncodeNumbers() { assertThat(affineCipher.encode("Testing,1 2 3, testing.", 3, 4)) .isEqualTo("jqgjc rw123 jqgjc rw"); @@ -47,6 +53,7 @@ public void testEncodeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("encode deep thought") public void testEncodeDeepThought() { assertThat(affineCipher.encode("Truth is fiction.", 5, 17)) .isEqualTo("iynia fdqfb ifje"); @@ -54,6 +61,7 @@ public void testEncodeDeepThought() { @Disabled("Remove to run test") @Test + @DisplayName("encode all the letters") public void testEncodeAllTheLetters() { assertThat(affineCipher.encode("The quick brown fox jumps over the lazy dog.", 17, 33)) .isEqualTo("swxtj npvyk lruol iejdc blaxk swxmh qzglf"); @@ -61,6 +69,7 @@ public void testEncodeAllTheLetters() { @Disabled("Remove to run test") @Test + @DisplayName("encode with a not coprime to m") public void testEncodeThrowsMeaningfulException() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> affineCipher.encode("This is a test", 6, 17)) @@ -69,6 +78,7 @@ public void testEncodeThrowsMeaningfulException() { @Disabled("Remove to run test") @Test + @DisplayName("decode exercism") public void testDecodeExercism() { assertThat(affineCipher.decode("tytgn fjr", 3, 7)) .isEqualTo("exercism"); @@ -76,6 +86,7 @@ public void testDecodeExercism() { @Disabled("Remove to run test") @Test + @DisplayName("decode a sentence") public void testDecodeSentence() { assertThat(affineCipher.decode("qdwju nqcro muwhn odqun oppmd aunwd o", 19, 16)) .isEqualTo("anobstacleisoftenasteppingstone"); @@ -83,6 +94,7 @@ public void testDecodeSentence() { @Disabled("Remove to run test") @Test + @DisplayName("decode numbers") public void testDecodeNumbers() { assertThat(affineCipher.decode("odpoz ub123 odpoz ub", 25, 7)) .isEqualTo("testing123testing"); @@ -90,6 +102,7 @@ public void testDecodeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("decode all the letters") public void testDecodeAllTheLetters() { assertThat(affineCipher.decode("swxtj npvyk lruol iejdc blaxk swxmh qzglf", 17, 33)) .isEqualTo("thequickbrownfoxjumpsoverthelazydog"); @@ -97,6 +110,7 @@ public void testDecodeAllTheLetters() { @Disabled("Remove to run test") @Test + @DisplayName("decode with no spaces in input") public void testDecodeWithNoSpaces() { assertThat(affineCipher.decode("swxtjnpvyklruoliejdcblaxkswxmhqzglf", 17, 33)) .isEqualTo("thequickbrownfoxjumpsoverthelazydog"); @@ -104,6 +118,7 @@ public void testDecodeWithNoSpaces() { @Disabled("Remove to run test") @Test + @DisplayName("decode with too many spaces") public void testDecodeWithTooManySpaces() { assertThat(affineCipher.decode("vszzm cly yd cg qdp", 15, 16)) .isEqualTo("jollygreengiant"); @@ -111,6 +126,7 @@ public void testDecodeWithTooManySpaces() { @Disabled("Remove to run test") @Test + @DisplayName("decode with a not coprime to m") public void testDecodeThrowsMeaningfulException() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> affineCipher.decode("Test", 13, 5)) diff --git a/exercises/practice/all-your-base/src/test/java/BaseConverterTest.java b/exercises/practice/all-your-base/src/test/java/BaseConverterTest.java index c84e97af0..c2ca95a70 100644 --- a/exercises/practice/all-your-base/src/test/java/BaseConverterTest.java +++ b/exercises/practice/all-your-base/src/test/java/BaseConverterTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -7,6 +8,7 @@ public class BaseConverterTest { @Test + @DisplayName("single bit one to decimal") public void testSingleBitOneToDecimal() { BaseConverter baseConverter = new BaseConverter(2, new int[]{1}); @@ -16,6 +18,7 @@ public void testSingleBitOneToDecimal() { @Disabled("Remove to run test") @Test + @DisplayName("binary to single decimal") public void testBinaryToSingleDecimal() { BaseConverter baseConverter = new BaseConverter(2, new int[]{1, 0, 1}); @@ -25,6 +28,7 @@ public void testBinaryToSingleDecimal() { @Disabled("Remove to run test") @Test + @DisplayName("single decimal to binary") public void testSingleDecimalToBinary() { BaseConverter baseConverter = new BaseConverter(10, new int[]{5}); @@ -34,6 +38,7 @@ public void testSingleDecimalToBinary() { @Disabled("Remove to run test") @Test + @DisplayName("binary to multiple decimal") public void testBinaryToMultipleDecimal() { BaseConverter baseConverter = new BaseConverter(2, new int[]{1, 0, 1, 0, 1, 0}); @@ -43,6 +48,7 @@ public void testBinaryToMultipleDecimal() { @Disabled("Remove to run test") @Test + @DisplayName("decimal to binary") public void testDecimalToBinary() { BaseConverter baseConverter = new BaseConverter(10, new int[]{4, 2}); @@ -52,6 +58,7 @@ public void testDecimalToBinary() { @Disabled("Remove to run test") @Test + @DisplayName("trinary to hexadecimal") public void testTrinaryToHexadecimal() { BaseConverter baseConverter = new BaseConverter(3, new int[]{1, 1, 2, 0}); @@ -61,6 +68,7 @@ public void testTrinaryToHexadecimal() { @Disabled("Remove to run test") @Test + @DisplayName("hexadecimal to trinary") public void testHexadecimalToTrinary() { BaseConverter baseConverter = new BaseConverter(16, new int[]{2, 10}); @@ -70,6 +78,7 @@ public void testHexadecimalToTrinary() { @Disabled("Remove to run test") @Test + @DisplayName("15-bit integer") public void test15BitInteger() { BaseConverter baseConverter = new BaseConverter(97, new int[]{3, 46, 60}); @@ -79,6 +88,7 @@ public void test15BitInteger() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testEmptyDigits() { BaseConverter baseConverter = new BaseConverter(2, new int[]{}); @@ -88,6 +98,7 @@ public void testEmptyDigits() { @Disabled("Remove to run test") @Test + @DisplayName("single zero") public void testSingleZero() { BaseConverter baseConverter = new BaseConverter(10, new int[]{0}); @@ -97,6 +108,7 @@ public void testSingleZero() { @Disabled("Remove to run test") @Test + @DisplayName("multiple zeros") public void testMultipleZeros() { BaseConverter baseConverter = new BaseConverter(10, new int[]{0, 0, 0}); @@ -106,6 +118,7 @@ public void testMultipleZeros() { @Disabled("Remove to run test") @Test + @DisplayName("leading zeros") public void testLeadingZeros() { BaseConverter baseConverter = new BaseConverter(7, new int[]{0, 6, 0}); @@ -115,22 +128,25 @@ public void testLeadingZeros() { @Disabled("Remove to run test") @Test + @DisplayName("input base is one") public void testFirstBaseIsOne() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> new BaseConverter(1, new int[]{1})) + .isThrownBy(() -> new BaseConverter(1, new int[]{0})) .withMessage("Bases must be at least 2."); } @Disabled("Remove to run test") @Test + @DisplayName("input base is zero") public void testFirstBaseIsZero() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> new BaseConverter(0, new int[]{1})) + .isThrownBy(() -> new BaseConverter(0, new int[]{})) .withMessage("Bases must be at least 2."); } @Disabled("Remove to run test") @Test + @DisplayName("input base is negative") public void testFirstBaseIsNegative() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new BaseConverter(-2, new int[]{1})) @@ -139,6 +155,7 @@ public void testFirstBaseIsNegative() { @Disabled("Remove to run test") @Test + @DisplayName("negative digit") public void testNegativeDigit() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new BaseConverter(2, new int[]{1, -1, 1, 0, 1, 0})) @@ -147,6 +164,7 @@ public void testNegativeDigit() { @Disabled("Remove to run test") @Test + @DisplayName("invalid positive digit") public void testInvalidPositiveDigit() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new BaseConverter(2, new int[]{1, 2, 1, 0, 1, 0})) @@ -155,6 +173,7 @@ public void testInvalidPositiveDigit() { @Disabled("Remove to run test") @Test + @DisplayName("output base is one") public void testSecondBaseIsOne() { BaseConverter baseConverter = new BaseConverter(2, new int[]{1, 0, 1, 0, 1, 0}); @@ -166,6 +185,7 @@ public void testSecondBaseIsOne() { @Disabled("Remove to run test") @Test + @DisplayName("output base is zero") public void testSecondBaseIsZero() { BaseConverter baseConverter = new BaseConverter(10, new int[]{7}); @@ -176,6 +196,7 @@ public void testSecondBaseIsZero() { @Disabled("Remove to run test") @Test + @DisplayName("output base is negative") public void testSecondBaseIsNegative() { BaseConverter baseConverter = new BaseConverter(2, new int[]{1}); diff --git a/exercises/practice/allergies/src/test/java/AllergiesTest.java b/exercises/practice/allergies/src/test/java/AllergiesTest.java index 851b40656..cd0ae5cf7 100644 --- a/exercises/practice/allergies/src/test/java/AllergiesTest.java +++ b/exercises/practice/allergies/src/test/java/AllergiesTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -9,6 +10,7 @@ public class AllergiesTest { // Testing for eggs allergy @Test + @DisplayName("not allergic to anything") public void eggsNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -17,6 +19,7 @@ public void eggsNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to eggs") public void eggsAllergicOnlyToEggs() { Allergies allergies = new Allergies(1); @@ -25,6 +28,7 @@ public void eggsAllergicOnlyToEggs() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to eggs and something else") public void eggsAllergicToEggsAndSomethingElse() { Allergies allergies = new Allergies(3); @@ -33,6 +37,7 @@ public void eggsAllergicToEggsAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not eggs") public void eggsAllergicToSomethingButNotEggs() { Allergies allergies = new Allergies(2); @@ -41,6 +46,7 @@ public void eggsAllergicToSomethingButNotEggs() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void eggsAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -51,6 +57,7 @@ public void eggsAllergicToEverything() { // Testing for peanuts allergy @Test + @DisplayName("not allergic to anything") public void peanutsNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -59,6 +66,7 @@ public void peanutsNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to peanuts") public void peanutsAllergicOnlyToPeanuts() { Allergies allergies = new Allergies(2); @@ -67,6 +75,7 @@ public void peanutsAllergicOnlyToPeanuts() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to peanuts and something else") public void peanutsAllergicToPeanutsAndSomethingElse() { Allergies allergies = new Allergies(7); @@ -75,6 +84,7 @@ public void peanutsAllergicToPeanutsAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not peanuts") public void peanutsAllergicToSomethingButNotPeanuts() { Allergies allergies = new Allergies(5); @@ -83,6 +93,7 @@ public void peanutsAllergicToSomethingButNotPeanuts() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void peanutsAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -93,6 +104,7 @@ public void peanutsAllergicToEverything() { // Testing for shellfish allergy @Test + @DisplayName("not allergic to anything") public void shellfishNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -101,6 +113,7 @@ public void shellfishNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to shellfish") public void shellfishAllergicOnlyToShellfish() { Allergies allergies = new Allergies(4); @@ -109,6 +122,7 @@ public void shellfishAllergicOnlyToShellfish() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to shellfish and something else") public void shellfishAllergicToShellfishAndSomethingElse() { Allergies allergies = new Allergies(14); @@ -117,6 +131,7 @@ public void shellfishAllergicToShellfishAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not shellfish") public void shellfishAllergicToSomethingButNotShellfish() { Allergies allergies = new Allergies(10); @@ -125,6 +140,7 @@ public void shellfishAllergicToSomethingButNotShellfish() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void shellfishAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -135,6 +151,7 @@ public void shellfishAllergicToEverything() { // Testing for strawberries allergy @Test + @DisplayName("not allergic to anything") public void strawberriesNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -143,6 +160,7 @@ public void strawberriesNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to strawberries") public void strawberriesAllergicOnlyToStrawberries() { Allergies allergies = new Allergies(8); @@ -151,6 +169,7 @@ public void strawberriesAllergicOnlyToStrawberries() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to strawberries and something else") public void strawberriesAllergicToStrawberriesAndSomethingElse() { Allergies allergies = new Allergies(28); @@ -159,6 +178,7 @@ public void strawberriesAllergicToStrawberriesAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not strawberries") public void strawberriesAllergicToSomethingButNotStrawberries() { Allergies allergies = new Allergies(20); @@ -167,6 +187,7 @@ public void strawberriesAllergicToSomethingButNotStrawberries() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void strawberriesAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -177,6 +198,7 @@ public void strawberriesAllergicToEverything() { // Testing for tomatoes allergy @Test + @DisplayName("not allergic to anything") public void tomatoesNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -185,6 +207,7 @@ public void tomatoesNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to tomatoes") public void tomatoesAllergicOnlyToTomatoes() { Allergies allergies = new Allergies(16); @@ -193,6 +216,7 @@ public void tomatoesAllergicOnlyToTomatoes() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to tomatoes and something else") public void tomatoesAllergicToTomatoesAndSomethingElse() { Allergies allergies = new Allergies(56); @@ -201,6 +225,7 @@ public void tomatoesAllergicToTomatoesAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not tomatoes") public void tomatoesAllergicToSomethingButNotTomatoes() { Allergies allergies = new Allergies(40); @@ -209,6 +234,7 @@ public void tomatoesAllergicToSomethingButNotTomatoes() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void tomatoesAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -219,6 +245,7 @@ public void tomatoesAllergicToEverything() { // Testing for chocolate allergy @Test + @DisplayName("not allergic to anything") public void chocolateNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -227,6 +254,7 @@ public void chocolateNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to chocolate") public void chocolateAllergicOnlyToChocolate() { Allergies allergies = new Allergies(32); @@ -235,6 +263,7 @@ public void chocolateAllergicOnlyToChocolate() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to chocolate and something else") public void chocolateAllergicToChocolateAndSomethingElse() { Allergies allergies = new Allergies(112); @@ -243,6 +272,7 @@ public void chocolateAllergicToChocolateAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not chocolate") public void chocolateAllergicToSomethingButNotChocolate() { Allergies allergies = new Allergies(80); @@ -251,6 +281,7 @@ public void chocolateAllergicToSomethingButNotChocolate() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void chocolateAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -261,6 +292,7 @@ public void chocolateAllergicToEverything() { // Testing for pollen allergy @Test + @DisplayName("not allergic to anything") public void pollenNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -269,6 +301,7 @@ public void pollenNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to pollen") public void pollenAllergicOnlyToPollen() { Allergies allergies = new Allergies(64); @@ -277,6 +310,7 @@ public void pollenAllergicOnlyToPollen() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to pollen and something else") public void pollenAllergicToPollenAndSomethingElse() { Allergies allergies = new Allergies(224); @@ -285,6 +319,7 @@ public void pollenAllergicToPollenAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not pollen") public void pollenAllergicToSomethingButNotPollen() { Allergies allergies = new Allergies(160); @@ -293,6 +328,7 @@ public void pollenAllergicToSomethingButNotPollen() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void pollenAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -303,6 +339,7 @@ public void pollenAllergicToEverything() { // Testing for cats allergy @Test + @DisplayName("not allergic to anything") public void catsNotAllergicToAnything() { Allergies allergies = new Allergies(0); @@ -311,6 +348,7 @@ public void catsNotAllergicToAnything() { @Disabled("Remove to run test") @Test + @DisplayName("allergic only to cats") public void catsAllergicOnlyToCats() { Allergies allergies = new Allergies(128); @@ -319,6 +357,7 @@ public void catsAllergicOnlyToCats() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to cats and something else") public void catsAllergicToCatsAndSomethingElse() { Allergies allergies = new Allergies(192); @@ -327,6 +366,7 @@ public void catsAllergicToCatsAndSomethingElse() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to something, but not cats") public void catsAllergicToSomethingButNotCats() { Allergies allergies = new Allergies(64); @@ -335,6 +375,7 @@ public void catsAllergicToSomethingButNotCats() { @Disabled("Remove to run test") @Test + @DisplayName("allergic to everything") public void catsAllergicToEverything() { Allergies allergies = new Allergies(255); @@ -346,6 +387,7 @@ public void catsAllergicToEverything() { @Disabled("Remove to run test") @Test + @DisplayName("no allergies") public void listNoAllergies() { Allergies allergies = new Allergies(0); @@ -354,6 +396,7 @@ public void listNoAllergies() { @Disabled("Remove to run test") @Test + @DisplayName("just eggs") public void listJustEggs() { Allergies allergies = new Allergies(1); @@ -363,6 +406,7 @@ public void listJustEggs() { @Disabled("Remove to run test") @Test + @DisplayName("just peanuts") public void listJustPeanuts() { Allergies allergies = new Allergies(2); @@ -372,6 +416,7 @@ public void listJustPeanuts() { @Disabled("Remove to run test") @Test + @DisplayName("just strawberries") public void listJustStrawberries() { Allergies allergies = new Allergies(8); @@ -381,6 +426,7 @@ public void listJustStrawberries() { @Disabled("Remove to run test") @Test + @DisplayName("eggs and peanuts") public void listEggsAndPeanuts() { Allergies allergies = new Allergies(3); @@ -392,6 +438,7 @@ public void listEggsAndPeanuts() { @Disabled("Remove to run test") @Test + @DisplayName("more than eggs but not peanuts") public void listoMoreThanEggsButNotPeanuts() { Allergies allergies = new Allergies(5); @@ -403,6 +450,7 @@ public void listoMoreThanEggsButNotPeanuts() { @Disabled("Remove to run test") @Test + @DisplayName("lots of stuff") public void listManyAllergies() { Allergies allergies = new Allergies(248); @@ -417,6 +465,7 @@ public void listManyAllergies() { @Disabled("Remove to run test") @Test + @DisplayName("everything") public void listEverything() { Allergies allergies = new Allergies(255); @@ -434,6 +483,7 @@ public void listEverything() { @Disabled("Remove to run test") @Test + @DisplayName("no allergen score parts") public void listNoAllergenScoreParts() { Allergies allergies = new Allergies(509); @@ -450,6 +500,7 @@ public void listNoAllergenScoreParts() { @Disabled("Remove to run test") @Test + @DisplayName("no allergen score parts without highest valid score") public void listNoAllergenScorePartsWithoutHighestValidScore() { Allergies allergies = new Allergies(257); From 51896fc839f647979182ce76190dd8654b12305f Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Wed, 27 Aug 2025 15:12:21 +0530 Subject: [PATCH 47/67] Finished with adding @DisplayName to alphametics, anagram, armstrong-numbers, atbash-cipher (#2990) * alphametics, anagram, armstrong-numbers, atbash-cipher * alphametics, armstrong-numbers [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../src/test/java/AlphameticsTest.java | 13 ++++++++++++- .../anagram/src/test/java/AnagramTest.java | 19 +++++++++++++++++++ .../src/test/java/ArmstrongNumbersTest.java | 10 ++++++++++ .../src/test/java/AtbashTest.java | 15 +++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/exercises/practice/alphametics/src/test/java/AlphameticsTest.java b/exercises/practice/alphametics/src/test/java/AlphameticsTest.java index 93aef51be..8661a8de3 100644 --- a/exercises/practice/alphametics/src/test/java/AlphameticsTest.java +++ b/exercises/practice/alphametics/src/test/java/AlphameticsTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static java.util.Map.entry; @@ -8,6 +9,7 @@ public class AlphameticsTest { @Test + @DisplayName("puzzle with three letters") public void testThreeLetters() throws UnsolvablePuzzleException { assertThat(new Alphametics("I + BB == ILL").solve()) .containsOnly( @@ -18,6 +20,7 @@ public void testThreeLetters() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test + @DisplayName("solution must have unique value for each letter") public void testUniqueValue() { Alphametics alphametics = new Alphametics("A == B"); @@ -27,6 +30,7 @@ public void testUniqueValue() { @Disabled("Remove to run test") @Test + @DisplayName("leading zero solution is invalid") public void testLeadingZero() { Alphametics alphametics = new Alphametics("ACA + DD == BD"); @@ -36,6 +40,7 @@ public void testLeadingZero() { @Disabled("Remove to run test") @Test + @DisplayName("puzzle with two digits final carry") public void testTwoDigitsFinalCarry() throws UnsolvablePuzzleException { assertThat(new Alphametics("A + A + A + A + A + A + A + A + A + A + A + B == BCC").solve()) .containsOnly( @@ -46,6 +51,7 @@ public void testTwoDigitsFinalCarry() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test + @DisplayName("puzzle with four letters") public void testFourLetters() throws UnsolvablePuzzleException { assertThat(new Alphametics("AS + A == MOM").solve()) .containsOnly( @@ -57,6 +63,7 @@ public void testFourLetters() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test + @DisplayName("puzzle with six letters") public void testSixLetters() throws UnsolvablePuzzleException { assertThat(new Alphametics("NO + NO + TOO == LATE").solve()) .containsOnly( @@ -70,6 +77,7 @@ public void testSixLetters() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test + @DisplayName("puzzle with seven letters") public void testSevenLetters() throws UnsolvablePuzzleException { assertThat(new Alphametics("HE + SEES + THE == LIGHT").solve()) .containsOnly( @@ -84,6 +92,7 @@ public void testSevenLetters() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test + @DisplayName("puzzle with eight letters") public void testEightLetters() throws UnsolvablePuzzleException { assertThat(new Alphametics("SEND + MORE == MONEY").solve()) .containsOnly( @@ -99,6 +108,7 @@ public void testEightLetters() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test + @DisplayName("puzzle with ten letters") public void testTenLetters() throws UnsolvablePuzzleException { assertThat(new Alphametics("AND + A + STRONG + OFFENSE + AS + A + GOOD == DEFENSE").solve()) .containsOnly( @@ -116,7 +126,8 @@ public void testTenLetters() throws UnsolvablePuzzleException { @Disabled("Remove to run test") @Test - public void testTenLetters41Addends() throws UnsolvablePuzzleException { + @DisplayName("puzzle with ten letters and 199 addends") + public void testTenLetters199Addends() throws UnsolvablePuzzleException { assertThat(new Alphametics("THIS + A + FIRE + THEREFORE + FOR + ALL + HISTORIES + I + TELL + A + " + "TALE + THAT + FALSIFIES + ITS + TITLE + TIS + A + LIE + THE + TALE + OF + THE + LAST + FIRE + " + "HORSES + LATE + AFTER + THE + FIRST + FATHERS + FORESEE + THE + HORRORS + THE + LAST + FREE + " + diff --git a/exercises/practice/anagram/src/test/java/AnagramTest.java b/exercises/practice/anagram/src/test/java/AnagramTest.java index e5434291a..79a3a261b 100644 --- a/exercises/practice/anagram/src/test/java/AnagramTest.java +++ b/exercises/practice/anagram/src/test/java/AnagramTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -9,6 +10,7 @@ public class AnagramTest { @Test + @DisplayName("no matches") public void testNoMatches() { Anagram detector = new Anagram("diaper"); @@ -20,6 +22,7 @@ public void testNoMatches() { @Disabled("Remove to run test") @Test + @DisplayName("detects two anagrams") public void testDetectsTwoAnagrams() { Anagram detector = new Anagram("solemn"); @@ -29,6 +32,7 @@ public void testDetectsTwoAnagrams() { @Disabled("Remove to run test") @Test + @DisplayName("does not detect anagram subsets") public void testEliminateAnagramSubsets() { Anagram detector = new Anagram("good"); @@ -37,6 +41,7 @@ public void testEliminateAnagramSubsets() { @Disabled("Remove to run test") @Test + @DisplayName("detects anagram") public void testDetectLongerAnagram() { Anagram detector = new Anagram("listen"); @@ -48,6 +53,7 @@ public void testDetectLongerAnagram() { @Disabled("Remove to run test") @Test + @DisplayName("detects three anagrams") public void testDetectMultipleAnagramsForLongerWord() { Anagram detector = new Anagram("allergy"); assertThat( @@ -64,6 +70,7 @@ public void testDetectMultipleAnagramsForLongerWord() { @Disabled("Remove to run test") @Test + @DisplayName("detects multiple anagrams with different case") public void testDetectsMultipleAnagramsWithDifferentCase() { Anagram detector = new Anagram("nose"); @@ -73,6 +80,7 @@ public void testDetectsMultipleAnagramsWithDifferentCase() { @Disabled("Remove to run test") @Test + @DisplayName("does not detect non-anagrams with identical checksum") public void testEliminateAnagramsWithSameChecksum() { Anagram detector = new Anagram("mass"); @@ -82,6 +90,7 @@ public void testEliminateAnagramsWithSameChecksum() { @Disabled("Remove to run test") @Test + @DisplayName("detects anagrams case-insensitively") public void testCaseInsensitiveWhenBothAnagramAndSubjectStartWithUpperCaseLetter() { Anagram detector = new Anagram("Orchestra"); @@ -93,6 +102,7 @@ public void testCaseInsensitiveWhenBothAnagramAndSubjectStartWithUpperCaseLetter @Disabled("Remove to run test") @Test + @DisplayName("detects anagrams using case-insensitive subject") public void testCaseInsensitiveWhenSubjectStartsWithUpperCaseLetter() { Anagram detector = new Anagram("Orchestra"); @@ -104,6 +114,7 @@ public void testCaseInsensitiveWhenSubjectStartsWithUpperCaseLetter() { @Disabled("Remove to run test") @Test + @DisplayName("detects anagrams using case-insensitive possible matches") public void testCaseInsensitiveWhenAnagramStartsWithUpperCaseLetter() { Anagram detector = new Anagram("orchestra"); @@ -115,6 +126,7 @@ public void testCaseInsensitiveWhenAnagramStartsWithUpperCaseLetter() { @Disabled("Remove to run test") @Test + @DisplayName("does not detect an anagram if the original word is repeated") public void testIdenticalWordRepeatedIsNotAnagram() { Anagram detector = new Anagram("go"); @@ -124,6 +136,7 @@ public void testIdenticalWordRepeatedIsNotAnagram() { @Disabled("Remove to run test") @Test + @DisplayName("anagrams must use all letters exactly once") public void testAnagramMustUseAllLettersExactlyOnce() { Anagram detector = new Anagram("tapper"); @@ -133,6 +146,7 @@ public void testAnagramMustUseAllLettersExactlyOnce() { @Disabled("Remove to run test") @Test + @DisplayName("words are not anagrams of themselves") public void testWordsAreNotAnagramsOfThemselvesCaseInsensitive() { Anagram detector = new Anagram("BANANA"); @@ -142,6 +156,7 @@ public void testWordsAreNotAnagramsOfThemselvesCaseInsensitive() { @Disabled("Remove to run test") @Test + @DisplayName("words are not anagrams of themselves even if letter case is partially different") public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsPartiallyDifferent() { Anagram detector = new Anagram("BANANA"); @@ -151,6 +166,7 @@ public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsPartiallyDiffer @Disabled("Remove to run test") @Test + @DisplayName("words are not anagrams of themselves even if letter case is completely different") public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsCompletelyDifferent() { Anagram detector = new Anagram("BANANA"); @@ -160,6 +176,7 @@ public void testWordsAreNotAnagramsOfThemselvesEvenIfLetterCaseIsCompletelyDiffe @Disabled("Remove to run test") @Test + @DisplayName("words other than themselves can be anagrams") public void testWordsOtherThanThemselvesCanBeAnagrams() { Anagram detector = new Anagram("LISTEN"); @@ -169,6 +186,7 @@ public void testWordsOtherThanThemselvesCanBeAnagrams() { @Disabled("Remove to run test") @Test + @DisplayName("handles case of greek letters") public void testHandlesCaseOfGreekLetters() { Anagram detector = new Anagram("ΑΒΓ"); @@ -178,6 +196,7 @@ public void testHandlesCaseOfGreekLetters() { @Disabled("Remove to run test") @Test + @DisplayName("different characters may have the same bytes") public void testDifferentCharactersWithSameBytes() { Anagram detector = new Anagram("a⬂"); diff --git a/exercises/practice/armstrong-numbers/src/test/java/ArmstrongNumbersTest.java b/exercises/practice/armstrong-numbers/src/test/java/ArmstrongNumbersTest.java index ea08d7259..8977970dc 100644 --- a/exercises/practice/armstrong-numbers/src/test/java/ArmstrongNumbersTest.java +++ b/exercises/practice/armstrong-numbers/src/test/java/ArmstrongNumbersTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ public void setup() { } @Test + @DisplayName("Zero is an Armstrong number") public void zeroIsArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(0)) .isTrue(); @@ -21,6 +23,7 @@ public void zeroIsArmstrongNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Single-digit numbers are Armstrong numbers") public void singleDigitsAreArmstrongNumbers() { assertThat(armstrongNumbers.isArmstrongNumber(5)) .isTrue(); @@ -28,6 +31,7 @@ public void singleDigitsAreArmstrongNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("There are no two-digit Armstrong numbers") public void noTwoDigitArmstrongNumbers() { assertThat(armstrongNumbers.isArmstrongNumber(10)) .isFalse(); @@ -35,6 +39,7 @@ public void noTwoDigitArmstrongNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Three-digit number that is an Armstrong number") public void threeDigitNumberIsArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(153)) .isTrue(); @@ -42,6 +47,7 @@ public void threeDigitNumberIsArmstrongNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Three-digit number that is not an Armstrong number") public void threeDigitNumberIsNotArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(100)) .isFalse(); @@ -49,6 +55,7 @@ public void threeDigitNumberIsNotArmstrongNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Four-digit number that is an Armstrong number") public void fourDigitNumberIsArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(9474)) .isTrue(); @@ -56,6 +63,7 @@ public void fourDigitNumberIsArmstrongNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Four-digit number that is not an Armstrong number") public void fourDigitNumberIsNotArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(9475)) .isFalse(); @@ -63,6 +71,7 @@ public void fourDigitNumberIsNotArmstrongNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Seven-digit number that is an Armstrong number") public void sevenDigitNumberIsArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(9926315)) .isTrue(); @@ -70,6 +79,7 @@ public void sevenDigitNumberIsArmstrongNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Seven-digit number that is not an Armstrong number") public void sevenDigitNumberIsNotArmstrongNumber() { assertThat(armstrongNumbers.isArmstrongNumber(9926314)) .isFalse(); diff --git a/exercises/practice/atbash-cipher/src/test/java/AtbashTest.java b/exercises/practice/atbash-cipher/src/test/java/AtbashTest.java index f656a69dc..c914f8b28 100644 --- a/exercises/practice/atbash-cipher/src/test/java/AtbashTest.java +++ b/exercises/practice/atbash-cipher/src/test/java/AtbashTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,36 +15,42 @@ public void setup() { } @Test + @DisplayName("encode yes") public void testEncodeYes() { assertThat(atbash.encode("yes")).isEqualTo("bvh"); } @Disabled("Remove to run test") @Test + @DisplayName("encode no") public void testEncodeNo() { assertThat(atbash.encode("no")).isEqualTo("ml"); } @Disabled("Remove to run test") @Test + @DisplayName("encode OMG") public void testEncodeOmgInCapital() { assertThat(atbash.encode("OMG")).isEqualTo("lnt"); } @Disabled("Remove to run test") @Test + @DisplayName("encode spaces") public void testEncodeOmgWithSpaces() { assertThat(atbash.encode("O M G")).isEqualTo("lnt"); } @Disabled("Remove to run test") @Test + @DisplayName("encode mindblowingly") public void testEncodeMindBlowingly() { assertThat(atbash.encode("mindblowingly")).isEqualTo("nrmwy oldrm tob"); } @Disabled("Remove to run test") @Test + @DisplayName("encode numbers") public void testEncodeNumbers() { assertThat(atbash.encode("Testing,1 2 3, testing.")) .isEqualTo("gvhgr mt123 gvhgr mt"); @@ -51,6 +58,7 @@ public void testEncodeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("encode deep thought") public void testEncodeDeepThought() { assertThat(atbash.encode("Truth is fiction.")) .isEqualTo("gifgs rhurx grlm"); @@ -58,6 +66,7 @@ public void testEncodeDeepThought() { @Disabled("Remove to run test") @Test + @DisplayName("encode all the letters") public void testEncodeAllTheLetters() { assertThat(atbash.encode("The quick brown fox jumps over the lazy dog.")) .isEqualTo("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt"); @@ -65,12 +74,14 @@ public void testEncodeAllTheLetters() { @Disabled("Remove to run test") @Test + @DisplayName("decode exercism") public void testDecodeExercism() { assertThat(atbash.decode("vcvix rhn")).isEqualTo("exercism"); } @Disabled("Remove to run test") @Test + @DisplayName("decode a sentence") public void testDecodeASentence() { assertThat(atbash.decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v")) .isEqualTo("anobstacleisoftenasteppingstone"); @@ -78,6 +89,7 @@ public void testDecodeASentence() { @Disabled("Remove to run test") @Test + @DisplayName("decode numbers") public void testDecodeNumbers() { assertThat(atbash.decode("gvhgr mt123 gvhgr mt")) .isEqualTo("testing123testing"); @@ -85,6 +97,7 @@ public void testDecodeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("decode all the letters") public void testDecodeAllTheLetters() { assertThat(atbash.decode("gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt")) .isEqualTo("thequickbrownfoxjumpsoverthelazydog"); @@ -92,12 +105,14 @@ public void testDecodeAllTheLetters() { @Disabled("Remove to run test") @Test + @DisplayName("decode with too many spaces") public void testDecodeWithTooManySpaces() { assertThat(atbash.decode("vc vix r hn")).isEqualTo("exercism"); } @Disabled("Remove to run test") @Test + @DisplayName("decode with no spaces") public void testDecodeWithNoSpaces() { assertThat(atbash.decode("zmlyhgzxovrhlugvmzhgvkkrmthglmv")) .isEqualTo("anobstacleisoftenasteppingstone"); From b82141623bce645c0095518317e9a104de769b98 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Sat, 30 Aug 2025 11:17:16 +0530 Subject: [PATCH 48/67] Completed with adding @DisplayName to bank-account, binary-search-tree, binary-search, bob, book-store (#2991) * alphametics, anagram, armstrong-numbers, atbash-cipher * alphametics, armstrong-numbers * bank-account, binary-search-tree, binary-search, bob, book-store * Update exercises/practice/book-store/src/test/java/BookStoreTest.java Co-authored-by: Jagdish Prajapati * Update exercises/practice/book-store/src/test/java/BookStoreTest.java Co-authored-by: Jagdish Prajapati * binary-search * Update exercises/practice/book-store/src/test/java/BookStoreTest.java Co-authored-by: Jagdish Prajapati [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../src/test/java/BankAccountTest.java | 20 +++++++++++++- .../src/test/java/BinarySearchTreeTest.java | 11 ++++++++ .../src/test/java/BinarySearchTest.java | 12 +++++++++ .../practice/bob/src/test/java/BobTest.java | 26 +++++++++++++++++++ .../src/test/java/BookStoreTest.java | 25 ++++++++++++++++-- 5 files changed, 91 insertions(+), 3 deletions(-) diff --git a/exercises/practice/bank-account/src/test/java/BankAccountTest.java b/exercises/practice/bank-account/src/test/java/BankAccountTest.java index 35b4e04a0..8c6d65de7 100644 --- a/exercises/practice/bank-account/src/test/java/BankAccountTest.java +++ b/exercises/practice/bank-account/src/test/java/BankAccountTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Random; @@ -15,6 +16,7 @@ public void setUp() { } @Test + @DisplayName("Newly opened account has zero balance") public void newlyOpenedAccountHasEmptyBalance() throws BankAccountActionInvalidException { bankAccount.open(); @@ -23,6 +25,7 @@ public void newlyOpenedAccountHasEmptyBalance() throws BankAccountActionInvalidE @Disabled("Remove to run test") @Test + @DisplayName("Single deposit") public void singleDeposit() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(100); @@ -32,6 +35,7 @@ public void singleDeposit() throws BankAccountActionInvalidException { @Disabled("Remove to run test") @Test + @DisplayName("Multiple deposits") public void multipleDeposits() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(100); @@ -42,6 +46,7 @@ public void multipleDeposits() throws BankAccountActionInvalidException { @Disabled("Remove to run test") @Test + @DisplayName("Withdraw once") public void withdrawOnce() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(100); @@ -52,6 +57,7 @@ public void withdrawOnce() throws BankAccountActionInvalidException { @Disabled("Remove to run test") @Test + @DisplayName("Withdraw twice") public void withdrawTwice() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(100); @@ -63,6 +69,7 @@ public void withdrawTwice() throws BankAccountActionInvalidException { @Disabled("Remove to run test") @Test + @DisplayName("Can do multiple operations sequentially") public void canDoMultipleOperationsSequentially() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(100); @@ -76,6 +83,7 @@ public void canDoMultipleOperationsSequentially() throws BankAccountActionInvali @Disabled("Remove to run test") @Test + @DisplayName("Cannot check balance of closed account") public void cannotCheckBalanceOfClosedAccount() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.close(); @@ -87,6 +95,7 @@ public void cannotCheckBalanceOfClosedAccount() throws BankAccountActionInvalidE @Disabled("Remove to run test") @Test + @DisplayName("Cannot deposit into closed account") public void cannotDepositIntoClosedAccount() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.close(); @@ -98,6 +107,7 @@ public void cannotDepositIntoClosedAccount() throws BankAccountActionInvalidExce @Disabled("Remove to run test") @Test + @DisplayName("Cannot deposit into unopened account") public void cannotDepositIntoUnopenedAccount() { assertThatExceptionOfType(BankAccountActionInvalidException.class) .isThrownBy(() -> bankAccount.deposit(50)) @@ -106,6 +116,7 @@ public void cannotDepositIntoUnopenedAccount() { @Disabled("Remove to run test") @Test + @DisplayName("Cannot withdraw from closed account") public void cannotWithdrawFromClosedAccount() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.close(); @@ -117,6 +128,7 @@ public void cannotWithdrawFromClosedAccount() throws BankAccountActionInvalidExc @Disabled("Remove to run test") @Test + @DisplayName("Cannot close an account that was not opened") public void cannotCloseAnAccountThatWasNotOpened() { assertThatExceptionOfType(BankAccountActionInvalidException.class) .isThrownBy(bankAccount::close) @@ -125,6 +137,7 @@ public void cannotCloseAnAccountThatWasNotOpened() { @Disabled("Remove to run test") @Test + @DisplayName("Cannot open an already opened account") public void cannotOpenAnAlreadyOpenedAccount() throws BankAccountActionInvalidException { bankAccount.open(); @@ -135,6 +148,7 @@ public void cannotOpenAnAlreadyOpenedAccount() throws BankAccountActionInvalidEx @Disabled("Remove to run test") @Test + @DisplayName("Reopened account does not retain balance") public void reopenedAccountDoesNotRetainBalance() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(50); @@ -146,7 +160,8 @@ public void reopenedAccountDoesNotRetainBalance() throws BankAccountActionInvali @Disabled("Remove to run test") @Test - public void cannotWithdrawMoreThanWasDeposited() throws BankAccountActionInvalidException { + @DisplayName("Cannot withdraw more than deposited") + public void cannotWithdrawMoreThanDeposited() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(25); @@ -157,6 +172,7 @@ public void cannotWithdrawMoreThanWasDeposited() throws BankAccountActionInvalid @Disabled("Remove to run test") @Test + @DisplayName("Cannot withdraw negative") public void cannotWithdrawNegativeAmount() throws BankAccountActionInvalidException { bankAccount.open(); bankAccount.deposit(100); @@ -168,6 +184,7 @@ public void cannotWithdrawNegativeAmount() throws BankAccountActionInvalidExcept @Disabled("Remove to run test") @Test + @DisplayName("Cannot deposit negative") public void cannotDepositNegativeAmount() throws BankAccountActionInvalidException { bankAccount.open(); @@ -178,6 +195,7 @@ public void cannotDepositNegativeAmount() throws BankAccountActionInvalidExcepti @Disabled("Remove to run test") @Test + @DisplayName("Can handle concurrent transactions") public void canHandleConcurrentTransactions() throws BankAccountActionInvalidException, InterruptedException { bankAccount.open(); bankAccount.deposit(1000); diff --git a/exercises/practice/binary-search-tree/src/test/java/BinarySearchTreeTest.java b/exercises/practice/binary-search-tree/src/test/java/BinarySearchTreeTest.java index 1d16df93c..f6d8f1478 100644 --- a/exercises/practice/binary-search-tree/src/test/java/BinarySearchTreeTest.java +++ b/exercises/practice/binary-search-tree/src/test/java/BinarySearchTreeTest.java @@ -4,11 +4,13 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class BinarySearchTreeTest { @Test + @DisplayName("data is retained") public void dataIsRetained() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); @@ -24,6 +26,7 @@ public void dataIsRetained() { @Disabled("Remove to run test") @Test + @DisplayName("insert data at proper node") public void insertsLess() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); @@ -45,6 +48,7 @@ public void insertsLess() { @Disabled("Remove to run test") @Test + @DisplayName("same number at left node") public void insertsSame() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); String expectedRoot = "4"; @@ -65,6 +69,7 @@ public void insertsSame() { @Disabled("Remove to run test") @Test + @DisplayName("greater number at right node") public void insertsRight() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); int expectedRoot = 4; @@ -85,6 +90,7 @@ public void insertsRight() { @Disabled("Remove to run test") @Test + @DisplayName("can create complex tree") public void createsComplexTree() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); List expected = List.of('4', '2', '6', '1', '3', '5', '7'); @@ -97,6 +103,7 @@ public void createsComplexTree() { @Disabled("Remove to run test") @Test + @DisplayName("can sort single number") public void sortsSingleElement() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); List expected = Collections.singletonList("2"); @@ -108,6 +115,7 @@ public void sortsSingleElement() { @Disabled("Remove to run test") @Test + @DisplayName("can sort if second number is smaller than first") public void sortsCollectionOfTwoIfSecondInsertedIsSmallerThanFirst() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); List expected = List.of(1, 2); @@ -120,6 +128,7 @@ public void sortsCollectionOfTwoIfSecondInsertedIsSmallerThanFirst() { @Disabled("Remove to run test") @Test + @DisplayName("can sort if second number is same as first") public void sortsCollectionOfTwoIfSecondNumberisSameAsFirst() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); List expected = List.of('2', '2'); @@ -132,6 +141,7 @@ public void sortsCollectionOfTwoIfSecondNumberisSameAsFirst() { @Disabled("Remove to run test") @Test + @DisplayName("can sort if second number is greater than first") public void sortsCollectionOfTwoIfSecondInsertedIsBiggerThanFirst() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); List expected = List.of('2', '3'); @@ -144,6 +154,7 @@ public void sortsCollectionOfTwoIfSecondInsertedIsBiggerThanFirst() { @Disabled("Remove to run test") @Test + @DisplayName("can sort complex tree") public void iteratesOverComplexTree() { BinarySearchTree binarySearchTree = new BinarySearchTree<>(); List expected = List.of("1", "2", "3", "5", "6", "7"); diff --git a/exercises/practice/binary-search/src/test/java/BinarySearchTest.java b/exercises/practice/binary-search/src/test/java/BinarySearchTest.java index f586f744c..4846489c9 100644 --- a/exercises/practice/binary-search/src/test/java/BinarySearchTest.java +++ b/exercises/practice/binary-search/src/test/java/BinarySearchTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Collections; @@ -10,6 +11,7 @@ public class BinarySearchTest { @Test + @DisplayName("finds a value in an array with one element") public void findsAValueInAnArrayWithOneElement() throws ValueNotFoundException { List listOfUnitLength = Collections.singletonList(6); @@ -20,6 +22,7 @@ public void findsAValueInAnArrayWithOneElement() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("finds a value in the middle of an array") public void findsAValueInTheMiddleOfAnArray() throws ValueNotFoundException { List sortedList = List.of(1, 3, 4, 6, 8, 9, 11); @@ -30,6 +33,7 @@ public void findsAValueInTheMiddleOfAnArray() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("finds a value at the beginning of an array") public void findsAValueAtTheBeginningOfAnArray() throws ValueNotFoundException { List sortedList = List.of(1, 3, 4, 6, 8, 9, 11); @@ -40,6 +44,7 @@ public void findsAValueAtTheBeginningOfAnArray() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("finds a value at the end of an array") public void findsAValueAtTheEndOfAnArray() throws ValueNotFoundException { List sortedList = List.of(1, 3, 4, 6, 8, 9, 11); @@ -50,6 +55,7 @@ public void findsAValueAtTheEndOfAnArray() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("finds a value in an array of odd length") public void findsAValueInAnArrayOfOddLength() throws ValueNotFoundException { List sortedListOfOddLength = List.of(1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634); @@ -60,6 +66,7 @@ public void findsAValueInAnArrayOfOddLength() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("finds a value in an array of even length") public void findsAValueInAnArrayOfEvenLength() throws ValueNotFoundException { List sortedListOfEvenLength = List.of(1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377); @@ -70,6 +77,7 @@ public void findsAValueInAnArrayOfEvenLength() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("identifies that a value is not included in the array") public void identifiesThatAValueIsNotFoundInTheArray() { List sortedList = List.of(1, 3, 4, 6, 8, 9, 11); @@ -82,6 +90,7 @@ public void identifiesThatAValueIsNotFoundInTheArray() { @Disabled("Remove to run test") @Test + @DisplayName("a value smaller than the array's smallest value is not found") public void aValueSmallerThanTheArraysSmallestValueIsNotFound() { List sortedList = List.of(1, 3, 4, 6, 8, 9, 11); @@ -94,6 +103,7 @@ public void aValueSmallerThanTheArraysSmallestValueIsNotFound() { @Disabled("Remove to run test") @Test + @DisplayName("a value larger than the array's largest value is not found") public void aValueLargerThanTheArraysLargestValueIsNotFound() throws ValueNotFoundException { List sortedList = List.of(1, 3, 4, 6, 8, 9, 11); @@ -106,6 +116,7 @@ public void aValueLargerThanTheArraysLargestValueIsNotFound() throws ValueNotFou @Disabled("Remove to run test") @Test + @DisplayName("nothing is found in an empty array") public void nothingIsFoundInAnEmptyArray() throws ValueNotFoundException { List emptyList = Collections.emptyList(); @@ -118,6 +129,7 @@ public void nothingIsFoundInAnEmptyArray() throws ValueNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("nothing is found when the left and right bounds cross") public void nothingIsFoundWhenTheLeftAndRightBoundCross() throws ValueNotFoundException { List sortedList = List.of(1, 2); diff --git a/exercises/practice/bob/src/test/java/BobTest.java b/exercises/practice/bob/src/test/java/BobTest.java index 801315b42..756fb93f8 100644 --- a/exercises/practice/bob/src/test/java/BobTest.java +++ b/exercises/practice/bob/src/test/java/BobTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.BeforeEach; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ public void setUp() { } @Test + @DisplayName("stating something") public void saySomething() { assertThat(bob.hey("Tom-ay-to, tom-aaaah-to.")) .isEqualTo("Whatever."); @@ -21,6 +23,7 @@ public void saySomething() { @Disabled("Remove to run test") @Test + @DisplayName("shouting") public void shouting() { assertThat(bob.hey("WATCH OUT!")) .isEqualTo("Whoa, chill out!"); @@ -28,6 +31,7 @@ public void shouting() { @Disabled("Remove to run test") @Test + @DisplayName("shouting gibberish") public void shoutingGibberish() { assertThat(bob.hey("FCECDFCAAB")) .isEqualTo("Whoa, chill out!"); @@ -35,6 +39,7 @@ public void shoutingGibberish() { @Disabled("Remove to run test") @Test + @DisplayName("asking a question") public void askingAQuestion() { assertThat(bob.hey("Does this cryogenic chamber make me look fat?")) .isEqualTo("Sure."); @@ -42,6 +47,7 @@ public void askingAQuestion() { @Disabled("Remove to run test") @Test + @DisplayName("asking a numeric question") public void askingANumericQuestion() { assertThat(bob.hey("You are, what, like 15?")) .isEqualTo("Sure."); @@ -49,6 +55,7 @@ public void askingANumericQuestion() { @Disabled("Remove to run test") @Test + @DisplayName("asking gibberish") public void askingGibberish() { assertThat(bob.hey("fffbbcbeab?")) .isEqualTo("Sure."); @@ -56,6 +63,7 @@ public void askingGibberish() { @Disabled("Remove to run test") @Test + @DisplayName("talking forcefully") public void talkingForcefully() { assertThat(bob.hey("Hi there!")) .isEqualTo("Whatever."); @@ -63,6 +71,7 @@ public void talkingForcefully() { @Disabled("Remove to run test") @Test + @DisplayName("using acronyms in regular speech") public void usingAcronymsInRegularSpeech() { assertThat(bob.hey("It's OK if you don't want to go work for NASA.")) .isEqualTo("Whatever."); @@ -70,6 +79,7 @@ public void usingAcronymsInRegularSpeech() { @Disabled("Remove to run test") @Test + @DisplayName("forceful question") public void forcefulQuestions() { assertThat(bob.hey("WHAT'S GOING ON?")) .isEqualTo("Calm down, I know what I'm doing!"); @@ -77,6 +87,7 @@ public void forcefulQuestions() { @Disabled("Remove to run test") @Test + @DisplayName("shouting numbers") public void shoutingNumbers() { assertThat(bob.hey("1, 2, 3 GO!")) .isEqualTo("Whoa, chill out!"); @@ -84,6 +95,7 @@ public void shoutingNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("no letters") public void onlyNumbers() { assertThat(bob.hey("1, 2, 3")) .isEqualTo("Whatever."); @@ -91,6 +103,7 @@ public void onlyNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("question with no letters") public void questionWithOnlyNumbers() { assertThat(bob.hey("4?")) .isEqualTo("Sure."); @@ -98,6 +111,7 @@ public void questionWithOnlyNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("shouting with special characters") public void shoutingWithSpecialCharacters() { assertThat(bob.hey("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!")) .isEqualTo("Whoa, chill out!"); @@ -105,6 +119,7 @@ public void shoutingWithSpecialCharacters() { @Disabled("Remove to run test") @Test + @DisplayName("shouting with no exclamation mark") public void shoutingWithNoExclamationMark() { assertThat(bob.hey("I HATE THE DENTIST")) .isEqualTo("Whoa, chill out!"); @@ -112,6 +127,7 @@ public void shoutingWithNoExclamationMark() { @Disabled("Remove to run test") @Test + @DisplayName("statement containing question mark") public void statementContainingQuestionMark() { assertThat(bob.hey("Ending with ? means a question.")) .isEqualTo("Whatever."); @@ -119,6 +135,7 @@ public void statementContainingQuestionMark() { @Disabled("Remove to run test") @Test + @DisplayName("non-letters with question") public void nonLettersWithQuestion() { assertThat(bob.hey(":) ?")) .isEqualTo("Sure."); @@ -126,6 +143,7 @@ public void nonLettersWithQuestion() { @Disabled("Remove to run test") @Test + @DisplayName("prattling on") public void prattlingOn() { assertThat(bob.hey("Wait! Hang on. Are you going to be OK?")) .isEqualTo("Sure."); @@ -133,6 +151,7 @@ public void prattlingOn() { @Disabled("Remove to run test") @Test + @DisplayName("silence") public void silence() { assertThat(bob.hey("")) .isEqualTo("Fine. Be that way!"); @@ -140,6 +159,7 @@ public void silence() { @Disabled("Remove to run test") @Test + @DisplayName("prolonged silence") public void prolongedSilence() { assertThat(bob.hey(" ")) .isEqualTo("Fine. Be that way!"); @@ -147,6 +167,7 @@ public void prolongedSilence() { @Disabled("Remove to run test") @Test + @DisplayName("alternate silence") public void alternateSilence() { assertThat(bob.hey("\t\t\t\t\t\t\t\t\t\t")) .isEqualTo("Fine. Be that way!"); @@ -154,6 +175,7 @@ public void alternateSilence() { @Disabled("Remove to run test") @Test + @DisplayName("starting with whitespace") public void startingWithWhitespace() { assertThat(bob.hey(" hmmmmmmm...")) .isEqualTo("Whatever."); @@ -161,6 +183,7 @@ public void startingWithWhitespace() { @Disabled("Remove to run test") @Test + @DisplayName("ending with whitespace") public void endingWithWhiteSpace() { assertThat(bob.hey("Okay if like my spacebar quite a bit? ")) .isEqualTo("Sure."); @@ -168,6 +191,7 @@ public void endingWithWhiteSpace() { @Disabled("Remove to run test") @Test + @DisplayName("other whitespace") public void otherWhiteSpace() { assertThat(bob.hey("\n\r \t")) .isEqualTo("Fine. Be that way!"); @@ -175,6 +199,7 @@ public void otherWhiteSpace() { @Disabled("Remove to run test") @Test + @DisplayName("non-question ending with whitespace") public void nonQuestionEndingWithWhiteSpace() { assertThat(bob.hey("This is a statement ending with whitespace ")) .isEqualTo("Whatever."); @@ -182,6 +207,7 @@ public void nonQuestionEndingWithWhiteSpace() { @Disabled("Remove to run test") @Test + @DisplayName("multiple line question") public void multipleLineQuestion() { assertThat(bob.hey("\nDoes this cryogenic chamber make\n me look fat?")) .isEqualTo("Sure."); diff --git a/exercises/practice/book-store/src/test/java/BookStoreTest.java b/exercises/practice/book-store/src/test/java/BookStoreTest.java index 920b83658..bbcbbcfb9 100644 --- a/exercises/practice/book-store/src/test/java/BookStoreTest.java +++ b/exercises/practice/book-store/src/test/java/BookStoreTest.java @@ -1,6 +1,7 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -23,6 +24,7 @@ public void setUp() { } @Test + @DisplayName("Only a single book") public void onlyASingleBook() { List books = Collections.singletonList(1); assertThat(bookStore.calculateBasketCost(books)) @@ -31,6 +33,7 @@ public void onlyASingleBook() { @Disabled("Remove to run test") @Test + @DisplayName("Two of the same book") public void twoOfSameBook() { List books = Arrays.asList(2, 2); assertThat(bookStore.calculateBasketCost(books)) @@ -39,6 +42,7 @@ public void twoOfSameBook() { @Disabled("Remove to run test") @Test + @DisplayName("Empty basket") public void emptyBasket() { List books = Collections.emptyList(); assertThat(bookStore.calculateBasketCost(books)) @@ -47,6 +51,7 @@ public void emptyBasket() { @Disabled("Remove to run test") @Test + @DisplayName("Two different books") public void twoDifferentBooks() { List books = Arrays.asList(1, 2); assertThat(bookStore.calculateBasketCost(books)) @@ -55,6 +60,7 @@ public void twoDifferentBooks() { @Disabled("Remove to run test") @Test + @DisplayName("Three different books") public void threeDifferentBooks() { List books = Arrays.asList(1, 2, 3); assertThat(bookStore.calculateBasketCost(books)) @@ -63,6 +69,7 @@ public void threeDifferentBooks() { @Disabled("Remove to run test") @Test + @DisplayName("Four different books") public void fourDifferentBooks() { List books = Arrays.asList(1, 2, 3, 4); assertThat(bookStore.calculateBasketCost(books)) @@ -71,6 +78,7 @@ public void fourDifferentBooks() { @Disabled("Remove to run test") @Test + @DisplayName("Five different books") public void fiveDifferentBooks() { List books = Arrays.asList(1, 2, 3, 4, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -79,6 +87,7 @@ public void fiveDifferentBooks() { @Disabled("Remove to run test") @Test + @DisplayName("Two groups of four is cheaper than group of five plus group of three") public void twoGroupsOfFourIsCheaperThanGroupOfFivePlusGroupOfThree() { List books = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -87,6 +96,7 @@ public void twoGroupsOfFourIsCheaperThanGroupOfFivePlusGroupOfThree() { @Disabled("Remove to run test") @Test + @DisplayName("Two groups of four is cheaper than groups of five and three") public void twoGroupsOfFourIsCheaperThanGroupsOfFiveAndThree() { List books = Arrays.asList(1, 1, 2, 3, 4, 4, 5, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -95,6 +105,7 @@ public void twoGroupsOfFourIsCheaperThanGroupsOfFiveAndThree() { @Disabled("Remove to run test") @Test + @DisplayName("Group of four plus group of two is cheaper than two groups of three") public void groupOfFourPlusGroupOfTwoIsCheaperThanTwoGroupsOfThree() { List books = Arrays.asList(1, 1, 2, 2, 3, 4); assertThat(bookStore.calculateBasketCost(books)) @@ -103,6 +114,7 @@ public void groupOfFourPlusGroupOfTwoIsCheaperThanTwoGroupsOfThree() { @Disabled("Remove to run test") @Test + @DisplayName("Two each of first four books and one copy each of rest") public void twoEachOfFirst4BooksAnd1CopyEachOfRest() { List books = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -111,6 +123,7 @@ public void twoEachOfFirst4BooksAnd1CopyEachOfRest() { @Disabled("Remove to run test") @Test + @DisplayName("Two copies of each book") public void twoCopiesOfEachBook() { List books = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -119,7 +132,8 @@ public void twoCopiesOfEachBook() { @Disabled("Remove to run test") @Test - public void threeCopiesOfFirstBookAnd2EachOfRemaining() { + @DisplayName("Three copies of first book and two each of remaining") + public void threeCopiesOfFirstBookAndTwoEachOfRemaining() { List books = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1); assertThat(bookStore.calculateBasketCost(books)) .isCloseTo(68.00, Assertions.offset(EQUALITY_TOLERANCE)); @@ -127,7 +141,8 @@ public void threeCopiesOfFirstBookAnd2EachOfRemaining() { @Disabled("Remove to run test") @Test - public void threeEachOFirst2BooksAnd2EachOfRemainingBooks() { + @DisplayName("Three each of first two books and two each of remaining books") + public void threeEachOfFirstTwoBooksAndTwoEachOfRemainingBooks() { List books = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 1, 2); assertThat(bookStore.calculateBasketCost(books)) .isCloseTo(75.20, Assertions.offset(EQUALITY_TOLERANCE)); @@ -135,6 +150,7 @@ public void threeEachOFirst2BooksAnd2EachOfRemainingBooks() { @Disabled("Remove to run test") @Test + @DisplayName("Four groups of four are cheaper than two groups each of five and three") public void fourGroupsOfFourAreCheaperThanTwoGroupsEachOfFiveAndThree() { List books = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 5, 1, 1, 2, 2, 3, 3, 4, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -143,6 +159,9 @@ public void fourGroupsOfFourAreCheaperThanTwoGroupsEachOfFiveAndThree() { @Disabled("Remove to run test") @Test + @DisplayName( + "Check that groups of four are created properly even when there are more groups of three than groups of five" + ) public void groupsOfFourAreCreatedEvenWhenThereAreMoreGroupsOfThreeThanGroupsOfFive() { List books = Arrays.asList(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5); assertThat(bookStore.calculateBasketCost(books)) @@ -151,6 +170,7 @@ public void groupsOfFourAreCreatedEvenWhenThereAreMoreGroupsOfThreeThanGroupsOfF @Disabled("Remove to run test") @Test + @DisplayName("One group of one and four is cheaper than one group of two and three") public void oneGroupOfOneAndFourIsCheaperThanOneGroupOfTwoAndThree() { List books = Arrays.asList(1, 1, 2, 3, 4); assertThat(bookStore.calculateBasketCost(books)) @@ -159,6 +179,7 @@ public void oneGroupOfOneAndFourIsCheaperThanOneGroupOfTwoAndThree() { @Disabled("Remove to run test") @Test + @DisplayName("One group of one and two plus three groups of four is cheaper than one group of each size") public void oneGroupOfOneAndTwoPlusThreeGroupsOfFourIsCheaperThanOneGroupOfEachSize() { List books = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5); assertThat(bookStore.calculateBasketCost(books)) From eb51842f51b63fe7b60b1d15e28658f58adf80a9 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Sat, 30 Aug 2025 22:36:51 +0530 Subject: [PATCH 49/67] Done with adding @DisplayName to bottle-song, bowling, change (#2992) * bottle-song, bowling, change * Update BowlingGameTest.java * Update exercises/practice/bottle-song/src/test/java/BottleSongTest.java Co-authored-by: Jagdish Prajapati [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../src/test/java/BottleSongTest.java | 12 +++++-- .../src/test/java/BowlingGameTest.java | 34 +++++++++++++++++++ .../src/test/java/ChangeCalculatorTest.java | 16 ++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/exercises/practice/bottle-song/src/test/java/BottleSongTest.java b/exercises/practice/bottle-song/src/test/java/BottleSongTest.java index a47a7edc8..7d5ce0a97 100644 --- a/exercises/practice/bottle-song/src/test/java/BottleSongTest.java +++ b/exercises/practice/bottle-song/src/test/java/BottleSongTest.java @@ -1,6 +1,7 @@ -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ public void setup() { } @Test + @DisplayName("first generic verse") public void firstGenericVerse() { assertThat(bottleSong.recite(10, 1)).isEqualTo( "Ten green bottles hanging on the wall,\n" + @@ -25,6 +27,7 @@ public void firstGenericVerse() { @Disabled("Remove to run test") @Test + @DisplayName("last generic verse") public void lastGenericVerse() { assertThat(bottleSong.recite(3, 1)).isEqualTo( "Three green bottles hanging on the wall,\n" + @@ -36,6 +39,7 @@ public void lastGenericVerse() { @Disabled("Remove to run test") @Test + @DisplayName("verse with 2 bottles") public void verseWithTwoBottles() { assertThat(bottleSong.recite(2, 1)).isEqualTo( "Two green bottles hanging on the wall,\n" + @@ -47,6 +51,7 @@ public void verseWithTwoBottles() { @Disabled("Remove to run test") @Test + @DisplayName("verse with 1 bottle") public void verseWithOneBottle() { assertThat(bottleSong.recite(1, 1)).isEqualTo( "One green bottle hanging on the wall,\n" + @@ -58,6 +63,7 @@ public void verseWithOneBottle() { @Disabled("Remove to run test") @Test + @DisplayName("first two verses") public void firstTwoVerses() { assertThat(bottleSong.recite(10, 2)).isEqualTo( "Ten green bottles hanging on the wall,\n" + @@ -74,6 +80,7 @@ public void firstTwoVerses() { @Disabled("Remove to run test") @Test + @DisplayName("last three verses") public void lastThreeVerses() { assertThat(bottleSong.recite(3, 3)).isEqualTo( "Three green bottles hanging on the wall,\n" + @@ -95,6 +102,7 @@ public void lastThreeVerses() { @Disabled("Remove to run test") @Test + @DisplayName("all verses") public void allVerses() { assertThat(bottleSong.recite(10, 10)) .isEqualTo( diff --git a/exercises/practice/bowling/src/test/java/BowlingGameTest.java b/exercises/practice/bowling/src/test/java/BowlingGameTest.java index 7b63a4fc1..a8fbcad96 100644 --- a/exercises/practice/bowling/src/test/java/BowlingGameTest.java +++ b/exercises/practice/bowling/src/test/java/BowlingGameTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ private void playGame(int[] rolls) { } @Test + @DisplayName("should be able to score a game with all zeros") public void shouldBeAbleToScoreAGameWithAllZeros() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -23,6 +25,7 @@ public void shouldBeAbleToScoreAGameWithAllZeros() { @Disabled("Remove to run test") @Test + @DisplayName("should be able to score a game with no strikes or spares") public void shouldBeAbleToScoreAGameWithNoStrikesOrSpares() { int[] rolls = {3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6}; @@ -32,6 +35,7 @@ public void shouldBeAbleToScoreAGameWithNoStrikesOrSpares() { @Disabled("Remove to run test") @Test + @DisplayName("a spare followed by zeros is worth ten points") public void aSpareFollowedByZerosIsWorthTenPoints() { int[] rolls = {6, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -41,6 +45,7 @@ public void aSpareFollowedByZerosIsWorthTenPoints() { @Disabled("Remove to run test") @Test + @DisplayName("points scored in the roll after a spare are counted twice") public void pointsScoredInTheRollAfterASpareAreCountedTwice() { int[] rolls = {6, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -50,6 +55,7 @@ public void pointsScoredInTheRollAfterASpareAreCountedTwice() { @Disabled("Remove to run test") @Test + @DisplayName("consecutive spares each get a one roll bonus") public void consecutiveSparesEachGetAOneRollBonus() { int[] rolls = {5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -59,6 +65,7 @@ public void consecutiveSparesEachGetAOneRollBonus() { @Disabled("Remove to run test") @Test + @DisplayName("a spare in the last frame gets a one roll bonus that is counted once") public void aSpareInTheLastFrameGetsAOneRollBonusThatIsCountedOnce() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7}; @@ -68,6 +75,7 @@ public void aSpareInTheLastFrameGetsAOneRollBonusThatIsCountedOnce() { @Disabled("Remove to run test") @Test + @DisplayName("a strike earns ten points in a frame with a single roll") public void aStrikeEarnsTenPointsInFrameWithASingleRoll() { int[] rolls = {10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -77,6 +85,7 @@ public void aStrikeEarnsTenPointsInFrameWithASingleRoll() { @Disabled("Remove to run test") @Test + @DisplayName("points scored in the two rolls after a strike are counted twice as a bonus") public void pointsScoredInTheTwoRollsAfterAStrikeAreCountedTwiceAsABonus() { int[] rolls = {10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -86,6 +95,7 @@ public void pointsScoredInTheTwoRollsAfterAStrikeAreCountedTwiceAsABonus() { @Disabled("Remove to run test") @Test + @DisplayName("consecutive strikes each get the two roll bonus") public void consecutiveStrikesEachGetTheTwoRollBonus() { int[] rolls = {10, 10, 10, 5, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -95,6 +105,7 @@ public void consecutiveStrikesEachGetTheTwoRollBonus() { @Disabled("Remove to run test") @Test + @DisplayName("a strike in the last frame gets a two roll bonus that is counted once") public void aStrikeInTheLastFrameGetsATwoRollBonusThatIsCountedOnce() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 1}; @@ -104,6 +115,7 @@ public void aStrikeInTheLastFrameGetsATwoRollBonusThatIsCountedOnce() { @Disabled("Remove to run test") @Test + @DisplayName("rolling a spare with the two roll bonus does not get a bonus roll") public void rollingASpareWithTheTwoRollBonusDoesNotGetABonusRoll() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 7, 3}; @@ -113,6 +125,7 @@ public void rollingASpareWithTheTwoRollBonusDoesNotGetABonusRoll() { @Disabled("Remove to run test") @Test + @DisplayName("strikes with the two roll bonus do not get bonus rolls") public void strikesWithTheTwoRollBonusDoNotGetBonusRolls() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10}; @@ -122,6 +135,7 @@ public void strikesWithTheTwoRollBonusDoNotGetBonusRolls() { @Disabled("Remove to run test") @Test + @DisplayName("last two strikes followed by only last bonus with non strike points") public void lastTwoStrikesFollowedByOnlyLastBonusWithNonStrikePoints() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 0, 1}; @@ -131,6 +145,7 @@ public void lastTwoStrikesFollowedByOnlyLastBonusWithNonStrikePoints() { @Disabled("Remove to run test") @Test + @DisplayName("a strike with the one roll bonus after a spare in the last frame does not get a bonus") public void aStrikeWithTheOneRollBonusAfterASpareInTheLastFrameDoesNotGetABonus() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 10}; @@ -140,6 +155,7 @@ public void aStrikeWithTheOneRollBonusAfterASpareInTheLastFrameDoesNotGetABonus( @Disabled("Remove to run test") @Test + @DisplayName("all strikes is a perfect game") public void allStrikesIsAPerfectGame() { int[] rolls = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}; @@ -149,6 +165,7 @@ public void allStrikesIsAPerfectGame() { @Disabled("Remove to run test") @Test + @DisplayName("rolls cannot score negative points") public void rollsCanNotScoreNegativePoints() { int[] rolls = {-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -159,6 +176,7 @@ public void rollsCanNotScoreNegativePoints() { @Disabled("Remove to run test") @Test + @DisplayName("a roll cannot score more than 10 points") public void aRollCanNotScoreMoreThan10Points() { int[] rolls = {11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -169,6 +187,7 @@ public void aRollCanNotScoreMoreThan10Points() { @Disabled("Remove to run test") @Test + @DisplayName("two rolls in a frame cannot score more than 10 points") public void twoRollsInAFrameCanNotScoreMoreThan10Points() { int[] rolls = {5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -179,6 +198,7 @@ public void twoRollsInAFrameCanNotScoreMoreThan10Points() { @Disabled("Remove to run test") @Test + @DisplayName("bonus roll after a strike in the last frame cannot score more than 10 points") public void bonusRollAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 0}; @@ -189,6 +209,7 @@ public void bonusRollAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points() { @Disabled("Remove to run test") @Test + @DisplayName("two bonus rolls after a strike in the last frame cannot score more than 10 points") public void twoBonusRollsAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 5, 6}; @@ -199,6 +220,7 @@ public void twoBonusRollsAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points() @Disabled("Remove to run test") @Test + @DisplayName("two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike") public void twoBonusRollsAfterAStrikeInTheLastFrameCanScoreMoreThan10PointsIfOneIsAStrike() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 6}; @@ -209,6 +231,9 @@ public void twoBonusRollsAfterAStrikeInTheLastFrameCanScoreMoreThan10PointsIfOne @Disabled("Remove to run test") @Test + @DisplayName( + "the second bonus rolls after a strike in the last frame cannot be a strike if the first one is not a strike" + ) public void theSecondBonusRollsAfterAStrikeInTheLastFrameCanNotBeAStrikeIfTheFirstOneIsNotAStrike() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 6, 10}; @@ -219,6 +244,7 @@ public void theSecondBonusRollsAfterAStrikeInTheLastFrameCanNotBeAStrikeIfTheFir @Disabled("Remove to run test") @Test + @DisplayName("second bonus roll after a strike in the last frame cannot score more than 10 points") public void secondBonusRollAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 11}; @@ -229,6 +255,7 @@ public void secondBonusRollAfterAStrikeInTheLastFrameCanNotScoreMoreThan10Points @Disabled("Remove to run test") @Test + @DisplayName("an unstarted game cannot be scored") public void anUnstartedGameCanNotBeScored() { int[] rolls = new int[0]; @@ -241,6 +268,7 @@ public void anUnstartedGameCanNotBeScored() { @Disabled("Remove to run test") @Test + @DisplayName("an incomplete game cannot be scored") public void anIncompleteGameCanNotBeScored() { int[] rolls = {0, 0}; @@ -253,6 +281,7 @@ public void anIncompleteGameCanNotBeScored() { @Disabled("Remove to run test") @Test + @DisplayName("cannot roll if game already has ten frames") public void canNotRollIfGameAlreadyHasTenFrames() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -263,6 +292,7 @@ public void canNotRollIfGameAlreadyHasTenFrames() { @Disabled("Remove to run test") @Test + @DisplayName("bonus rolls for a strike in the last frame must be rolled before score can be calculated") public void bonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10}; @@ -275,6 +305,7 @@ public void bonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalcul @Disabled("Remove to run test") @Test + @DisplayName("both bonus rolls for a strike in the last frame must be rolled before score can be calculated") public void bothBonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10}; @@ -287,6 +318,7 @@ public void bothBonusRollsForAStrikeInTheLastFrameMustBeRolledBeforeScoreCanBeCa @Disabled("Remove to run test") @Test + @DisplayName("bonus roll for a spare in the last frame must be rolled before score can be calculated") public void bonusRollForASpareInTheLastFrameMustBeRolledBeforeScoreCanBeCalculated() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3}; @@ -299,6 +331,7 @@ public void bonusRollForASpareInTheLastFrameMustBeRolledBeforeScoreCanBeCalculat @Disabled("Remove to run test") @Test + @DisplayName("cannot roll after bonus roll for spare") public void canNotRollAfterBonusRollForSpare() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 2, 2}; @@ -309,6 +342,7 @@ public void canNotRollAfterBonusRollForSpare() { @Disabled("Remove to run test") @Test + @DisplayName("cannot roll after bonus rolls for strike") public void canNotRollAfterBonusRollForStrike() { int[] rolls = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 3, 2, 2}; diff --git a/exercises/practice/change/src/test/java/ChangeCalculatorTest.java b/exercises/practice/change/src/test/java/ChangeCalculatorTest.java index e685c28d0..b88c316d3 100644 --- a/exercises/practice/change/src/test/java/ChangeCalculatorTest.java +++ b/exercises/practice/change/src/test/java/ChangeCalculatorTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; @@ -8,7 +9,8 @@ public class ChangeCalculatorTest { @Test - public void testChangeFor1Cent() { + @DisplayName("change for 1 cent") + public void testChangeForOneCent() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25)); assertThat(changeCalculator.computeMostEfficientChange(1)) @@ -17,6 +19,7 @@ public void testChangeFor1Cent() { @Disabled("Remove to run test") @Test + @DisplayName("single coin change") public void testChangeThatCanBeGivenInASingleCoin() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25, 100)); @@ -26,6 +29,7 @@ public void testChangeThatCanBeGivenInASingleCoin() { @Disabled("Remove to run test") @Test + @DisplayName("multiple coin change") public void testChangeThatMustBeGivenInMultipleCoins() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25, 100)); @@ -35,6 +39,7 @@ public void testChangeThatMustBeGivenInMultipleCoins() { @Disabled("Remove to run test") @Test + @DisplayName("change with Lilliputian Coins") public void testLilliputianCurrency() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 4, 15, 20, 50)); @@ -44,6 +49,7 @@ public void testLilliputianCurrency() { @Disabled("Remove to run test") @Test + @DisplayName("change with Lower Elbonia Coins") public void testLowerElbonianCurrency() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 21, 25)); @@ -53,6 +59,7 @@ public void testLowerElbonianCurrency() { @Disabled("Remove to run test") @Test + @DisplayName("large target values") public void testLargeAmountOfChange() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5, 10, 20, 50, 100)); @@ -62,6 +69,7 @@ public void testLargeAmountOfChange() { @Disabled("Remove to run test") @Test + @DisplayName("possible change without unit coins available") public void testPossibleChangeWithoutUnitCoinAvailable() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(2, 5, 10, 20, 50)); @@ -71,6 +79,7 @@ public void testPossibleChangeWithoutUnitCoinAvailable() { @Disabled("Remove to run test") @Test + @DisplayName("another possible change without unit coins available") public void testAnotherPossibleChangeWithoutUnitCoinAvailable() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(4, 5)); @@ -80,6 +89,7 @@ public void testAnotherPossibleChangeWithoutUnitCoinAvailable() { @Disabled("Remove to run test") @Test + @DisplayName("a greedy approach is not optimal") public void testAGreedyApproachIsNotOptimal() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 10, 11)); @@ -89,6 +99,7 @@ public void testAGreedyApproachIsNotOptimal() { @Disabled("Remove to run test") @Test + @DisplayName("no coins make 0 change") public void testZeroChange() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 21, 25)); @@ -98,6 +109,7 @@ public void testZeroChange() { @Disabled("Remove to run test") @Test + @DisplayName("error testing for change smaller than the smallest of coins") public void testChangeLessThanSmallestCoinInCurrencyCannotBeRepresented() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(5, 10)); @@ -108,6 +120,7 @@ public void testChangeLessThanSmallestCoinInCurrencyCannotBeRepresented() { @Disabled("Remove to run test") @Test + @DisplayName("error if no combination can add up to target") public void testChangeLargerThanAllCoinsInCurrencyThatCannotBeRepresented() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(5, 10)); @@ -118,6 +131,7 @@ public void testChangeLargerThanAllCoinsInCurrencyThatCannotBeRepresented() { @Disabled("Remove to run test") @Test + @DisplayName("cannot find negative change values") public void testNegativeChangeIsRejected() { ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5)); From c4997ff19b1929a780fd2c4c11021b202a2beac4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 20:14:29 +0530 Subject: [PATCH 50/67] Bump actions/setup-java from 4.7.1 to 5.0.0 (#2995) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4.7.1 to 5.0.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/c5195efecf7bdfc987ee8bae7a71cb8b11521c00...dded0888837ed1f317902acf8a20df0ad188d165) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/java.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index b1efb49ed..5bf09732e 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: java-version: 17 distribution: "temurin" @@ -31,7 +31,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up JDK 1.17 - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: java-version: 17 distribution: "temurin" From 2dcd9363b5f459ebf74397ec861a992cc2738ec1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 20:15:21 +0530 Subject: [PATCH 51/67] Bump actions/checkout from 4.2.2 to 5.0.0 (#2994) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/11bd71901bbe5b1630ceea73d27597364c9af683...08c6903cd8c0fde910a37f88322edcfb5dd907a8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jagdish Prajapati --- .github/workflows/java.yml | 6 +++--- .github/workflows/markdown.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 5bf09732e..48fc8795a 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -15,7 +15,7 @@ jobs: name: Check if tests compile cleanly with starter sources runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Set up JDK 1.17 uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: @@ -29,7 +29,7 @@ jobs: name: Lint Java files using Checkstyle runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Set up JDK 1.17 uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: @@ -43,7 +43,7 @@ jobs: name: Test all exercises using java-test-runner runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Test all exercises using java-test-runner run: bin/test-with-test-runner - name: Archive test results diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index cb23011e0..81e5bbe77 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -17,6 +17,6 @@ jobs: name: Lint Markdown files runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 - name: Lint markdown uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e From 5ecbd3b6f8f0a9033922b3911ffa0f027c75a293 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Wed, 3 Sep 2025 16:26:59 +0530 Subject: [PATCH 52/67] circular-buffer, clock, collatz-conjecture (#2993) Co-authored-by: Jagdish Prajapati [no important files changed] --- .../src/test/java/CircularBufferTest.java | 15 +++++++++++++ .../clock/src/test/java/ClockAddTest.java | 17 +++++++++++++++ .../src/test/java/ClockCreationTest.java | 21 +++++++++++++++++++ .../clock/src/test/java/ClockEqualTest.java | 17 +++++++++++++++ .../src/test/java/CollatzCalculatorTest.java | 7 +++++++ 5 files changed, 77 insertions(+) diff --git a/exercises/practice/circular-buffer/src/test/java/CircularBufferTest.java b/exercises/practice/circular-buffer/src/test/java/CircularBufferTest.java index 1596b3535..6ecbc1930 100644 --- a/exercises/practice/circular-buffer/src/test/java/CircularBufferTest.java +++ b/exercises/practice/circular-buffer/src/test/java/CircularBufferTest.java @@ -2,11 +2,13 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class CircularBufferTest { @Test + @DisplayName("reading empty buffer should fail") public void readingFromEmptyBufferShouldThrowException() { CircularBuffer buffer = new CircularBuffer<>(1); @@ -17,6 +19,7 @@ public void readingFromEmptyBufferShouldThrowException() { @Disabled("Remove to run test") @Test + @DisplayName("can read an item just written") public void canReadItemJustWritten() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -26,6 +29,7 @@ public void canReadItemJustWritten() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("each item may only be read once") public void canReadItemOnlyOnce() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -39,6 +43,7 @@ public void canReadItemOnlyOnce() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("items are read in the order they are written") public void readsItemsInOrderWritten() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(2); @@ -50,6 +55,7 @@ public void readsItemsInOrderWritten() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("full buffer can't be written to") public void fullBufferCantBeWrittenTo() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -62,6 +68,7 @@ public void fullBufferCantBeWrittenTo() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("a read frees up capacity for another write") public void readFreesUpSpaceForWrite() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -73,6 +80,7 @@ public void readFreesUpSpaceForWrite() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("read position is maintained even across multiple writes") public void maintainsReadPositionAcrossWrites() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(3); @@ -86,6 +94,7 @@ public void maintainsReadPositionAcrossWrites() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("items cleared out of buffer can't be read") public void cantReadClearedItems() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -99,6 +108,7 @@ public void cantReadClearedItems() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("clear frees up capacity for another write") public void clearFreesUpCapacity() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -110,6 +120,7 @@ public void clearFreesUpCapacity() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("clear does nothing on empty buffer") public void clearDoesNothingOnEmptyBuffer() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(1); @@ -120,6 +131,7 @@ public void clearDoesNothingOnEmptyBuffer() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("overwrite acts like write on non-full buffer") public void overwriteActsLikeWriteOnNonFullBuffer() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(2); @@ -131,6 +143,7 @@ public void overwriteActsLikeWriteOnNonFullBuffer() throws BufferIOException { @Disabled("Remove to run test") @Test + @DisplayName("overwrite replaces the oldest item on full buffer") public void overwriteRemovesOldestElementOnFullBuffer() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(2); @@ -143,6 +156,7 @@ public void overwriteRemovesOldestElementOnFullBuffer() throws BufferIOException @Disabled("Remove to run test") @Test + @DisplayName("overwrite replaces the oldest item remaining in buffer following a read") public void overwriteDoesntRemoveAnAlreadyReadElement() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(3); @@ -159,6 +173,7 @@ public void overwriteDoesntRemoveAnAlreadyReadElement() throws BufferIOException @Disabled("Remove to run test") @Test + @DisplayName("initial clear does not affect wrapping around") public void initialClearDoesNotAffectWrappingAround() throws BufferIOException { CircularBuffer buffer = new CircularBuffer<>(2); diff --git a/exercises/practice/clock/src/test/java/ClockAddTest.java b/exercises/practice/clock/src/test/java/ClockAddTest.java index 34c22e06b..1a82b687e 100644 --- a/exercises/practice/clock/src/test/java/ClockAddTest.java +++ b/exercises/practice/clock/src/test/java/ClockAddTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -7,6 +8,7 @@ public class ClockAddTest { @Disabled("Remove to run test") @Test + @DisplayName("add minutes") public void addMinutes() { Clock clock = new Clock(10, 0); clock.add(3); @@ -16,6 +18,7 @@ public void addMinutes() { @Disabled("Remove to run test") @Test + @DisplayName("add no minutes") public void addNoMinutes() { Clock clock = new Clock(6, 41); clock.add(0); @@ -25,6 +28,7 @@ public void addNoMinutes() { @Disabled("Remove to run test") @Test + @DisplayName("add to next hour") public void addToNextHour() { Clock clock = new Clock(0, 45); clock.add(40); @@ -34,6 +38,7 @@ public void addToNextHour() { @Disabled("Remove to run test") @Test + @DisplayName("add more than one hour") public void addMoreThanOneHour() { Clock clock = new Clock(10, 0); clock.add(61); @@ -43,6 +48,7 @@ public void addMoreThanOneHour() { @Disabled("Remove to run test") @Test + @DisplayName("add more than two hours with carry") public void addMoreThanTwoHoursWithCarry() { Clock clock = new Clock(0, 45); clock.add(160); @@ -52,6 +58,7 @@ public void addMoreThanTwoHoursWithCarry() { @Disabled("Remove to run test") @Test + @DisplayName("add across midnight") public void addAcrossMidnight() { Clock clock = new Clock(23, 59); clock.add(2); @@ -61,6 +68,7 @@ public void addAcrossMidnight() { @Disabled("Remove to run test") @Test + @DisplayName("add more than one day (1500 min = 25 hrs)") public void addMoreThanOneDay() { Clock clock = new Clock(5, 32); clock.add(1500); @@ -70,6 +78,7 @@ public void addMoreThanOneDay() { @Disabled("Remove to run test") @Test + @DisplayName("add more than two days") public void addMoreThanTwoDays() { Clock clock = new Clock(1, 1); clock.add(3500); @@ -79,6 +88,7 @@ public void addMoreThanTwoDays() { @Disabled("Remove to run test") @Test + @DisplayName("subtract minutes") public void subtractMinutes() { Clock clock = new Clock(10, 3); clock.add(-3); @@ -88,6 +98,7 @@ public void subtractMinutes() { @Disabled("Remove to run test") @Test + @DisplayName("subtract to previous hour") public void subtractToPreviousHour() { Clock clock = new Clock(10, 3); clock.add(-30); @@ -97,6 +108,7 @@ public void subtractToPreviousHour() { @Disabled("Remove to run test") @Test + @DisplayName("subtract more than an hour") public void subtractMoreThanAnHour() { Clock clock = new Clock(10, 3); clock.add(-70); @@ -106,6 +118,7 @@ public void subtractMoreThanAnHour() { @Disabled("Remove to run test") @Test + @DisplayName("subtract across midnight") public void subtractAcrossMidnight() { Clock clock = new Clock(0, 3); clock.add(-4); @@ -115,6 +128,7 @@ public void subtractAcrossMidnight() { @Disabled("Remove to run test") @Test + @DisplayName("subtract more than two hours") public void subtractMoreThanTwoHours() { Clock clock = new Clock(0, 0); clock.add(-160); @@ -124,6 +138,7 @@ public void subtractMoreThanTwoHours() { @Disabled("Remove to run test") @Test + @DisplayName("subtract more than two hours with borrow") public void subtractMoreThanTwoHoursWithBorrow() { Clock clock = new Clock(6, 15); clock.add(-160); @@ -133,6 +148,7 @@ public void subtractMoreThanTwoHoursWithBorrow() { @Disabled("Remove to run test") @Test + @DisplayName("subtract more than one day (1500 min = 25 hrs)") public void subtractMoreThanOneDay() { Clock clock = new Clock(5, 32); clock.add(-1500); @@ -142,6 +158,7 @@ public void subtractMoreThanOneDay() { @Disabled("Remove to run test") @Test + @DisplayName("subtract more than two days") public void subtractMoreThanTwoDays() { Clock clock = new Clock(2, 20); clock.add(-3000); diff --git a/exercises/practice/clock/src/test/java/ClockCreationTest.java b/exercises/practice/clock/src/test/java/ClockCreationTest.java index 84f68d60d..674d1fee7 100644 --- a/exercises/practice/clock/src/test/java/ClockCreationTest.java +++ b/exercises/practice/clock/src/test/java/ClockCreationTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -6,120 +7,140 @@ public class ClockCreationTest { @Test + @DisplayName("on the hour") public void canPrintTimeOnTheHour() { assertThat(new Clock(8, 0).toString()).isEqualTo("08:00"); } @Disabled("Remove to run test") @Test + @DisplayName("past the hour") public void canPrintTimeWithMinutes() { assertThat(new Clock(11, 9).toString()).isEqualTo("11:09"); } @Disabled("Remove to run test") @Test + @DisplayName("midnight is zero hours") public void midnightPrintsAsZero() { assertThat(new Clock(24, 0).toString()).isEqualTo("00:00"); } @Disabled("Remove to run test") @Test + @DisplayName("hour rolls over") public void hourRollsOver() { assertThat(new Clock(25, 0).toString()).isEqualTo("01:00"); } @Disabled("Remove to run test") @Test + @DisplayName("hour rolls over continuously") public void hourRollsOverContinuously() { assertThat(new Clock(100, 0).toString()).isEqualTo("04:00"); } @Disabled("Remove to run test") @Test + @DisplayName("sixty minutes is next hour") public void sixtyMinutesIsNextHour() { assertThat(new Clock(1, 60).toString()).isEqualTo("02:00"); } @Disabled("Remove to run test") @Test + @DisplayName("minutes roll over") public void minutesRollOver() { assertThat(new Clock(0, 160).toString()).isEqualTo("02:40"); } @Disabled("Remove to run test") @Test + @DisplayName("minutes roll over continuously") public void minutesRollOverContinuously() { assertThat(new Clock(0, 1723).toString()).isEqualTo("04:43"); } @Disabled("Remove to run test") @Test + @DisplayName("hour and minutes roll over") public void hourAndMinutesRollOver() { assertThat(new Clock(25, 160).toString()).isEqualTo("03:40"); } @Disabled("Remove to run test") @Test + @DisplayName("hour and minutes roll over continuously") public void hourAndMinutesRollOverContinuously() { assertThat(new Clock(201, 3001).toString()).isEqualTo("11:01"); } @Disabled("Remove to run test") @Test + @DisplayName("hour and minutes roll over to exactly midnight") public void hourAndMinutesRollOverToExactlyMidnight() { assertThat(new Clock(72, 8640).toString()).isEqualTo("00:00"); } @Disabled("Remove to run test") @Test + @DisplayName("negative hour") public void negativeHour() { assertThat(new Clock(-1, 15).toString()).isEqualTo("23:15"); } @Disabled("Remove to run test") @Test + @DisplayName("negative hour rolls over") public void negativeHourRollsOver() { assertThat(new Clock(-25, 0).toString()).isEqualTo("23:00"); } @Disabled("Remove to run test") @Test + @DisplayName("negative hour rolls over continuously") public void negativeHourRollsOverContinuously() { assertThat(new Clock(-91, 0).toString()).isEqualTo("05:00"); } @Disabled("Remove to run test") @Test + @DisplayName("negative minutes") public void negativeMinutes() { assertThat(new Clock(1, -40).toString()).isEqualTo("00:20"); } @Disabled("Remove to run test") @Test + @DisplayName("negative minutes roll over") public void negativeMinutesRollOver() { assertThat(new Clock(1, -160).toString()).isEqualTo("22:20"); } @Disabled("Remove to run test") @Test + @DisplayName("negative minutes roll over continuously") public void negativeMinutesRollOverContinuously() { assertThat(new Clock(1, -4820).toString()).isEqualTo("16:40"); } @Disabled("Remove to run test") @Test + @DisplayName("negative sixty minutes is previous hour") public void negativeSixtyMinutesIsPreviousHour() { assertThat(new Clock(2, -60).toString()).isEqualTo("01:00"); } @Disabled("Remove to run test") @Test + @DisplayName("negative hour and minutes both roll over") public void negativeHourAndMinutesBothRollOver() { assertThat(new Clock(-25, -160).toString()).isEqualTo("20:20"); } @Disabled("Remove to run test") @Test + @DisplayName("negative hour and minutes both roll over continuously") public void negativeHourAndMinutesBothRollOverContinuously() { assertThat(new Clock(-121, -5810).toString()).isEqualTo("22:10"); } diff --git a/exercises/practice/clock/src/test/java/ClockEqualTest.java b/exercises/practice/clock/src/test/java/ClockEqualTest.java index 0ec041f71..d73687773 100644 --- a/exercises/practice/clock/src/test/java/ClockEqualTest.java +++ b/exercises/practice/clock/src/test/java/ClockEqualTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -7,6 +8,7 @@ public class ClockEqualTest { @Disabled("Remove to run test") @Test + @DisplayName("clocks with same time") public void clocksWithSameTimeAreEqual() { assertThat(new Clock(15, 37)) .isEqualTo(new Clock(15, 37)); @@ -14,6 +16,7 @@ public void clocksWithSameTimeAreEqual() { @Disabled("Remove to run test") @Test + @DisplayName("clocks a minute apart") public void clocksAMinuteApartAreNotEqual() { assertThat(new Clock(15, 36)) .isNotEqualTo(new Clock(15, 37)); @@ -21,6 +24,7 @@ public void clocksAMinuteApartAreNotEqual() { @Disabled("Remove to run test") @Test + @DisplayName("clocks an hour apart") public void clocksAnHourApartAreNotEqual() { assertThat(new Clock(14, 37)) .isNotEqualTo(new Clock(15, 37)); @@ -28,6 +32,7 @@ public void clocksAnHourApartAreNotEqual() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with hour overflow") public void clocksWithHourOverflow() { assertThat(new Clock(34, 37)) .isEqualTo(new Clock(10, 37)); @@ -35,6 +40,7 @@ public void clocksWithHourOverflow() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with hour overflow by several days") public void clocksWithHourOverflowBySeveralDays() { assertThat(new Clock(99, 11)) .isEqualTo(new Clock(3, 11)); @@ -42,6 +48,7 @@ public void clocksWithHourOverflowBySeveralDays() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative hour") public void clocksWithNegateHour() { assertThat(new Clock(-2, 40)) .isEqualTo(new Clock(22, 40)); @@ -49,6 +56,7 @@ public void clocksWithNegateHour() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative hour that wraps") public void clocksWithNegativeHourThatWraps() { assertThat(new Clock(-31, 3)) .isEqualTo(new Clock(17, 3)); @@ -56,6 +64,7 @@ public void clocksWithNegativeHourThatWraps() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative hour that wraps multiple times") public void clocksWithNegativeHourThatWrapsMultipleTimes() { assertThat(new Clock(-83, 49)) .isEqualTo(new Clock(13, 49)); @@ -63,6 +72,7 @@ public void clocksWithNegativeHourThatWrapsMultipleTimes() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with minute overflow") public void clocksWithMinuteOverflow() { assertThat(new Clock(0, 1441)) .isEqualTo(new Clock(0, 1)); @@ -70,6 +80,7 @@ public void clocksWithMinuteOverflow() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with minute overflow by several days") public void clocksWithMinuteOverflowBySeveralDays() { assertThat(new Clock(2, 4322)) .isEqualTo(new Clock(2, 2)); @@ -77,6 +88,7 @@ public void clocksWithMinuteOverflowBySeveralDays() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative minute") public void clocksWithNegativeMinutes() { assertThat(new Clock(3, -20)) .isEqualTo(new Clock(2, 40)); @@ -84,6 +96,7 @@ public void clocksWithNegativeMinutes() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative minute that wraps") public void clocksWithNegativeMinutesThatWraps() { assertThat(new Clock(5, -1490)) .isEqualTo(new Clock(4, 10)); @@ -91,6 +104,7 @@ public void clocksWithNegativeMinutesThatWraps() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative minute that wraps multiple times") public void clocksWithNegativeMinutesThatWrapsMultipleTimes() { assertThat(new Clock(6, -4305)) .isEqualTo(new Clock(6, 15)); @@ -98,6 +112,7 @@ public void clocksWithNegativeMinutesThatWrapsMultipleTimes() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative hours and minutes") public void clocksWithNegativeHoursAndMinutes() { assertThat(new Clock(-12, -268)) .isEqualTo(new Clock(7, 32)); @@ -105,6 +120,7 @@ public void clocksWithNegativeHoursAndMinutes() { @Disabled("Remove to run test") @Test + @DisplayName("clocks with negative hours and minutes that wrap") public void clocksWithNegativeHoursAndMinutesThatWrap() { assertThat(new Clock(-54, -11513)) .isEqualTo(new Clock(18, 7)); @@ -112,6 +128,7 @@ public void clocksWithNegativeHoursAndMinutesThatWrap() { @Disabled("Remove to run test") @Test + @DisplayName("full clock and zeroed clock") public void clocksWithFullClockAndZeroedClockAreEqual() { assertThat(new Clock(24, 0)) .isEqualTo(new Clock(0, 0)); diff --git a/exercises/practice/collatz-conjecture/src/test/java/CollatzCalculatorTest.java b/exercises/practice/collatz-conjecture/src/test/java/CollatzCalculatorTest.java index 708d608dc..c095ffdf6 100644 --- a/exercises/practice/collatz-conjecture/src/test/java/CollatzCalculatorTest.java +++ b/exercises/practice/collatz-conjecture/src/test/java/CollatzCalculatorTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -9,30 +10,35 @@ public class CollatzCalculatorTest { private CollatzCalculator collatzCalculator = new CollatzCalculator(); @Test + @DisplayName("zero steps for one") public void testZeroStepsRequiredWhenStartingFrom1() { assertThat(collatzCalculator.computeStepCount(1)).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("divide if even") public void testCorrectNumberOfStepsWhenAllStepsAreDivisions() { assertThat(collatzCalculator.computeStepCount(16)).isEqualTo(4); } @Disabled("Remove to run test") @Test + @DisplayName("even and odd steps") public void testCorrectNumberOfStepsWhenBothStepTypesAreNeeded() { assertThat(collatzCalculator.computeStepCount(12)).isEqualTo(9); } @Disabled("Remove to run test") @Test + @DisplayName("large number of even and odd steps") public void testAVeryLargeInput() { assertThat(collatzCalculator.computeStepCount(1000000)).isEqualTo(152); } @Disabled("Remove to run test") @Test + @DisplayName("zero is an error") public void testZeroIsConsideredInvalidInput() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> collatzCalculator.computeStepCount(0)) @@ -41,6 +47,7 @@ public void testZeroIsConsideredInvalidInput() { @Disabled("Remove to run test") @Test + @DisplayName("negative value is an error") public void testNegativeIntegerIsConsideredInvalidInput() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> collatzCalculator.computeStepCount(-15)) From 0246421241c310c3120d1b459108a2dd303edf3c Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Wed, 3 Sep 2025 16:35:54 +0530 Subject: [PATCH 53/67] complex-numbers, connect, crypto-square (#2996) Co-authored-by: Jagdish Prajapati [no important files changed] --- .../src/test/java/ComplexNumberTest.java | 32 +++++++++++++++++++ .../connect/src/test/java/ConnectTest.java | 11 +++++++ .../src/test/java/CryptoSquareTest.java | 9 ++++++ 3 files changed, 52 insertions(+) diff --git a/exercises/practice/complex-numbers/src/test/java/ComplexNumberTest.java b/exercises/practice/complex-numbers/src/test/java/ComplexNumberTest.java index 1d479ea68..d9edc378a 100644 --- a/exercises/practice/complex-numbers/src/test/java/ComplexNumberTest.java +++ b/exercises/practice/complex-numbers/src/test/java/ComplexNumberTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -24,6 +25,7 @@ private void assertComplexNumbersEqual(ComplexNumber c1, ComplexNumber c2) { // Tests @Test + @DisplayName("Real part of a purely real number") public void testRealPartOfPurelyRealNumber() { double expected = 1.0; double actual = new ComplexNumber(1.0, 0).getReal(); @@ -32,6 +34,7 @@ public void testRealPartOfPurelyRealNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Real part of a purely imaginary number") public void testRealPartOfPurelyImaginaryNumber() { double expected = 0.0; double actual = new ComplexNumber(0, 1.0).getReal(); @@ -40,6 +43,7 @@ public void testRealPartOfPurelyImaginaryNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Real part of a number with real and imaginary part") public void testRealPartOfNumberWithRealAndImaginaryParts() { double expected = 1.0; double actual = new ComplexNumber(1.0, 2.0).getReal(); @@ -48,6 +52,7 @@ public void testRealPartOfNumberWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Imaginary part of a purely real number") public void testImaginaryPartOfPurelyRealNumber() { double expected = 0.0; double actual = new ComplexNumber(1.0, 0).getImaginary(); @@ -56,6 +61,7 @@ public void testImaginaryPartOfPurelyRealNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Imaginary part of a purely imaginary number") public void testImaginaryPartOfPurelyImaginaryNumber() { double expected = 1.0; double actual = new ComplexNumber(0, 1.0).getImaginary(); @@ -64,6 +70,7 @@ public void testImaginaryPartOfPurelyImaginaryNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Imaginary part of a number with real and imaginary part") public void testImaginaryPartOfNumberWithRealAndImaginaryParts() { double expected = 2.0; double actual = new ComplexNumber(1.0, 2.0).getImaginary(); @@ -72,6 +79,7 @@ public void testImaginaryPartOfNumberWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Imaginary unit") public void testImaginaryUnitExhibitsDefiningProperty() { ComplexNumber expected = new ComplexNumber(-1.0, 0); ComplexNumber actual = new ComplexNumber(0, 1.0).multiply(new ComplexNumber(0, 1.0)); @@ -80,6 +88,7 @@ public void testImaginaryUnitExhibitsDefiningProperty() { @Disabled("Remove to run test") @Test + @DisplayName("Add purely real numbers") public void testAdditionWithPurelyRealNumbers() { ComplexNumber expected = new ComplexNumber(3.0, 0); ComplexNumber actual = new ComplexNumber(1.0, 0).add(new ComplexNumber(2.0, 0)); @@ -88,6 +97,7 @@ public void testAdditionWithPurelyRealNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Add purely imaginary numbers") public void testAdditionWithPurelyImaginaryNumbers() { ComplexNumber expected = new ComplexNumber(0, 3.0); ComplexNumber actual = new ComplexNumber(0, 1.0).add(new ComplexNumber(0, 2.0)); @@ -96,6 +106,7 @@ public void testAdditionWithPurelyImaginaryNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Add numbers with real and imaginary part") public void testAdditionWithRealAndImaginaryParts() { ComplexNumber expected = new ComplexNumber(4.0, 6.0); ComplexNumber actual = new ComplexNumber(1.0, 2.0).add(new ComplexNumber(3.0, 4.0)); @@ -104,6 +115,7 @@ public void testAdditionWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract purely real numbers") public void testSubtractionWithPurelyRealNumbers() { ComplexNumber expected = new ComplexNumber(-1.0, 0.0); ComplexNumber actual = new ComplexNumber(1.0, 0.0).subtract(new ComplexNumber(2.0, 0.0)); @@ -112,6 +124,7 @@ public void testSubtractionWithPurelyRealNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract purely imaginary numbers") public void testSubtractionWithPurelyImaginaryNumbers() { ComplexNumber expected = new ComplexNumber(0, -1.0); ComplexNumber actual = new ComplexNumber(0, 1.0).subtract(new ComplexNumber(0, 2.0)); @@ -120,6 +133,7 @@ public void testSubtractionWithPurelyImaginaryNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract numbers with real and imaginary part") public void testSubtractionWithRealAndImaginaryParts() { ComplexNumber expected = new ComplexNumber(-2.0, -2.0); ComplexNumber actual = new ComplexNumber(1.0, 2.0).subtract(new ComplexNumber(3.0, 4.0)); @@ -128,6 +142,7 @@ public void testSubtractionWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply purely real numbers") public void testMultiplicationWithPurelyRealNumbers() { ComplexNumber expected = new ComplexNumber(2.0, 0); ComplexNumber actual = new ComplexNumber(1.0, 0).multiply(new ComplexNumber(2.0, 0)); @@ -136,6 +151,7 @@ public void testMultiplicationWithPurelyRealNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply purely imaginary numbers") public void testMultiplicationWithPurelyImaginaryNumbers() { ComplexNumber expected = new ComplexNumber(-2.0, 0); ComplexNumber actual = new ComplexNumber(0, 1.0).multiply(new ComplexNumber(0, 2.0)); @@ -144,6 +160,7 @@ public void testMultiplicationWithPurelyImaginaryNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply numbers with real and imaginary part") public void testMultiplicationWithRealAndImaginaryParts() { ComplexNumber expected = new ComplexNumber(-5.0, 10.0); ComplexNumber actual = new ComplexNumber(1.0, 2.0).multiply(new ComplexNumber(3.0, 4.0)); @@ -152,6 +169,7 @@ public void testMultiplicationWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Divide purely real numbers") public void testDivisionWithPurelyRealNumbers() { ComplexNumber expected = new ComplexNumber(0.5, 0); ComplexNumber actual = new ComplexNumber(1.0, 0).divide(new ComplexNumber(2.0, 0)); @@ -160,6 +178,7 @@ public void testDivisionWithPurelyRealNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Divide purely imaginary numbers") public void testDivisionWithPurelyImaginaryNumbers() { ComplexNumber expected = new ComplexNumber(0.5, 0); ComplexNumber actual = new ComplexNumber(0, 1.0).divide(new ComplexNumber(0, 2.0)); @@ -168,6 +187,7 @@ public void testDivisionWithPurelyImaginaryNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Divide numbers with real and imaginary part") public void testDivisionWithRealAndImaginaryParts() { ComplexNumber expected = new ComplexNumber(0.44, 0.08); ComplexNumber actual = new ComplexNumber(1.0, 2.0).divide(new ComplexNumber(3.0, 4.0)); @@ -176,6 +196,7 @@ public void testDivisionWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a positive purely real number") public void testAbsoluteValueOfPositivePurelyRealNumber() { double expected = 5.0; double actual = new ComplexNumber(5.0, 0).abs(); @@ -184,6 +205,7 @@ public void testAbsoluteValueOfPositivePurelyRealNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a negative purely real number") public void testAbsoluteValueOfNegativePurelyRealNumber() { double expected = 5.0; double actual = new ComplexNumber(-5.0, 0).abs(); @@ -192,6 +214,7 @@ public void testAbsoluteValueOfNegativePurelyRealNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a purely imaginary number with positive imaginary part") public void testAbsoluteValueOfPurelyImaginaryNumberWithPositiveImaginaryPart() { double expected = 5.0; double actual = new ComplexNumber(0, 5.0).abs(); @@ -200,6 +223,7 @@ public void testAbsoluteValueOfPurelyImaginaryNumberWithPositiveImaginaryPart() @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a purely imaginary number with negative imaginary part") public void testAbsoluteValueOfPurelyImaginaryNumberWithNegativeImaginaryPart() { double expected = 5.0; double actual = new ComplexNumber(0, -5.0).abs(); @@ -208,6 +232,7 @@ public void testAbsoluteValueOfPurelyImaginaryNumberWithNegativeImaginaryPart() @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a number with real and imaginary part") public void testAbsoluteValueOfNumberWithRealAndImaginaryParts() { double expected = 5.0; double actual = new ComplexNumber(3.0, 4.0).abs(); @@ -216,6 +241,7 @@ public void testAbsoluteValueOfNumberWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Conjugate a purely real number") public void testConjugationOfPurelyRealNumber() { ComplexNumber expected = new ComplexNumber(5.0, 0); ComplexNumber actual = new ComplexNumber(5.0, 0).conjugate(); @@ -224,6 +250,7 @@ public void testConjugationOfPurelyRealNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Conjugate a purely imaginary number") public void testConjugationOfPurelyImaginaryNumber() { ComplexNumber expected = new ComplexNumber(0, -5.0); ComplexNumber actual = new ComplexNumber(0, 5.0).conjugate(); @@ -232,6 +259,7 @@ public void testConjugationOfPurelyImaginaryNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Conjugate a number with real and imaginary part") public void testConjugationOfNumberWithRealAndImaginaryParts() { ComplexNumber expected = new ComplexNumber(1.0, -1.0); ComplexNumber actual = new ComplexNumber(1.0, 1.0).conjugate(); @@ -240,6 +268,7 @@ public void testConjugationOfNumberWithRealAndImaginaryParts() { @Disabled("Remove to run test") @Test + @DisplayName("Euler's identity/formula") public void testExponentialOfPurelyImaginaryNumber() { ComplexNumber expected = new ComplexNumber(-1.0, 0); ComplexNumber actual = new ComplexNumber(0, Math.PI).exponentialOf(); @@ -248,6 +277,7 @@ public void testExponentialOfPurelyImaginaryNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Exponential of 0") public void testExponentialOfZero() { ComplexNumber expected = new ComplexNumber(1.0, 0); ComplexNumber actual = new ComplexNumber(0, 0).exponentialOf(); @@ -256,6 +286,7 @@ public void testExponentialOfZero() { @Disabled("Remove to run test") @Test + @DisplayName("Exponential of a purely real number") public void testExponentialOfPurelyRealNumber() { ComplexNumber expected = new ComplexNumber(Math.E, 0); ComplexNumber actual = new ComplexNumber(1.0, 0).exponentialOf(); @@ -264,6 +295,7 @@ public void testExponentialOfPurelyRealNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Exponential resulting in a number with real and imaginary part") public void testExponentialOfNumberWithRealAndImaginaryParts() { ComplexNumber expected = new ComplexNumber(1, 1); ComplexNumber actual = new ComplexNumber(Math.log(2.0) / 2, Math.PI / 4).exponentialOf(); diff --git a/exercises/practice/connect/src/test/java/ConnectTest.java b/exercises/practice/connect/src/test/java/ConnectTest.java index e6bfa17c8..0bc3a12a2 100644 --- a/exercises/practice/connect/src/test/java/ConnectTest.java +++ b/exercises/practice/connect/src/test/java/ConnectTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -6,6 +7,7 @@ public class ConnectTest { @Test + @DisplayName("an empty board has no winner") public void anEmptyBoardHasNoWinner() { //GIVEN @@ -27,6 +29,7 @@ public void anEmptyBoardHasNoWinner() { @Disabled("Remove to run test") @Test + @DisplayName("X can win on a 1x1 board") public void xCanWinOnA1x1Board() { //GIVEN @@ -45,6 +48,7 @@ public void xCanWinOnA1x1Board() { @Disabled("Remove to run test") @Test + @DisplayName("O can win on a 1x1 board") public void oCanWinOnA1x1Board() { //GIVEN @@ -63,6 +67,7 @@ public void oCanWinOnA1x1Board() { @Disabled("Remove to run test") @Test + @DisplayName("only edges does not make a winner") public void onlyEdgesDoesNotMakeAWinner() { //GIVEN @@ -84,6 +89,7 @@ public void onlyEdgesDoesNotMakeAWinner() { @Disabled("Remove to run test") @Test + @DisplayName("illegal diagonal does not make a winner") public void illegalDiagonalDoesNotMakeAWinner() { //GIVEN @@ -106,6 +112,7 @@ public void illegalDiagonalDoesNotMakeAWinner() { @Disabled("Remove to run test") @Test + @DisplayName("nobody wins crossing adjacent angles") public void nobodyWinsCrossingAdjacentAngles() { //GIVEN @@ -128,6 +135,7 @@ public void nobodyWinsCrossingAdjacentAngles() { @Disabled("Remove to run test") @Test + @DisplayName("X wins crossing from left to right") public void xWinsCrossingFromLeftToRight() { //GIVEN @@ -150,6 +158,7 @@ public void xWinsCrossingFromLeftToRight() { @Disabled("Remove to run test") @Test + @DisplayName("O wins crossing from top to bottom") public void oWinsCrossingFromTopToBottom() { //GIVEN @@ -172,6 +181,7 @@ public void oWinsCrossingFromTopToBottom() { @Disabled("Remove to run test") @Test + @DisplayName("X wins using a convoluted path") public void xWinsUsingConvolutedPath() { //GIVEN @@ -194,6 +204,7 @@ public void xWinsUsingConvolutedPath() { @Disabled("Remove to run test") @Test + @DisplayName("X wins using a spiral path") public void xWinsUsingASpiralPath() { //GIVEN diff --git a/exercises/practice/crypto-square/src/test/java/CryptoSquareTest.java b/exercises/practice/crypto-square/src/test/java/CryptoSquareTest.java index 59eb4fc70..b9a668a7a 100644 --- a/exercises/practice/crypto-square/src/test/java/CryptoSquareTest.java +++ b/exercises/practice/crypto-square/src/test/java/CryptoSquareTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -6,6 +7,7 @@ public class CryptoSquareTest { @Test + @DisplayName("empty plaintext results in an empty ciphertext") public void emptyPlaintextResultsInEmptyCiphertext() { CryptoSquare cryptoSquare = new CryptoSquare(""); String expectedOutput = ""; @@ -15,6 +17,7 @@ public void emptyPlaintextResultsInEmptyCiphertext() { @Disabled("Remove to run test") @Test + @DisplayName("normalization results in empty plaintext") public void normalizationResultsInEmptyCiphertext() { CryptoSquare cryptoSquare = new CryptoSquare("... --- ..."); String expectedOutput = ""; @@ -24,6 +27,7 @@ public void normalizationResultsInEmptyCiphertext() { @Disabled("Remove to run test") @Test + @DisplayName("Lowercase") public void lettersAreLowerCasedDuringEncryption() { CryptoSquare cryptoSquare = new CryptoSquare("A"); String expectedOutput = "a"; @@ -33,6 +37,7 @@ public void lettersAreLowerCasedDuringEncryption() { @Disabled("Remove to run test") @Test + @DisplayName("Remove spaces") public void spacesAreRemovedDuringEncryption() { CryptoSquare cryptoSquare = new CryptoSquare(" b "); String expectedOutput = "b"; @@ -42,6 +47,7 @@ public void spacesAreRemovedDuringEncryption() { @Disabled("Remove to run test") @Test + @DisplayName("Remove punctuation") public void punctuationIsRemovedDuringEncryption() { CryptoSquare cryptoSquare = new CryptoSquare("@1,%!"); String expectedOutput = "1"; @@ -51,6 +57,7 @@ public void punctuationIsRemovedDuringEncryption() { @Disabled("Remove to run test") @Test + @DisplayName("9 character plaintext results in 3 chunks of 3 characters") public void nineCharacterPlaintextResultsInThreeChunksOfThreeCharacters() { CryptoSquare cryptoSquare = new CryptoSquare("This is fun!"); String expectedOutput = "tsf hiu isn"; @@ -60,6 +67,7 @@ public void nineCharacterPlaintextResultsInThreeChunksOfThreeCharacters() { @Disabled("Remove to run test") @Test + @DisplayName("8 character plaintext results in 3 chunks, the last one with a trailing space") public void eightCharacterPlaintextResultsInThreeChunksWithATrailingSpace() { CryptoSquare cryptoSquare = new CryptoSquare("Chill out."); String expectedOutput = "clu hlt io "; @@ -69,6 +77,7 @@ public void eightCharacterPlaintextResultsInThreeChunksWithATrailingSpace() { @Disabled("Remove to run test") @Test + @DisplayName("54 character plaintext results in 7 chunks, the last two with trailing spaces") public void fiftyFourCharacterPlaintextResultsInSevenChunksWithTrailingSpaces() { CryptoSquare cryptoSquare = new CryptoSquare("If man was meant to stay on the ground, god would have " + "given us roots."); From ba645547b125360d2ad5a8ad0d043ccf5e374c36 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Wed, 3 Sep 2025 16:36:51 +0530 Subject: [PATCH 54/67] dominoes, et1, flatten-array (#2998) Co-authored-by: Jagdish Prajapati [no important files changed] --- .../dominoes/src/test/java/DominoesTest.java | 14 ++++++++++++++ exercises/practice/etl/src/test/java/EtlTest.java | 5 +++++ .../flatten-array/src/test/java/FlattenerTest.java | 12 ++++++++++++ 3 files changed, 31 insertions(+) diff --git a/exercises/practice/dominoes/src/test/java/DominoesTest.java b/exercises/practice/dominoes/src/test/java/DominoesTest.java index 76f2e4931..d1570eb77 100644 --- a/exercises/practice/dominoes/src/test/java/DominoesTest.java +++ b/exercises/practice/dominoes/src/test/java/DominoesTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -12,6 +13,7 @@ public class DominoesTest { @Test + @DisplayName("empty input = empty output") public void emtpyInputEmptyOutputTest() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); @@ -24,6 +26,7 @@ public void emtpyInputEmptyOutputTest() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("singleton input = singleton output") public void singletonInputSingletonOutput() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); @@ -37,6 +40,7 @@ public void singletonInputSingletonOutput() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("singleton that can't be chained") public void singletonCantBeChainedTest() { Dominoes dominoes = new Dominoes(); @@ -50,6 +54,7 @@ public void singletonCantBeChainedTest() { @Disabled("Remove to run test") @Test + @DisplayName("three elements") public void threeElementsTest() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); @@ -63,6 +68,7 @@ public void threeElementsTest() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("can reverse dominoes") public void canReverseDominoesTest() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); @@ -76,6 +82,7 @@ public void canReverseDominoesTest() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("can't be chained") public void cantBeChainedTest() { Dominoes dominoes = new Dominoes(); @@ -89,6 +96,7 @@ public void cantBeChainedTest() { @Disabled("Remove to run test") @Test + @DisplayName("disconnected - simple") public void disconnectedSimpleTest() { Dominoes dominoes = new Dominoes(); @@ -102,6 +110,7 @@ public void disconnectedSimpleTest() { @Disabled("Remove to run test") @Test + @DisplayName("disconnected - double loop") public void disconnectedDoubleLoopTest() { Dominoes dominoes = new Dominoes(); @@ -115,6 +124,7 @@ public void disconnectedDoubleLoopTest() { @Disabled("Remove to run test") @Test + @DisplayName("disconnected - single isolated") public void disconnectedSingleIsolatedTest() { Dominoes dominoes = new Dominoes(); @@ -128,6 +138,7 @@ public void disconnectedSingleIsolatedTest() { @Disabled("Remove to run test") @Test + @DisplayName("need backtrack") public void needBacktrackTest() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); @@ -142,6 +153,7 @@ public void needBacktrackTest() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("separate loops") public void separateLoopsTest() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); @@ -156,6 +168,7 @@ public void separateLoopsTest() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("nine elements") public void nineElementsTest() throws ChainNotFoundException { Dominoes dominoes = new Dominoes(); Domino[] dominoesArray = {new Domino(1, 2), new Domino(5, 3), new Domino(3, 1), @@ -170,6 +183,7 @@ public void nineElementsTest() throws ChainNotFoundException { @Disabled("Remove to run test") @Test + @DisplayName("separate three-domino loops") public void separateThreeDominoLoopsTest() { Dominoes dominoes = new Dominoes(); diff --git a/exercises/practice/etl/src/test/java/EtlTest.java b/exercises/practice/etl/src/test/java/EtlTest.java index c25755690..4ef9292aa 100644 --- a/exercises/practice/etl/src/test/java/EtlTest.java +++ b/exercises/practice/etl/src/test/java/EtlTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -14,6 +15,7 @@ public class EtlTest { private final Etl etl = new Etl(); @Test + @DisplayName("single letter") public void testTransformOneValue() { Map> old = new HashMap>() { { @@ -34,6 +36,7 @@ public void testTransformOneValue() { @Disabled("Remove to run test") @Test + @DisplayName("single score with multiple letters") public void testTransformMoreValues() { Map> old = new HashMap>() { { @@ -58,6 +61,7 @@ public void testTransformMoreValues() { @Disabled("Remove to run test") @Test + @DisplayName("multiple scores with multiple letters") public void testMoreKeys() { Map> old = new HashMap>() { { @@ -82,6 +86,7 @@ public void testMoreKeys() { @Disabled("Remove to run test") @Test + @DisplayName("multiple scores with differing numbers of letters") public void testFullDataset() { Map> old = new HashMap>() { { diff --git a/exercises/practice/flatten-array/src/test/java/FlattenerTest.java b/exercises/practice/flatten-array/src/test/java/FlattenerTest.java index a9f94e280..2a5fb4323 100644 --- a/exercises/practice/flatten-array/src/test/java/FlattenerTest.java +++ b/exercises/practice/flatten-array/src/test/java/FlattenerTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static java.util.Arrays.asList; @@ -17,6 +18,7 @@ public void setUp() { } @Test + @DisplayName("empty") public void testEmpty() { assertThat(flattener.flatten(emptyList())) .isEmpty(); @@ -24,6 +26,7 @@ public void testEmpty() { @Disabled("Remove to run test") @Test + @DisplayName("no nesting") public void testFlatListIsPreserved() { assertThat(flattener.flatten(asList(0, '1', "two"))) .containsExactly(0, '1', "two"); @@ -31,6 +34,7 @@ public void testFlatListIsPreserved() { @Disabled("Remove to run test") @Test + @DisplayName("flattens a nested array") public void testNestedList() { assertThat(flattener.flatten(singletonList(emptyList()))) .isEmpty(); @@ -38,6 +42,7 @@ public void testNestedList() { @Disabled("Remove to run test") @Test + @DisplayName("flattens array with just integers present") public void testASingleLevelOfNestingWithNoNulls() { assertThat(flattener.flatten(asList(1, asList('2', 3, 4, 5, "six", "7"), 8))) .containsExactly(1, '2', 3, 4, 5, "six", "7", 8); @@ -45,6 +50,7 @@ public void testASingleLevelOfNestingWithNoNulls() { @Disabled("Remove to run test") @Test + @DisplayName("5 level nesting") public void testFiveLevelsOfNestingWithNoNulls() { assertThat(flattener.flatten( asList(0, @@ -59,6 +65,7 @@ public void testFiveLevelsOfNestingWithNoNulls() { @Disabled("Remove to run test") @Test + @DisplayName("6 level nesting") public void testSixLevelsOfNestingWithNoNulls() { assertThat(flattener.flatten( asList("one", @@ -71,6 +78,7 @@ public void testSixLevelsOfNestingWithNoNulls() { @Disabled("Remove to run test") @Test + @DisplayName("null values are omitted from the final result") public void testNullValuesAreOmitted() { assertThat(flattener.flatten(asList("1", "two", null))) .containsExactly("1", "two"); @@ -78,6 +86,7 @@ public void testNullValuesAreOmitted() { @Disabled("Remove to run test") @Test + @DisplayName("consecutive null values at the front of the list are omitted from the final result") public void testConsecutiveNullValuesAtFrontOfListAreOmitted() { assertThat(flattener.flatten(asList(null, null, 3))) .containsExactly(3); @@ -85,6 +94,7 @@ public void testConsecutiveNullValuesAtFrontOfListAreOmitted() { @Disabled("Remove to run test") @Test + @DisplayName("consecutive null values in the middle of the list are omitted from the final result") public void testConsecutiveNullValuesInMiddleOfListAreOmitted() { assertThat(flattener.flatten(asList(1, null, null, "4"))) .containsExactly(1, "4"); @@ -92,6 +102,7 @@ public void testConsecutiveNullValuesInMiddleOfListAreOmitted() { @Disabled("Remove to run test") @Test + @DisplayName("6 level nest list with null values") public void testSixLevelsOfNestingWithNulls() { assertThat(flattener.flatten( asList("0", @@ -107,6 +118,7 @@ public void testSixLevelsOfNestingWithNulls() { @Disabled("Remove to run test") @Test + @DisplayName("all values in nested list are null") public void testNestedListsFullOfNullsOnly() { assertThat(flattener.flatten( asList(null, From d869d4590ab6c38bea08075ce499fff16332c369 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Fri, 5 Sep 2025 01:07:42 +0530 Subject: [PATCH 55/67] finalized with adding @DisplayName to custom-set, darts, diamond, difference-of-squares (#2997) * custom-set, darts, diamond, difference-of-squares * Update exercises/practice/diamond/src/test/java/DiamondPrinterTest.java Co-authored-by: Jagdish Prajapati [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../src/test/java/CustomSetTest.java | 41 +++++++++++++++++++ .../darts/src/test/java/DartsTest.java | 14 +++++++ .../src/test/java/DiamondPrinterTest.java | 8 +++- .../DifferenceOfSquaresCalculatorTest.java | 10 +++++ 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/exercises/practice/custom-set/src/test/java/CustomSetTest.java b/exercises/practice/custom-set/src/test/java/CustomSetTest.java index 4f32727c4..1032ecd50 100644 --- a/exercises/practice/custom-set/src/test/java/CustomSetTest.java +++ b/exercises/practice/custom-set/src/test/java/CustomSetTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -9,6 +10,7 @@ public class CustomSetTest { @Test + @DisplayName("Returns true if the set contains no elements") public void setsWithNoElementsAreEmpty() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); assertThat(customSet.isEmpty()).isTrue(); @@ -16,6 +18,7 @@ public void setsWithNoElementsAreEmpty() { @Disabled("Remove to run test") @Test + @DisplayName("sets with elements are not empty") public void setsWithElementsAreNotEmpty() { CustomSet customSet = new CustomSet<>(Collections.singletonList('1')); assertThat(customSet.isEmpty()).isFalse(); @@ -23,6 +26,7 @@ public void setsWithElementsAreNotEmpty() { @Disabled("Remove to run test") @Test + @DisplayName("nothing is contained in an empty set") public void nothingIsContainedInAnEmptySet() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); assertThat(customSet.contains("1")).isFalse(); @@ -30,6 +34,7 @@ public void nothingIsContainedInAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("when the element is in the set") public void whenTheElementIsInTheSet() { CustomSet customSet = new CustomSet<>(Arrays.asList(1, 2, 3)); assertThat(customSet.contains(1)).isTrue(); @@ -37,6 +42,7 @@ public void whenTheElementIsInTheSet() { @Disabled("Remove to run test") @Test + @DisplayName("when the element is not in the set") public void whenTheElementIsNotInTheSet() { CustomSet customSet = new CustomSet<>(Arrays.asList('1', '2', '3')); assertThat(customSet.contains('4')).isFalse(); @@ -44,6 +50,7 @@ public void whenTheElementIsNotInTheSet() { @Disabled("Remove to run test") @Test + @DisplayName("empty set is a subset of another empty set") public void emptySetIsASubsetOfAnotherEmptySet() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); CustomSet secondCustomSet = new CustomSet<>(Collections.emptyList()); @@ -52,6 +59,7 @@ public void emptySetIsASubsetOfAnotherEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("empty set is a subset of non-empty set") public void emptySetIsASubsetOfNonEmptySet() { CustomSet customSet = new CustomSet<>(Collections.singletonList(1)); CustomSet secondCustomSet = new CustomSet<>(Collections.emptyList()); @@ -60,6 +68,7 @@ public void emptySetIsASubsetOfNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("non-empty set is not a subset of empty set") public void nonEmptySetIsNotASubsetOfEmptySet() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); CustomSet secondCustomSet = new CustomSet<>(Collections.singletonList('1')); @@ -68,6 +77,7 @@ public void nonEmptySetIsNotASubsetOfEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("set is a subset of set with exact same elements") public void setIsASubsetOfSetWithExactSameElements() { CustomSet customSet = new CustomSet<>(Arrays.asList("1", "2", "3")); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList("1", "2", "3")); @@ -76,6 +86,7 @@ public void setIsASubsetOfSetWithExactSameElements() { @Disabled("Remove to run test") @Test + @DisplayName("set is a subset of larger set with same elements") public void setIsASubsetOfLargerSetWithSameElements() { CustomSet customSet = new CustomSet<>(Arrays.asList(4, 1, 2, 3)); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList(1, 2, 3)); @@ -84,6 +95,7 @@ public void setIsASubsetOfLargerSetWithSameElements() { @Disabled("Remove to run test") @Test + @DisplayName("set is not a subset of set that does not contain its elements") public void setIsNotASubsetOfSetThatDoesNotContainItsElements() { CustomSet customSet = new CustomSet<>(Arrays.asList('4', '1', '3')); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList('1', '2', '3')); @@ -92,6 +104,7 @@ public void setIsNotASubsetOfSetThatDoesNotContainItsElements() { @Disabled("Remove to run test") @Test + @DisplayName("the empty set is disjoint with itself") public void theEmptySetIsDisjointWithItself() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); CustomSet secondCustomSet = new CustomSet<>(Collections.emptyList()); @@ -100,6 +113,7 @@ public void theEmptySetIsDisjointWithItself() { @Disabled("Remove to run test") @Test + @DisplayName("empty set is disjoint with non-empty set") public void emptySetIsDisjointWithNonEmptySet() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); CustomSet secondCustomSet = new CustomSet<>(Collections.singletonList(1)); @@ -108,6 +122,7 @@ public void emptySetIsDisjointWithNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("non-empty set is disjoint with empty set") public void nonEmptySetIsDisjointWithEmptySet() { CustomSet customSet = new CustomSet<>(Collections.singletonList('1')); CustomSet secondCustomSet = new CustomSet<>(Collections.emptyList()); @@ -116,6 +131,7 @@ public void nonEmptySetIsDisjointWithEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("sets are not disjoint if they share an element") public void setsAreNotDisjointIfTheyShareAnElement() { CustomSet customSet = new CustomSet<>(Arrays.asList("1", "2")); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList("2", "3")); @@ -124,6 +140,7 @@ public void setsAreNotDisjointIfTheyShareAnElement() { @Disabled("Remove to run test") @Test + @DisplayName("sets are disjoint if they share no elements") public void setsAreDisjointIfTheyShareNoElements() { CustomSet customSet = new CustomSet<>(Arrays.asList(1, 2)); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList(3, 4)); @@ -132,6 +149,7 @@ public void setsAreDisjointIfTheyShareNoElements() { @Disabled("Remove to run test") @Test + @DisplayName("empty sets are equal") public void emptySetsAreEqual() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); CustomSet secondCustomSet = new CustomSet<>(Collections.emptyList()); @@ -140,6 +158,7 @@ public void emptySetsAreEqual() { @Disabled("Remove to run test") @Test + @DisplayName("empty set is not equal to non-empty set") public void emptySetIsNotEqualToNonEmptySet() { CustomSet customSet = new CustomSet<>(Collections.emptyList()); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList("1", "2", "3")); @@ -148,6 +167,7 @@ public void emptySetIsNotEqualToNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("non-empty set is not equal to empty set") public void nonEmptySetIsNotEqualToEmptySet() { CustomSet customSet = new CustomSet<>(Arrays.asList(1, 2, 3)); CustomSet secondCustomSet = new CustomSet<>(Collections.emptyList()); @@ -156,6 +176,7 @@ public void nonEmptySetIsNotEqualToEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("sets with the same elements are equal") public void setsWithTheSameElementsAreEqual() { CustomSet customSet = new CustomSet<>(Arrays.asList('1', '2')); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList('2', '1')); @@ -164,6 +185,7 @@ public void setsWithTheSameElementsAreEqual() { @Disabled("Remove to run test") @Test + @DisplayName("sets with different elements are not equal") public void setsWithDifferentElementsAreNotEqual() { CustomSet customSet = new CustomSet<>(Arrays.asList("1", "2", "3")); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList("1", "2", "4")); @@ -172,6 +194,7 @@ public void setsWithDifferentElementsAreNotEqual() { @Disabled("Remove to run test") @Test + @DisplayName("set is not equal to larger set with same elements") public void setIsNotEqualToLargerSetWithSameElements() { CustomSet customSet = new CustomSet<>(Arrays.asList("1", "2", "3")); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList("1", "2", "3", "4")); @@ -180,6 +203,7 @@ public void setIsNotEqualToLargerSetWithSameElements() { @Disabled("Remove to run test") @Test + @DisplayName("set is equal to a set constructed from an array with duplicates") public void secondSetWithDuplicatesIsEqualToFirstSet() { CustomSet customSet = new CustomSet<>(Collections.singletonList("1")); CustomSet secondCustomSet = new CustomSet<>(Arrays.asList("1", "1")); @@ -188,6 +212,7 @@ public void secondSetWithDuplicatesIsEqualToFirstSet() { @Disabled("Remove to run test") @Test + @DisplayName("difference removes all duplicates in the first set") public void firstSetWithDuplicatesIsEqualToSecondSet() { CustomSet customSet = new CustomSet<>(Arrays.asList("1", "1")); CustomSet secondCustomSet = new CustomSet<>(Collections.singletonList("1")); @@ -196,6 +221,7 @@ public void firstSetWithDuplicatesIsEqualToSecondSet() { @Disabled("Remove to run test") @Test + @DisplayName("add to empty set") public void addToEmptySet() { int element = 3; CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Collections.singletonList(element))); @@ -211,6 +237,7 @@ public void addToEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("add to non-empty set") public void addToNonEmptySet() { char element = '3'; CustomSet expected = new CustomSet<>(Collections.unmodifiableList( @@ -226,6 +253,7 @@ public void addToNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("adding an existing element does not change the set") public void addingAnExistingElementDoesNotChangeTheSet() { String element = "3"; CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Arrays.asList("1", "2", "3"))); @@ -239,6 +267,7 @@ public void addingAnExistingElementDoesNotChangeTheSet() { @Disabled("Remove to run test") @Test + @DisplayName("intersection of two empty sets is an empty set") public void intersectionOfTwoEmptySetsIsAnEmptySet() { CustomSet actual = new CustomSet(Collections.emptyList()) .getIntersection(new CustomSet<>(Collections.emptyList())); @@ -249,6 +278,7 @@ public void intersectionOfTwoEmptySetsIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("intersection of an empty set and non-empty set is an empty set") public void intersectionOfAnEmptySetAndNonEmptySetIsAnEmptySet() { CustomSet actual = new CustomSet(Collections.emptyList()) .getIntersection(new CustomSet<>(Arrays.asList('3', '2', '5'))); @@ -259,6 +289,7 @@ public void intersectionOfAnEmptySetAndNonEmptySetIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("intersection of a non-empty set and an empty set is an empty set") public void intersectionOfANonEmptySetAndAnEmptySetIsAnEmptySet() { CustomSet actual = new CustomSet<>(Arrays.asList("1", "2", "3", "4")) .getIntersection(new CustomSet<>(Collections.emptyList())); @@ -270,6 +301,7 @@ public void intersectionOfANonEmptySetAndAnEmptySetIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("intersection of two sets with no shared elements is an empty set") public void intersectionOfTwoSetsWithNoSharedElementsIsAnEmptySet() { CustomSet actual = new CustomSet<>(Arrays.asList(1, 2, 3)) .getIntersection(new CustomSet<>(Arrays.asList(4, 5, 6))); @@ -280,6 +312,7 @@ public void intersectionOfTwoSetsWithNoSharedElementsIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("intersection of two sets with shared elements is a set of the shared elements") public void intersectionOfTwoSetsWithSharedElementsIsASetOfTheSharedElements() { CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Arrays.asList('2', '3'))); CustomSet actual = new CustomSet<>(Arrays.asList('1', '2', '3', '4')) @@ -292,6 +325,7 @@ public void intersectionOfTwoSetsWithSharedElementsIsASetOfTheSharedElements() { @Disabled("Remove to run test") @Test + @DisplayName("difference of two empty sets is an empty set") public void differenceOfTwoEmptySetsIsAnEmptySet() { CustomSet actual = new CustomSet(Collections.emptyList()) .getDifference(new CustomSet<>(Collections.emptyList())); @@ -302,6 +336,7 @@ public void differenceOfTwoEmptySetsIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("difference of empty set and non-empty set is an empty set") public void differenceOfAnEmptySetAndNonEmptySetIsAnEmptySet() { CustomSet actual = new CustomSet(Collections.emptyList()) .getDifference(new CustomSet<>(Arrays.asList(3, 2, 5))); @@ -312,6 +347,7 @@ public void differenceOfAnEmptySetAndNonEmptySetIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("difference of a non-empty set and an empty set is the non-empty set") public void differenceOfANonEmptySetAndAnEmptySetIsTheNonEmptySet() { CustomSet expected = new CustomSet<>(Collections.unmodifiableList( Arrays.asList('1', '2', '3', '4'))); @@ -325,6 +361,7 @@ public void differenceOfANonEmptySetAndAnEmptySetIsTheNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("difference of two non-empty sets is a set of elements that are only in the first set") public void differenceOfTwoNonEmptySetsIsASetOfElementsThatAreOnlyInTheFirstSet() { CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Arrays.asList("1", "3"))); CustomSet actual = new CustomSet<>(Arrays.asList("3", "2", "1")) @@ -338,6 +375,7 @@ public void differenceOfTwoNonEmptySetsIsASetOfElementsThatAreOnlyInTheFirstSet( @Disabled("Remove to run test") @Test + @DisplayName("union of empty sets is an empty set") public void unionOfTwoEmptySetsIsAnEmptySet() { CustomSet actual = new CustomSet(Collections.emptyList()) .getUnion(new CustomSet<>(Collections.emptyList())); @@ -348,6 +386,7 @@ public void unionOfTwoEmptySetsIsAnEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("union of an empty set and non-empty set is the non-empty set") public void unionOfAnEmptySetAndNonEmptySetIsTheNonEmptySet() { CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Collections.singletonList('2'))); CustomSet actual = new CustomSet(Collections.emptyList()) @@ -360,6 +399,7 @@ public void unionOfAnEmptySetAndNonEmptySetIsTheNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("union of a non-empty set and empty set is the non-empty set") public void unionOfANonEmptySetAndAnEmptySetIsTheNonEmptySet() { CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Arrays.asList("1", "3"))); CustomSet actual = new CustomSet<>(Arrays.asList("1", "3")) @@ -372,6 +412,7 @@ public void unionOfANonEmptySetAndAnEmptySetIsTheNonEmptySet() { @Disabled("Remove to run test") @Test + @DisplayName("union of non-empty sets contains all unique elements") public void unionOfTwoNonEmptySetsContainsAllUniqueElements() { CustomSet expected = new CustomSet<>(Collections.unmodifiableList(Arrays.asList(3, 2, 1))); CustomSet actual = new CustomSet<>(Arrays.asList(1, 3)) diff --git a/exercises/practice/darts/src/test/java/DartsTest.java b/exercises/practice/darts/src/test/java/DartsTest.java index f6e10baed..45376bfe4 100644 --- a/exercises/practice/darts/src/test/java/DartsTest.java +++ b/exercises/practice/darts/src/test/java/DartsTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -7,60 +8,70 @@ public class DartsTest { Darts darts = new Darts(); @Test + @DisplayName("Missed target") public void missedTarget() { assertThat(darts.score(-9, 9)).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("On the outer circle") public void onTheOuterCircle() { assertThat(darts.score(0, 10)).isEqualTo(1); } @Disabled("Remove to run test") @Test + @DisplayName("On the middle circle") public void onTheMiddleCircle() { assertThat(darts.score(-5, 0)).isEqualTo(5); } @Disabled("Remove to run test") @Test + @DisplayName("On the inner circle") public void onTheInnerCircle() { assertThat(darts.score(0, -1)).isEqualTo(10); } @Disabled("Remove to run test") @Test + @DisplayName("Exactly on center") public void exactlyOnCentre() { assertThat(darts.score(0, 0)).isEqualTo(10); } @Disabled("Remove to run test") @Test + @DisplayName("Near the center") public void nearTheCentre() { assertThat(darts.score(-0.1, -0.1)).isEqualTo(10); } @Disabled("Remove to run test") @Test + @DisplayName("Just within the inner circle") public void justWithinTheInnerCircle() { assertThat(darts.score(0.7, 0.7)).isEqualTo(10); } @Disabled("Remove to run test") @Test + @DisplayName("Just outside the inner circle") public void justOutsideTheInnerCircle() { assertThat(darts.score(0.8, -0.8)).isEqualTo(5); } @Disabled("Remove to run test") @Test + @DisplayName("Just within the middle circle") public void justWithinTheMiddleCircle() { assertThat(darts.score(-3.5, 3.5)).isEqualTo(5); } @Disabled("Remove to run test") @Test + @DisplayName("Just outside the middle circle") public void justOutsideTheMiddleCircle() { assertThat(darts.score(-3.6, -3.6)).isEqualTo(1); } @@ -68,18 +79,21 @@ public void justOutsideTheMiddleCircle() { @Disabled("Remove to run test") @Test + @DisplayName("Just within the outer circle") public void justWithinTheOuterCircle() { assertThat(darts.score(-7.0, 7.0)).isEqualTo(1); } @Disabled("Remove to run test") @Test + @DisplayName("Just outside the outer circle") public void justOutsideTheOuterCircle() { assertThat(darts.score(7.1, -7.1)).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("Asymmetric position between the inner and middle circles") public void asymmetricPositionBetweenTheInnerAndMiddleCircles() { assertThat(darts.score(0.5, -4)).isEqualTo(5); } diff --git a/exercises/practice/diamond/src/test/java/DiamondPrinterTest.java b/exercises/practice/diamond/src/test/java/DiamondPrinterTest.java index 3388aeefe..6608faeac 100644 --- a/exercises/practice/diamond/src/test/java/DiamondPrinterTest.java +++ b/exercises/practice/diamond/src/test/java/DiamondPrinterTest.java @@ -1,7 +1,8 @@ import static org.assertj.core.api.Assertions.assertThat; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class DiamondPrinterTest { @@ -14,6 +15,7 @@ public void setUp() { } @Test + @DisplayName("Degenerate case with a single 'A' row") public void testOneByOneDiamond() { assertThat(diamondPrinter.printToList('A')) .containsExactly("A"); @@ -21,6 +23,7 @@ public void testOneByOneDiamond() { @Disabled("Remove to run test") @Test + @DisplayName("Degenerate case with no row containing 3 distinct groups of spaces") public void testTwoByTwoDiamond() { assertThat(diamondPrinter.printToList('B')) .containsExactly( @@ -31,6 +34,7 @@ public void testTwoByTwoDiamond() { @Disabled("Remove to run test") @Test + @DisplayName("Smallest non-degenerate case with odd diamond side length") public void testThreeByThreeDiamond() { assertThat(diamondPrinter.printToList('C')) .containsExactly( @@ -43,6 +47,7 @@ public void testThreeByThreeDiamond() { @Disabled("Remove to run test") @Test + @DisplayName("Smallest non-degenerate case with even diamond side length") public void testFourByFourDiamond() { assertThat(diamondPrinter.printToList('D')) .containsExactly( @@ -57,6 +62,7 @@ public void testFourByFourDiamond() { @Disabled("Remove to run test") @Test + @DisplayName("Largest possible diamond") public void testFullDiamond() { assertThat(diamondPrinter.printToList('Z')) .containsExactly( diff --git a/exercises/practice/difference-of-squares/src/test/java/DifferenceOfSquaresCalculatorTest.java b/exercises/practice/difference-of-squares/src/test/java/DifferenceOfSquaresCalculatorTest.java index 3df042fdf..93d3af7a4 100644 --- a/exercises/practice/difference-of-squares/src/test/java/DifferenceOfSquaresCalculatorTest.java +++ b/exercises/practice/difference-of-squares/src/test/java/DifferenceOfSquaresCalculatorTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ public void setUp() { } @Test + @DisplayName("square of sum 1") public void testSquareOfSumUpToOne() { int expected = 1; int actual = calculator.computeSquareOfSumTo(1); @@ -22,6 +24,7 @@ public void testSquareOfSumUpToOne() { @Disabled("Remove to run test") @Test + @DisplayName("square of sum 5") public void testSquareOfSumUpToFive() { int expected = 225; int actual = calculator.computeSquareOfSumTo(5); @@ -30,6 +33,7 @@ public void testSquareOfSumUpToFive() { @Disabled("Remove to run test") @Test + @DisplayName("square of sum 100") public void testSquareOfSumUpToHundred() { int expected = 25502500; int actual = calculator.computeSquareOfSumTo(100); @@ -38,6 +42,7 @@ public void testSquareOfSumUpToHundred() { @Disabled("Remove to run test") @Test + @DisplayName("sum of squares 1") public void testSumOfSquaresUpToOne() { int expected = 1; int actual = calculator.computeSumOfSquaresTo(1); @@ -46,6 +51,7 @@ public void testSumOfSquaresUpToOne() { @Disabled("Remove to run test") @Test + @DisplayName("sum of squares 5") public void testSumOfSquaresUpToFive() { int expected = 55; int actual = calculator.computeSumOfSquaresTo(5); @@ -54,6 +60,7 @@ public void testSumOfSquaresUpToFive() { @Disabled("Remove to run test") @Test + @DisplayName("sum of squares 100") public void testSumOfSquaresUpToHundred() { int expected = 338350; int actual = calculator.computeSumOfSquaresTo(100); @@ -62,6 +69,7 @@ public void testSumOfSquaresUpToHundred() { @Disabled("Remove to run test") @Test + @DisplayName("difference of squares 1") public void testDifferenceOfSquaresUpToOne() { int expected = 0; int actual = calculator.computeDifferenceOfSquares(1); @@ -70,6 +78,7 @@ public void testDifferenceOfSquaresUpToOne() { @Disabled("Remove to run test") @Test + @DisplayName("difference of squares 5") public void testDifferenceOfSquaresUpToFive() { int expected = 170; int actual = calculator.computeDifferenceOfSquares(5); @@ -78,6 +87,7 @@ public void testDifferenceOfSquaresUpToFive() { @Disabled("Remove to run test") @Test + @DisplayName("difference of squares 100") public void testDifferenceOfSquaresUpToHundred() { int expected = 25164150; int actual = calculator.computeDifferenceOfSquares(100); From 52808f1dfbb6b93324096fa81f58a722f62d3f17 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Fri, 5 Sep 2025 01:23:37 +0530 Subject: [PATCH 56/67] flower-field, food-chain, forth (#2999) Co-authored-by: Jagdish Prajapati [no important files changed] --- .../src/test/java/FlowerFieldBoardTest.java | 13 +++++ .../src/test/java/FoodChainTest.java | 11 ++++ .../src/test/java/ForthEvaluatorTest.java | 56 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java b/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java index 518b6344b..3eba013f6 100644 --- a/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java +++ b/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -10,6 +11,7 @@ public class FlowerFieldBoardTest { @Test + @DisplayName("no rows") public void testInputBoardWithNoRowsAndNoColumns() { List inputBoard = Collections.emptyList(); List expectedNumberedBoard = Collections.emptyList(); @@ -20,6 +22,7 @@ public void testInputBoardWithNoRowsAndNoColumns() { @Disabled("Remove to run test") @Test + @DisplayName("no columns") public void testInputBoardWithOneRowAndNoColumns() { List inputBoard = Collections.singletonList(""); List expectedNumberedBoard = Collections.singletonList(""); @@ -30,6 +33,7 @@ public void testInputBoardWithOneRowAndNoColumns() { @Disabled("Remove to run test") @Test + @DisplayName("no flowers") public void testInputBoardWithNoFlowers() { List inputBoard = Arrays.asList( " ", @@ -50,6 +54,7 @@ public void testInputBoardWithNoFlowers() { @Disabled("Remove to run test") @Test + @DisplayName("garden full of flowers") public void testInputBoardWithOnlyFlowers() { List inputBoard = Arrays.asList( "***", @@ -70,6 +75,7 @@ public void testInputBoardWithOnlyFlowers() { @Disabled("Remove to run test") @Test + @DisplayName("flower surrounded by spaces") public void testInputBoardWithSingleFlowerAtCenter() { List inputBoard = Arrays.asList( " ", @@ -90,6 +96,7 @@ public void testInputBoardWithSingleFlowerAtCenter() { @Disabled("Remove to run test") @Test + @DisplayName("space surrounded by flowers") public void testInputBoardWithFlowersAroundPerimeter() { List inputBoard = Arrays.asList( "***", @@ -110,6 +117,7 @@ public void testInputBoardWithFlowersAroundPerimeter() { @Disabled("Remove to run test") @Test + @DisplayName("horizontal line") public void testInputBoardWithSingleRowAndTwoFlowers() { List inputBoard = Collections.singletonList( " * * " @@ -126,6 +134,7 @@ public void testInputBoardWithSingleRowAndTwoFlowers() { @Disabled("Remove to run test") @Test + @DisplayName("horizontal line, flowers at edges") public void testInputBoardWithSingleRowAndTwoFlowersAtEdges() { List inputBoard = Collections.singletonList( "* *" @@ -142,6 +151,7 @@ public void testInputBoardWithSingleRowAndTwoFlowersAtEdges() { @Disabled("Remove to run test") @Test + @DisplayName("vertical line") public void testInputBoardWithSingleColumnAndTwoFlowers() { List inputBoard = Arrays.asList( " ", @@ -166,6 +176,7 @@ public void testInputBoardWithSingleColumnAndTwoFlowers() { @Disabled("Remove to run test") @Test + @DisplayName("vertical line, flowers at edges") public void testInputBoardWithSingleColumnAndTwoFlowersAtEdges() { List inputBoard = Arrays.asList( "*", @@ -190,6 +201,7 @@ public void testInputBoardWithSingleColumnAndTwoFlowersAtEdges() { @Disabled("Remove to run test") @Test + @DisplayName("cross") public void testInputBoardWithFlowersInCross() { List inputBoard = Arrays.asList( " * ", @@ -214,6 +226,7 @@ public void testInputBoardWithFlowersInCross() { @Disabled("Remove to run test") @Test + @DisplayName("large garden") public void testLargeInputBoard() { List inputBoard = Arrays.asList( " * * ", diff --git a/exercises/practice/food-chain/src/test/java/FoodChainTest.java b/exercises/practice/food-chain/src/test/java/FoodChainTest.java index a0f5caa57..43e7a0746 100644 --- a/exercises/practice/food-chain/src/test/java/FoodChainTest.java +++ b/exercises/practice/food-chain/src/test/java/FoodChainTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -13,6 +14,7 @@ public void setup() { } @Test + @DisplayName("fly") public void fly() { int verse = 1; String expected = "I know an old lady who swallowed a fly.\n" + @@ -23,6 +25,7 @@ public void fly() { @Test @Disabled("Remove to run test.") + @DisplayName("spider") public void spider() { int verse = 2; String expected = "I know an old lady who swallowed a spider.\n" + @@ -35,6 +38,7 @@ public void spider() { @Test @Disabled("Remove to run test.") + @DisplayName("bird") public void bird() { int verse = 3; String expected = "I know an old lady who swallowed a bird.\n" + @@ -49,6 +53,7 @@ public void bird() { @Test @Disabled("Remove to run test.") + @DisplayName("cat") public void cat() { int verse = 4; String expected = "I know an old lady who swallowed a cat.\n" + @@ -65,6 +70,7 @@ public void cat() { @Test @Disabled("Remove to run test.") + @DisplayName("dog") public void dog() { int verse = 5; String expected = "I know an old lady who swallowed a dog.\n" + @@ -81,6 +87,7 @@ public void dog() { @Test @Disabled("Remove to run test.") + @DisplayName("goat") public void goat() { int verse = 6; String expected = "I know an old lady who swallowed a goat.\n" + @@ -98,6 +105,7 @@ public void goat() { @Test @Disabled("Remove to run test.") + @DisplayName("cow") public void cow() { int verse = 7; String expected = "I know an old lady who swallowed a cow.\n" + @@ -116,6 +124,7 @@ public void cow() { @Test @Disabled("Remove to run test.") + @DisplayName("horse") public void horse() { int verse = 8; String expected = "I know an old lady who swallowed a horse.\n" + @@ -127,6 +136,7 @@ public void horse() { @Test @Disabled("Remove to run test.") + @DisplayName("multiple verses") public void multipleVerses() { int startVerse = 1; int endVerse = 3; @@ -151,6 +161,7 @@ public void multipleVerses() { @Test @Disabled("Remove to run test.") + @DisplayName("full song") public void wholeSong() { int startVerse = 1; int endVerse = 8; diff --git a/exercises/practice/forth/src/test/java/ForthEvaluatorTest.java b/exercises/practice/forth/src/test/java/ForthEvaluatorTest.java index bd36657b3..9a7c23e01 100644 --- a/exercises/practice/forth/src/test/java/ForthEvaluatorTest.java +++ b/exercises/practice/forth/src/test/java/ForthEvaluatorTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -13,6 +14,7 @@ public class ForthEvaluatorTest { private ForthEvaluator forthEvaluator = new ForthEvaluator(); @Test + @DisplayName("numbers just get pushed onto the stack") public void testNumbersAreJustPushedOntoTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 3 4 5"))) .containsExactly(1, 2, 3, 4, 5); @@ -20,6 +22,7 @@ public void testNumbersAreJustPushedOntoTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("pushes negative numbers onto the stack") public void testNegativeNumbersArePushedOntoTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("-1 -2 -3 -4 -5"))) .containsExactly(-1, -2, -3, -4, -5); @@ -27,6 +30,7 @@ public void testNegativeNumbersArePushedOntoTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("can add two numbers") public void testTwoNumbersCanBeAdded() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 +"))) .containsExactly(3); @@ -34,6 +38,7 @@ public void testTwoNumbersCanBeAdded() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfAdditionAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("+"))) @@ -42,6 +47,7 @@ public void testErrorIfAdditionAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is only one value on the stack") public void testErrorIfAdditionAttemptedWithOneNumberOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("1 +"))) @@ -50,6 +56,7 @@ public void testErrorIfAdditionAttemptedWithOneNumberOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("more than two values on the stack") public void testAdditionForMoreThanTwoValuesOnTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 3 +"))) .containsExactly(1, 5); @@ -57,6 +64,7 @@ public void testAdditionForMoreThanTwoValuesOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("can subtract two numbers") public void testTwoNumbersCanBeSubtracted() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("3 4 -"))) .containsExactly(-1); @@ -64,6 +72,7 @@ public void testTwoNumbersCanBeSubtracted() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfSubtractionAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("-"))) @@ -73,6 +82,7 @@ public void testErrorIfSubtractionAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is only one value on the stack") public void testErrorIfSubtractionAttemptedWithOneNumberOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("1 -"))) @@ -81,6 +91,7 @@ public void testErrorIfSubtractionAttemptedWithOneNumberOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("more than two values on the stack") public void testSubtractionForMoreThanTwoValuesOnTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 12 3 -"))) .containsExactly(1, 9); @@ -88,12 +99,14 @@ public void testSubtractionForMoreThanTwoValuesOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("can multiply two numbers") public void testTwoNumbersCanBeMultiplied() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("2 4 *"))).containsExactly(8); } @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfMultiplicationAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("*"))) @@ -102,6 +115,7 @@ public void testErrorIfMultiplicationAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is only one value on the stack") public void testErrorIfMultiplicationAttemptedWithOneNumberOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("1 *"))) @@ -110,6 +124,7 @@ public void testErrorIfMultiplicationAttemptedWithOneNumberOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("more than two values on the stack") public void testMultiplicationForMoreThanTwoValuesOnTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 3 *"))) .containsExactly(1, 6); @@ -117,18 +132,21 @@ public void testMultiplicationForMoreThanTwoValuesOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("can divide two numbers") public void testTwoNumbersCanBeDivided() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("12 3 /"))).containsExactly(4); } @Disabled("Remove to run test") @Test + @DisplayName("performs integer division") public void testThatIntegerDivisionIsUsed() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("8 3 /"))).containsExactly(2); } @Disabled("Remove to run test") @Test + @DisplayName("errors if dividing by zero") public void testErrorIfDividingByZero() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("4 0 /"))) @@ -137,6 +155,7 @@ public void testErrorIfDividingByZero() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfDivisionAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("/"))) @@ -145,6 +164,7 @@ public void testErrorIfDivisionAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is only one value on the stack") public void testErrorIfDivisionAttemptedWithOneNumberOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("1 /"))) @@ -153,6 +173,7 @@ public void testErrorIfDivisionAttemptedWithOneNumberOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("more than two values on the stack") public void testDivisionForMoreThanTwoValuesOnTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 12 3 /"))) .containsExactly(1, 4); @@ -160,42 +181,49 @@ public void testDivisionForMoreThanTwoValuesOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("addition and subtraction") public void testCombinedAdditionAndSubtraction() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 + 4 -"))).containsExactly(-1); } @Disabled("Remove to run test") @Test + @DisplayName("multiplication and division") public void testCombinedMultiplicationAndDivision() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("2 4 * 3 /"))).containsExactly(2); } @Disabled("Remove to run test") @Test + @DisplayName("multiplication and addition") public void testCombinedMultiplicationAndAddition() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 3 4 * +"))).containsExactly(13); } @Disabled("Remove to run test") @Test + @DisplayName("addition and multiplication") public void testCombinedAdditionAndMultiplication() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 3 4 + *"))).containsExactly(7); } @Disabled("Remove to run test") @Test + @DisplayName("copies a value on the stack") public void testDupCopiesAValueOnTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 dup"))).containsExactly(1, 1); } @Disabled("Remove to run test") @Test + @DisplayName("copies the top value on the stack") public void testDupCopiesTopValueOnTheStack() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 dup"))).containsExactly(1, 2, 2); } @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfDuplicatingAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("dup"))) @@ -204,18 +232,21 @@ public void testErrorIfDuplicatingAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("removes the top value on the stack if it is the only one") public void testDropRemovesTheTopValueOnTheStackIfItIsTheOnlyOne() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 drop"))).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("removes the top value on the stack if it is not the only one") public void testDropRemovesTheTopValueOnTheStackIfItIsNotTheOnlyOne() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 drop"))).containsExactly(1); } @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfDroppingAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("drop"))) @@ -224,12 +255,14 @@ public void testErrorIfDroppingAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("swaps the top two values on the stack if they are the only ones") public void testSwapSwapsTheTopTwosValueOnTheStackIfTheyAreTheOnlyOnes() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 swap"))).containsExactly(2, 1); } @Disabled("Remove to run test") @Test + @DisplayName("swaps the top two values on the stack if they are not the only ones") public void testSwapSwapsTheTopTwosValueOnTheStackIfTheyAreNotTheOnlyOnes() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 3 swap"))) .containsExactly(1, 3, 2); @@ -237,6 +270,7 @@ public void testSwapSwapsTheTopTwosValueOnTheStackIfTheyAreNotTheOnlyOnes() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfSwappingAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("swap"))) @@ -245,6 +279,7 @@ public void testErrorIfSwappingAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is only one value on the stack") public void testErrorIfSwappingAttemptedWithOneNumberOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("1 swap"))) @@ -253,6 +288,7 @@ public void testErrorIfSwappingAttemptedWithOneNumberOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("copies the second element if there are only two") public void testOverCopiesTheSecondElementIfThereAreOnlyTwo() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 over"))) .containsExactly(1, 2, 1); @@ -260,6 +296,7 @@ public void testOverCopiesTheSecondElementIfThereAreOnlyTwo() { @Disabled("Remove to run test") @Test + @DisplayName("copies the second element if there are more than two") public void testOverCopiesTheSecondElementIfThereAreMoreThanTwo() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 3 over"))) .containsExactly(1, 2, 3, 2); @@ -267,6 +304,7 @@ public void testOverCopiesTheSecondElementIfThereAreMoreThanTwo() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is nothing on the stack") public void testErrorIfOveringAttemptedWithNothingOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("over"))) @@ -275,6 +313,7 @@ public void testErrorIfOveringAttemptedWithNothingOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("errors if there is only one value on the stack") public void testErrorIfOveringAttemptedWithOneNumberOnTheStack() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("1 over"))) @@ -283,6 +322,7 @@ public void testErrorIfOveringAttemptedWithOneNumberOnTheStack() { @Disabled("Remove to run test") @Test + @DisplayName("can consist of built-in words") public void testUserDefinedOperatorsCanConsistOfBuiltInOperators() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": dup-twice dup dup ;", "1 dup-twice"))) .containsExactly(1, 1, 1); @@ -290,6 +330,7 @@ public void testUserDefinedOperatorsCanConsistOfBuiltInOperators() { @Disabled("Remove to run test") @Test + @DisplayName("execute in the right order") public void testUserDefinedOperatorsAreEvaluatedInTheCorrectOrder() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": countup 1 2 3 ;", "countup"))) .containsExactly(1, 2, 3); @@ -297,6 +338,7 @@ public void testUserDefinedOperatorsAreEvaluatedInTheCorrectOrder() { @Disabled("Remove to run test") @Test + @DisplayName("can override other user-defined words") public void testCanRedefineAUserDefinedOperator() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": foo dup ;", ": foo dup dup ;", "1 foo"))) .containsExactly(1, 1, 1); @@ -304,6 +346,7 @@ public void testCanRedefineAUserDefinedOperator() { @Disabled("Remove to run test") @Test + @DisplayName("can override built-in words") public void testCanOverrideBuiltInWordOperators() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": swap dup ;", "1 swap"))) .containsExactly(1, 1); @@ -311,6 +354,7 @@ public void testCanOverrideBuiltInWordOperators() { @Disabled("Remove to run test") @Test + @DisplayName("can override built-in operators") public void testCanOverrideBuiltInArithmeticOperators() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": + * ;", "3 4 +"))) .containsExactly(12); @@ -318,6 +362,7 @@ public void testCanOverrideBuiltInArithmeticOperators() { @Disabled("Remove to run test") @Test + @DisplayName("can use different words with the same name") public void testCanUseDifferentWordsWithTheSameName() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"))) .containsExactly(5, 6); @@ -325,6 +370,7 @@ public void testCanUseDifferentWordsWithTheSameName() { @Disabled("Remove to run test") @Test + @DisplayName("can define word that uses word with the same name") public void testCanDefineWordThatUsesWordWithTheSameName() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": foo 10 ;", ": foo foo 1 + ;", "foo"))) .containsExactly(11); @@ -332,6 +378,7 @@ public void testCanDefineWordThatUsesWordWithTheSameName() { @Disabled("Remove to run test") @Test + @DisplayName("cannot redefine non-negative numbers") public void testCannotRedefineNonNegativeNumbers() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList(": 1 2 ;"))) @@ -340,6 +387,7 @@ public void testCannotRedefineNonNegativeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("cannot redefine negative numbers") public void testCannotRedefineNegativeNumbers() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList(": -1 2 ;"))) @@ -348,6 +396,7 @@ public void testCannotRedefineNegativeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("errors if executing a non-existent word") public void testErrorIfEvaluatingAnUndefinedOperator() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> forthEvaluator.evaluateProgram(Collections.singletonList("foo"))) @@ -356,6 +405,7 @@ public void testErrorIfEvaluatingAnUndefinedOperator() { @Disabled("Remove to run test") @Test + @DisplayName("DUP is case-insensitive") public void testDupIsCaseInsensitive() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 DUP Dup dup"))) .containsExactly(1, 1, 1, 1); @@ -363,6 +413,7 @@ public void testDupIsCaseInsensitive() { @Disabled("Remove to run test") @Test + @DisplayName("DROP is case-insensitive") public void testDropIsCaseInsensitive() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 3 4 DROP Drop drop"))) .containsExactly(1); @@ -370,6 +421,7 @@ public void testDropIsCaseInsensitive() { @Disabled("Remove to run test") @Test + @DisplayName("SWAP is case-insensitive") public void testSwapIsCaseInsensitive() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 SWAP 3 Swap 4 swap"))) .containsExactly(2, 3, 4, 1); @@ -377,6 +429,7 @@ public void testSwapIsCaseInsensitive() { @Disabled("Remove to run test") @Test + @DisplayName("OVER is case-insensitive") public void testOverIsCaseInsensitive() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("1 2 OVER Over over"))) .containsExactly(1, 2, 1, 2, 1); @@ -384,6 +437,7 @@ public void testOverIsCaseInsensitive() { @Disabled("Remove to run test") @Test + @DisplayName("user-defined words are case-insensitive") public void testUserDefinedWordsAreCaseInsensitive() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": foo dup ;", "1 FOO Foo foo"))) .containsExactly(1, 1, 1, 1); @@ -391,6 +445,7 @@ public void testUserDefinedWordsAreCaseInsensitive() { @Disabled("Remove to run test") @Test + @DisplayName("definitions are case-insensitive") public void testDefinitionsAreCaseInsensitive() { assertThat(forthEvaluator.evaluateProgram(Arrays.asList(": SWAP DUP Dup dup ;", "1 swap"))) .containsExactly(1, 1, 1, 1); @@ -398,6 +453,7 @@ public void testDefinitionsAreCaseInsensitive() { @Disabled @Test + @DisplayName("only defines locally") public void testDefinitionsAreOnlyDefinedLocally() { ForthEvaluator firstInstance = new ForthEvaluator(); ForthEvaluator secondInstance = new ForthEvaluator(); From 21f79ebf4bbc6c5e204d5bec70104b77ea41c801 Mon Sep 17 00:00:00 2001 From: Bibek Parajuli <132128163+bibekparajuli123@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:54:07 +0530 Subject: [PATCH 57/67] Related to #2971 display names (#3000) * added @DisplayName to exercise rectangles * Add DisplayName annotations to rectangles * Add DisplayName annotations to relative-distance * Add DisplayName annotations to resistor-color * Add DisplayName annotations to resistor-color-duo * Add DisplayName annotations to resistor-color-trio * Add DisplayName annotations to reverse-string * Fix import ordering * Fix long display name line length issue [no important files changed] --- .../src/test/java/RectangleCounterTest.java | 15 +++++++++++++++ .../src/test/java/RelativeDistanceTest.java | 11 +++++++++++ .../src/test/java/ResistorColorDuoTest.java | 10 +++++++++- .../src/test/java/ResistorColorTrioTest.java | 13 ++++++++++++- .../src/test/java/ResistorColorTest.java | 7 ++++++- .../rest-api/src/test/java/RestApiTest.java | 10 ++++++++++ .../src/test/java/ReverseStringTest.java | 7 +++++++ 7 files changed, 70 insertions(+), 3 deletions(-) diff --git a/exercises/practice/rectangles/src/test/java/RectangleCounterTest.java b/exercises/practice/rectangles/src/test/java/RectangleCounterTest.java index 816c2b7aa..c1f665729 100644 --- a/exercises/practice/rectangles/src/test/java/RectangleCounterTest.java +++ b/exercises/practice/rectangles/src/test/java/RectangleCounterTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ public void setUp() { } @Test + @DisplayName("no rows") public void testInputWithNoRowsContainsNoRectangles() { String[] inputGrid = new String[]{}; @@ -22,6 +24,7 @@ public void testInputWithNoRowsContainsNoRectangles() { @Disabled("Remove to run test") @Test + @DisplayName("no columns") public void testInputWithNoColumnsContainsNoRectangles() { String[] inputGrid = new String[]{""}; @@ -30,6 +33,7 @@ public void testInputWithNoColumnsContainsNoRectangles() { @Disabled("Remove to run test") @Test + @DisplayName("no rectangles") public void testNonTrivialInputWithNoRectangles() { String[] inputGrid = new String[]{" "}; @@ -38,6 +42,7 @@ public void testNonTrivialInputWithNoRectangles() { @Disabled("Remove to run test") @Test + @DisplayName("one rectangle") public void testInputWithOneRectangle() { String[] inputGrid = new String[]{ "+-+", @@ -50,6 +55,7 @@ public void testInputWithOneRectangle() { @Disabled("Remove to run test") @Test + @DisplayName("two rectangles without shared parts") public void testInputWithTwoRectanglesWithoutSharedEdges() { String[] inputGrid = new String[]{ " +-+", @@ -64,6 +70,7 @@ public void testInputWithTwoRectanglesWithoutSharedEdges() { @Disabled("Remove to run test") @Test + @DisplayName("five rectangles with shared parts") public void testInputWithFiveRectanglesWithSharedEdges() { String[] inputGrid = new String[]{ " +-+", @@ -78,6 +85,7 @@ public void testInputWithFiveRectanglesWithSharedEdges() { @Disabled("Remove to run test") @Test + @DisplayName("rectangle of height 1 is counted") public void testThatRectangleOfHeightOneIsCounted() { String[] inputGrid = new String[]{ "+--+", @@ -89,6 +97,7 @@ public void testThatRectangleOfHeightOneIsCounted() { @Disabled("Remove to run test") @Test + @DisplayName("rectangle of width 1 is counted") public void testThatRectangleOfWidthOneIsCounted() { String[] inputGrid = new String[]{ "++", @@ -101,6 +110,7 @@ public void testThatRectangleOfWidthOneIsCounted() { @Disabled("Remove to run test") @Test + @DisplayName("1x1 square is counted") public void testThatOneByOneSquareIsCounted() { String[] inputGrid = new String[]{ "++", @@ -112,6 +122,7 @@ public void testThatOneByOneSquareIsCounted() { @Disabled("Remove to run test") @Test + @DisplayName("only complete rectangles are counted") public void testThatIncompleteRectanglesAreNotCounted() { String[] inputGrid = new String[]{ " +-+", @@ -126,6 +137,7 @@ public void testThatIncompleteRectanglesAreNotCounted() { @Disabled("Remove to run test") @Test + @DisplayName("rectangles can be of different sizes") public void testThatRectanglesOfDifferentSizesAreAllCounted() { String[] inputGrid = new String[]{ "+------+----+", @@ -140,6 +152,7 @@ public void testThatRectanglesOfDifferentSizesAreAllCounted() { @Disabled("Remove to run test") @Test + @DisplayName("corner is required for a rectangle to be complete") public void testThatIntersectionsWithoutCornerCharacterDoNotCountAsRectangleCorners() { String[] inputGrid = new String[]{ "+------+----+", @@ -154,6 +167,7 @@ public void testThatIntersectionsWithoutCornerCharacterDoNotCountAsRectangleCorn @Disabled("Remove to run test") @Test + @DisplayName("large input with many rectangles") public void testLargeInputWithManyRectangles() { String[] inputGrid = new String[]{ "+---+--+----+", @@ -171,6 +185,7 @@ public void testLargeInputWithManyRectangles() { @Disabled("Remove to run test") @Test + @DisplayName("rectangles must have four sides") public void testRectanglesMustHaveFourSides() { String[] inputGrid = new String[]{ "+-+ +-+", diff --git a/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java b/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java index 3002b1a91..b10a9459f 100644 --- a/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java +++ b/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,6 +13,7 @@ public class RelativeDistanceTest { @Test + @DisplayName("Direct parent-child relation") public void testDirectParentChildRelation() { Map> familyTree = new HashMap<>() { { @@ -26,6 +28,7 @@ public void testDirectParentChildRelation() { @Disabled("Remove to run test") @Test + @DisplayName("Sibling relationship") public void testSiblingRelationship() { Map> familyTree = new HashMap<>() { { @@ -39,6 +42,7 @@ public void testSiblingRelationship() { @Disabled("Remove to run test") @Test + @DisplayName("Two degrees of separation, grandchild") public void testTwoDegreesOfSeparationGrandchild() { Map> familyTree = new HashMap<>() { { @@ -53,6 +57,7 @@ public void testTwoDegreesOfSeparationGrandchild() { @Disabled("Remove to run test") @Test + @DisplayName("Unrelated individuals") public void testUnrelatedIndividuals() { Map> familyTree = new HashMap<>() { { @@ -67,6 +72,7 @@ public void testUnrelatedIndividuals() { @Disabled("Remove to run test") @Test + @DisplayName("Complex graph, cousins") public void testComplexGraphCousins() { Map> familyTree = new HashMap<>() { { @@ -131,6 +137,7 @@ public void testComplexGraphCousins() { @Disabled("Remove to run test") @Test + @DisplayName("Complex graph, no shortcut, far removed nephew") public void testComplexGraphNoShortcutFarRemovedNephew() { Map> familyTree = new HashMap<>() { { @@ -194,6 +201,10 @@ public void testComplexGraphNoShortcutFarRemovedNephew() { @Disabled("Remove to run test") @Test + @DisplayName( + "Complex graph, some shortcuts, cross-down and cross-up, " + + "cousins several times removed, with unrelated family tree" + ) public void testComplexGraphSomeShortcutsCrossDownAndCrossUpCousinsSeveralTimesRemovedWithUnrelatedFamilyTree() { Map> familyTree = new HashMap<>() { { diff --git a/exercises/practice/resistor-color-duo/src/test/java/ResistorColorDuoTest.java b/exercises/practice/resistor-color-duo/src/test/java/ResistorColorDuoTest.java index ef5fec6bb..6e8c5d0d4 100644 --- a/exercises/practice/resistor-color-duo/src/test/java/ResistorColorDuoTest.java +++ b/exercises/practice/resistor-color-duo/src/test/java/ResistorColorDuoTest.java @@ -1,6 +1,7 @@ import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -13,6 +14,7 @@ public void setup() { } @Test + @DisplayName("Brown and black") public void testBrownAndBlack() { assertThat( resistorColorDuo.value(new String[]{"brown", "black"}) @@ -21,6 +23,7 @@ public void testBrownAndBlack() { @Disabled("Remove to run test") @Test + @DisplayName("Blue and grey") public void testBlueAndGrey() { assertThat( resistorColorDuo.value(new String[]{ "blue", "grey" }) @@ -29,6 +32,7 @@ public void testBlueAndGrey() { @Disabled("Remove to run test") @Test + @DisplayName("Yellow and violet") public void testYellowAndViolet() { assertThat( resistorColorDuo.value(new String[]{ "yellow", "violet" }) @@ -37,6 +41,7 @@ public void testYellowAndViolet() { @Disabled("Remove to run test") @Test + @DisplayName("Orange and orange") public void testOrangeAndOrange() { assertThat( resistorColorDuo.value(new String[]{ "orange", "orange" }) @@ -45,6 +50,7 @@ public void testOrangeAndOrange() { @Disabled("Remove to run test") @Test + @DisplayName("White and red") public void testWhiteAndRed() { assertThat( resistorColorDuo.value(new String[]{ "white", "red" }) @@ -53,6 +59,7 @@ public void testWhiteAndRed() { @Disabled("Remove to run test") @Test + @DisplayName("Black and brown, one-digit") public void testBlackAndBrownOneDigit() { assertThat( resistorColorDuo.value(new String[]{ "black", "brown" }) @@ -61,6 +68,7 @@ public void testBlackAndBrownOneDigit() { @Disabled("Remove to run test") @Test + @DisplayName("Ignore additional colors") public void testIgnoreAdditionalColors() { assertThat( resistorColorDuo.value(new String[]{ "green", "brown", "orange" }) diff --git a/exercises/practice/resistor-color-trio/src/test/java/ResistorColorTrioTest.java b/exercises/practice/resistor-color-trio/src/test/java/ResistorColorTrioTest.java index 6d966eb33..3c682c3be 100644 --- a/exercises/practice/resistor-color-trio/src/test/java/ResistorColorTrioTest.java +++ b/exercises/practice/resistor-color-trio/src/test/java/ResistorColorTrioTest.java @@ -1,6 +1,7 @@ import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -13,6 +14,7 @@ public void setup() { } @Test + @DisplayName("Orange and orange and black") public void testOrangeAndOrangeAndBlack() { assertThat( resistorColorTrio.label(new String[]{"orange", "orange", "black"}) @@ -21,6 +23,7 @@ public void testOrangeAndOrangeAndBlack() { @Disabled("Remove to run test") @Test + @DisplayName("Blue and grey and brown") public void testBlueAndGreyAndBrown() { assertThat( resistorColorTrio.label(new String[]{"blue", "grey", "brown"}) @@ -29,6 +32,7 @@ public void testBlueAndGreyAndBrown() { @Disabled("Remove to run test") @Test + @DisplayName("Red and black and red") public void testRedAndBlackAndRed() { assertThat( resistorColorTrio.label(new String[]{"red", "black", "red"}) @@ -37,6 +41,7 @@ public void testRedAndBlackAndRed() { @Disabled("Remove to run test") @Test + @DisplayName("Green and brown and orange") public void testGreenAndBrownAndOrange() { assertThat( resistorColorTrio.label(new String[]{"green", "brown", "orange"}) @@ -45,6 +50,7 @@ public void testGreenAndBrownAndOrange() { @Disabled("Remove to run test") @Test + @DisplayName("Yellow and violet and yellow") public void testYellowAndVioletAndYellow() { assertThat( resistorColorTrio.label(new String[]{"yellow", "violet", "yellow"}) @@ -53,6 +59,7 @@ public void testYellowAndVioletAndYellow() { @Disabled("Remove to run test") @Test + @DisplayName("Blue and violet and blue") public void testBlueAndVioletAndBlue() { assertThat( resistorColorTrio.label(new String[]{"blue", "violet", "blue"}) @@ -61,6 +68,7 @@ public void testBlueAndVioletAndBlue() { @Disabled("Remove to run test") @Test + @DisplayName("Minimum possible value") public void testBlackAndBlackAndBlack() { assertThat( resistorColorTrio.label(new String[]{"black", "black", "black"}) @@ -69,6 +77,7 @@ public void testBlackAndBlackAndBlack() { @Disabled("Remove to run test") @Test + @DisplayName("Maximum possible value") public void testWhiteAndWhiteAndWhite() { assertThat( resistorColorTrio.label(new String[]{"white", "white", "white"}) @@ -77,6 +86,7 @@ public void testWhiteAndWhiteAndWhite() { @Disabled("Remove to run test") @Test + @DisplayName("First two colors make an invalid octal number") public void testFirstTwoColorsMakeAnInvalidOctalNumber() { assertThat( resistorColorTrio.label(new String[]{"black", "grey", "black"}) @@ -85,6 +95,7 @@ public void testFirstTwoColorsMakeAnInvalidOctalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Ignore extra colors") public void testIgnoreExtraColors() { assertThat( resistorColorTrio.label(new String[]{"blue", "green", "yellow", "orange"}) diff --git a/exercises/practice/resistor-color/src/test/java/ResistorColorTest.java b/exercises/practice/resistor-color/src/test/java/ResistorColorTest.java index fa7426b5b..9eb218d05 100644 --- a/exercises/practice/resistor-color/src/test/java/ResistorColorTest.java +++ b/exercises/practice/resistor-color/src/test/java/ResistorColorTest.java @@ -1,6 +1,7 @@ import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class ResistorColorTest { @@ -13,24 +14,28 @@ public void setup() { } @Test + @DisplayName("Black") public void testBlackColorCode() { assertThat(resistorColor.colorCode("black")).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("white") public void testWhiteColorCode() { assertThat(resistorColor.colorCode("white")).isEqualTo(9); } @Disabled("Remove to run test") @Test + @DisplayName("Orange") public void testOrangeColorCode() { assertThat(resistorColor.colorCode("orange")).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("Colors") public void testColors() { assertThat(resistorColor.colors()).containsExactly( "black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white" diff --git a/exercises/practice/rest-api/src/test/java/RestApiTest.java b/exercises/practice/rest-api/src/test/java/RestApiTest.java index d03f15717..e17d0a9a6 100644 --- a/exercises/practice/rest-api/src/test/java/RestApiTest.java +++ b/exercises/practice/rest-api/src/test/java/RestApiTest.java @@ -1,6 +1,7 @@ import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -8,6 +9,7 @@ public class RestApiTest { @Test + @DisplayName("no users") public void noUsers() { String expected = new JSONObject().put("users", new JSONArray()).toString(); @@ -18,6 +20,7 @@ public void noUsers() { @Disabled("Remove to run test") @Test + @DisplayName("add user") public void addUser() { String expected = new JSONObject() .put("name", "Adam") @@ -33,6 +36,7 @@ public void addUser() { @Disabled("Remove to run test") @Test + @DisplayName("get single user") public void getSingleUser() { String expected = new JSONObject() .put( @@ -56,6 +60,7 @@ public void getSingleUser() { } @Test + @DisplayName("both users have 0 balance") public void bothUsersHave0Balance() { String expected = new JSONObject() @@ -94,6 +99,7 @@ public void bothUsersHave0Balance() { @Disabled("Remove to run test") @Test + @DisplayName("borrower has negative balance") public void borrowerHasNegativeBalance() { String expected = new JSONObject() @@ -135,6 +141,7 @@ public void borrowerHasNegativeBalance() { @Disabled("Remove to run test") @Test + @DisplayName("lender has negative balance") public void lenderHasNegativeBalance() { String expected = new JSONObject() @@ -178,6 +185,7 @@ public void lenderHasNegativeBalance() { @Disabled("Remove to run test") @Test + @DisplayName("lender owes borrower") public void lenderOwesBorrower() { String expected = new JSONObject() @@ -216,6 +224,7 @@ public void lenderOwesBorrower() { @Disabled("Remove to run test") @Test + @DisplayName("lender owes borrower less than new loan") public void lenderOwesBorrowerLessThanNewLoan() { String expected = new JSONObject() @@ -254,6 +263,7 @@ public void lenderOwesBorrowerLessThanNewLoan() { @Disabled("Remove to run test") @Test + @DisplayName("lender owes borrower same as new loan") public void lenderOwesBorrowerSameAsNewLoan() { String expected = new JSONObject() diff --git a/exercises/practice/reverse-string/src/test/java/ReverseStringTest.java b/exercises/practice/reverse-string/src/test/java/ReverseStringTest.java index 1c7a72c74..64475609c 100644 --- a/exercises/practice/reverse-string/src/test/java/ReverseStringTest.java +++ b/exercises/practice/reverse-string/src/test/java/ReverseStringTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -6,36 +7,42 @@ public class ReverseStringTest { @Test + @DisplayName("an empty string") public void testAnEmptyString() { assertThat(new ReverseString().reverse("")).isEqualTo(""); } @Disabled("Remove to run test") @Test + @DisplayName("a word") public void testAWord() { assertThat(new ReverseString().reverse("robot")).isEqualTo("tobor"); } @Disabled("Remove to run test") @Test + @DisplayName("a capitalized word") public void testACapitalizedWord() { assertThat(new ReverseString().reverse("Ramen")).isEqualTo("nemaR"); } @Disabled("Remove to run test") @Test + @DisplayName("a sentence with punctuation") public void testASentenceWithPunctuation() { assertThat(new ReverseString().reverse("I'm hungry!")).isEqualTo("!yrgnuh m'I"); } @Disabled("Remove to run test") @Test + @DisplayName("a palindrome") public void testAPalindrome() { assertThat(new ReverseString().reverse("racecar")).isEqualTo("racecar"); } @Disabled("Remove to run test") @Test + @DisplayName("an even-sized word") public void testAnEvenSizedWord() { assertThat(new ReverseString().reverse("drawer")).isEqualTo("reward"); } From 766c84e0d5d94255067aff9f6ce0a80214135f0d Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Sun, 7 Sep 2025 04:59:30 +0530 Subject: [PATCH 58/67] Testing workflow (#3002) * Run test for only changed exercises --- .github/workflows/java.yml | 20 ++++++++++++++++- bin/test-changed-exercise | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100755 bin/test-changed-exercise diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index 48fc8795a..a966405ee 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -39,8 +39,9 @@ jobs: run: ./gradlew check --exclude-task test --continue working-directory: exercises - test: + test-all: name: Test all exercises using java-test-runner + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-24.04 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 @@ -52,3 +53,20 @@ jobs: name: test-results path: exercises/**/build/results.json if: failure() + + test-changed: + name: Test changed exercises using java-test-runner + if: github.event_name == 'pull_request' + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + with: + fetch-depth: 0 + - name: Test changed exercises using java-test-runner + run: bin/test-changed-exercise + - name: Archive test results + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: test-results + path: exercises/**/build/results.json + if: failure() diff --git a/bin/test-changed-exercise b/bin/test-changed-exercise new file mode 100755 index 000000000..776e940e1 --- /dev/null +++ b/bin/test-changed-exercise @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -eo pipefail + +# Determine the base branch of the PR +BASE_BRANCH=${GITHUB_BASE_REF:-main} + +# Fetch full history for proper diff +git fetch origin "$BASE_BRANCH" + +# Compute merge base +MERGE_BASE=$(git merge-base HEAD origin/"$BASE_BRANCH") + +# Get changed files relative to merge base +changed_files=$(git diff --name-only "$MERGE_BASE" HEAD) + +# Extract unique exercise directories +changed_exercises=$(echo "$changed_files" | \ + grep -E '^exercises/(practice|concept)/[^/]+/.+\.(java|gradle)$' | \ + cut -d/ -f1-3 | sort -u) + +if [ -z "$changed_exercises" ]; then + echo "No relevant exercises changed, skipping tests." + exit 0 +fi + +# Print exercises +echo "Changed exercises detected:" +echo "$changed_exercises" +echo "----------------------------------------" + +# Run tests +for dir in $changed_exercises; do + slug=$(basename "$dir") + + echo "========================================" + echo "=== Running tests for $slug ===" + echo "========================================" + + if [[ $dir == exercises/practice/* ]]; then + ./exercises/gradlew -p exercises ":practice:$slug:test" + elif [[ $dir == exercises/concept/* ]]; then + ./exercises/gradlew -p exercises ":concept:$slug:test" + fi +done From fb7042257201c1d266227231ea4ddbe99d4d9be9 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Mon, 8 Sep 2025 16:19:15 +0530 Subject: [PATCH 59/67] gigasecond, go-counting, grade-school, grains, grep (#3004) Co-authored-by: Jagdish Prajapati [no important files changed] --- .../src/test/java/GigasecondTest.java | 7 +++++ .../src/test/java/GoCountingTest.java | 12 +++++++++ .../src/test/java/SchoolTest.java | 20 ++++++++++++++ .../grains/src/test/java/GrainsTest.java | 12 +++++++++ .../grep/src/test/java/GrepToolTest.java | 26 +++++++++++++++++++ 5 files changed, 77 insertions(+) diff --git a/exercises/practice/gigasecond/src/test/java/GigasecondTest.java b/exercises/practice/gigasecond/src/test/java/GigasecondTest.java index 6b190f606..2f5872fdd 100644 --- a/exercises/practice/gigasecond/src/test/java/GigasecondTest.java +++ b/exercises/practice/gigasecond/src/test/java/GigasecondTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.time.LocalDate; @@ -10,6 +11,7 @@ public class GigasecondTest { @Test + @DisplayName("date only specification of time") public void modernTime() { Gigasecond gigaSecond = new Gigasecond(LocalDate.of(2011, Month.APRIL, 25)); @@ -18,6 +20,7 @@ public void modernTime() { @Disabled("Remove to run test") @Test + @DisplayName("second test for date only specification of time") public void afterEpochTime() { Gigasecond gigaSecond = new Gigasecond(LocalDate.of(1977, Month.JUNE, 13)); @@ -26,6 +29,7 @@ public void afterEpochTime() { @Disabled("Remove to run test") @Test + @DisplayName("third test for date only specification of time") public void beforeEpochTime() { Gigasecond gigaSecond = new Gigasecond(LocalDate.of(1959, Month.JULY, 19)); @@ -34,6 +38,7 @@ public void beforeEpochTime() { @Disabled("Remove to run test") @Test + @DisplayName("full time specified") public void withFullTimeSpecified() { Gigasecond gigaSecond = new Gigasecond(LocalDateTime.of(2015, Month.JANUARY, 24, 22, 0, 0)); @@ -42,6 +47,7 @@ public void withFullTimeSpecified() { @Disabled("Remove to run test") @Test + @DisplayName("full time with day roll-over") public void withFullTimeSpecifiedAndDayRollover() { Gigasecond gigaSecond = new Gigasecond(LocalDateTime.of(2015, Month.JANUARY, 24, 23, 59, 59)); @@ -50,6 +56,7 @@ public void withFullTimeSpecifiedAndDayRollover() { @Disabled("Remove to run test") @Test + @DisplayName("does not mutate the input") public void doesNotMutateInput() { LocalDateTime input = LocalDateTime.of(2015, Month.JANUARY, 24, 23, 59, 59); new Gigasecond(input).getDateTime(); diff --git a/exercises/practice/go-counting/src/test/java/GoCountingTest.java b/exercises/practice/go-counting/src/test/java/GoCountingTest.java index 48e14dc78..2b336088c 100644 --- a/exercises/practice/go-counting/src/test/java/GoCountingTest.java +++ b/exercises/practice/go-counting/src/test/java/GoCountingTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.awt.*; @@ -18,6 +19,7 @@ public class GoCountingTest { " W "; @Test + @DisplayName("Black corner territory on 5x5 board") public void blackCorner5x5BoardTest() { GoCounting gocounting = new GoCounting(board5x5); @@ -32,6 +34,7 @@ public void blackCorner5x5BoardTest() { @Disabled("Remove to run test") @Test + @DisplayName("White center territory on 5x5 board") public void whiteCenter5x5BoardTest() { GoCounting gocounting = new GoCounting(board5x5); @@ -44,6 +47,7 @@ public void whiteCenter5x5BoardTest() { @Disabled("Remove to run test") @Test + @DisplayName("Open corner territory on 5x5 board") public void openCorner5x5BoardTest() { GoCounting gocounting = new GoCounting(board5x5); @@ -58,6 +62,7 @@ public void openCorner5x5BoardTest() { @Disabled("Remove to run test") @Test + @DisplayName("A stone and not a territory on 5x5 board") public void stoneNotTerritory5x5Board() { GoCounting gocounting = new GoCounting(board5x5); @@ -69,6 +74,7 @@ public void stoneNotTerritory5x5Board() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid because X is too low for 5x5 board") public void invalidXTooLow5x5Board() { GoCounting gocounting = new GoCounting(board5x5); @@ -79,6 +85,7 @@ public void invalidXTooLow5x5Board() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid because X is too high for 5x5 board") public void invalidXTooHigh5x5Board() { GoCounting gocounting = new GoCounting(board5x5); @@ -89,6 +96,7 @@ public void invalidXTooHigh5x5Board() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid because Y is too low for 5x5 board") public void invalidYTooLow5x5Board() { GoCounting gocounting = new GoCounting(board5x5); @@ -99,6 +107,7 @@ public void invalidYTooLow5x5Board() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid because Y is too high for 5x5 board") public void invalidYTooHigh5x5Board() { GoCounting gocounting = new GoCounting(board5x5); @@ -109,6 +118,7 @@ public void invalidYTooHigh5x5Board() { @Disabled("Remove to run test") @Test + @DisplayName("One territory is the whole board") public void oneTerritoryIsWholeBoardTest() { GoCounting gocounting = new GoCounting(" "); @@ -127,6 +137,7 @@ public void oneTerritoryIsWholeBoardTest() { @Disabled("Remove to run test") @Test + @DisplayName("Two territory rectangular board") public void twoTerritoryRectangularBoardTest() { GoCounting gocounting = new GoCounting(" BW \n BW "); @@ -150,6 +161,7 @@ public void twoTerritoryRectangularBoardTest() { @Disabled("Remove to run test") @Test + @DisplayName("Two region rectangular board") public void twoRegionRectangularBoardTest() { GoCounting gocounting = new GoCounting(" B "); diff --git a/exercises/practice/grade-school/src/test/java/SchoolTest.java b/exercises/practice/grade-school/src/test/java/SchoolTest.java index 6363b8bd8..10a7fd728 100644 --- a/exercises/practice/grade-school/src/test/java/SchoolTest.java +++ b/exercises/practice/grade-school/src/test/java/SchoolTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -13,18 +14,21 @@ public void setUp() { } @Test + @DisplayName("Roster is empty when no student is added") public void rosterReturnsAnEmptyListIfThereAreNoStudentsEnrolled() { assertThat(school.roster()).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("Add a student") public void addAStudent() { assertThat(school.add("Aimee", 2)).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("Student is added to the roster") public void addingAStudentAddsThemToTheSortedRoster() { school.add("Aimee", 2); @@ -33,6 +37,7 @@ public void addingAStudentAddsThemToTheSortedRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Adding multiple students in the same grade in the roster") public void addingMultipleStudentsInTheSameGrade() { assertThat(school.add("Blair", 2)).isTrue(); assertThat(school.add("James", 2)).isTrue(); @@ -41,6 +46,7 @@ public void addingMultipleStudentsInTheSameGrade() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple students in the same grade are added to the roster") public void addingMoreStudentsAddsThemToTheSameSortedRoster() { school.add("Blair", 2); school.add("James", 2); @@ -51,6 +57,7 @@ public void addingMoreStudentsAddsThemToTheSameSortedRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Cannot add student to same grade in the roster more than once") public void cannotAddStudentsToSameGradeInTheRosterMoreThanOnce() { assertThat(school.add("Blair", 2)).isTrue(); assertThat(school.add("James", 2)).isTrue(); @@ -60,6 +67,7 @@ public void cannotAddStudentsToSameGradeInTheRosterMoreThanOnce() { @Disabled("Remove to run test") @Test + @DisplayName("Student not added to same grade in the roster more than once") public void studentNotAddedToSameGradeInTheRosterMoreThanOnce() { school.add("Blair", 2); school.add("James", 2); @@ -71,6 +79,7 @@ public void studentNotAddedToSameGradeInTheRosterMoreThanOnce() { @Disabled("Remove to run test") @Test + @DisplayName("Adding students in multiple grades") public void addingStudentsInMultipleGrades() { assertThat(school.add("Chelsea", 3)).isTrue(); assertThat(school.add("Logan", 7)).isTrue(); @@ -78,6 +87,7 @@ public void addingStudentsInMultipleGrades() { @Disabled("Remove to run test") @Test + @DisplayName("Students in multiple grades are added to the roster") public void addingStudentsToDifferentGradesAddsThemToTheSameSortedRoster() { school.add("Chelsea", 3); school.add("Logan", 7); @@ -87,6 +97,7 @@ public void addingStudentsToDifferentGradesAddsThemToTheSameSortedRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Cannot add same student to multiple grades in the roster") public void cannotAddSameStudentToMultipleGradesInTheRoster() { assertThat(school.add("Blair", 2)).isTrue(); assertThat(school.add("James", 2)).isTrue(); @@ -96,6 +107,7 @@ public void cannotAddSameStudentToMultipleGradesInTheRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Student not added to multiple grades in the roster") public void studentNotAddedToMultipleGradesInTheRoster() { school.add("Blair", 2); school.add("James", 2); @@ -107,6 +119,7 @@ public void studentNotAddedToMultipleGradesInTheRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Students are sorted by grades in the roster") public void studentsAreSortedByGradeInTheRoster() { school.add("Jim", 3); school.add("Peter", 2); @@ -117,6 +130,7 @@ public void studentsAreSortedByGradeInTheRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Students are sorted by name in the roster") public void studentsAreSortedByNameInTheRoster() { school.add("Peter", 2); school.add("Zoe", 2); @@ -127,6 +141,7 @@ public void studentsAreSortedByNameInTheRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Students are sorted by grades and then by name in the roster") public void studentsAreSortedByGradeAndThenByNameInTheRoster() { school.add("Peter", 2); school.add("Anna", 1); @@ -141,12 +156,14 @@ public void studentsAreSortedByGradeAndThenByNameInTheRoster() { @Disabled("Remove to run test") @Test + @DisplayName("Grade is empty if no students in the roster") public void gradeIsEmptyIfNoStudentsInTheRoster() { assertThat(school.grade(1)).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("Grade is empty if no students in that grade") public void gradeIsEmptyIfNoStudentsInThatGrade() { school.add("Peter", 2); school.add("Zoe", 2); @@ -158,6 +175,7 @@ public void gradeIsEmptyIfNoStudentsInThatGrade() { @Disabled("Remove to run test") @Test + @DisplayName("Student not added to same grade more than once") public void studentNotAddedToTheSameGradeMoreThanOnce() { school.add("Blair", 2); school.add("James", 2); @@ -169,6 +187,7 @@ public void studentNotAddedToTheSameGradeMoreThanOnce() { @Disabled("Remove to run test") @Test + @DisplayName("Student not added to multiple grades") public void studentNotAddedToMultipleGrades() { school.add("Blair", 2); school.add("James", 2); @@ -181,6 +200,7 @@ public void studentNotAddedToMultipleGrades() { @Disabled("Remove to run test") @Test + @DisplayName("Students are sorted by name in a grade") public void studentsAreSortedByNameInAGrade() { school.add("Franklin", 5); school.add("Bradley", 5); diff --git a/exercises/practice/grains/src/test/java/GrainsTest.java b/exercises/practice/grains/src/test/java/GrainsTest.java index 2b5a47689..a09e48e88 100644 --- a/exercises/practice/grains/src/test/java/GrainsTest.java +++ b/exercises/practice/grains/src/test/java/GrainsTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.math.BigInteger; @@ -11,6 +12,7 @@ public class GrainsTest { private Grains grains = new Grains(); @Test + @DisplayName("returns the number of grains on the square") public void countAtSquare1() { BigInteger result = grains.grainsOnSquare(1); assertThat(result).isEqualTo(new BigInteger("1")); @@ -18,6 +20,7 @@ public void countAtSquare1() { @Disabled("Remove to run test") @Test + @DisplayName("grains on square 2") public void countAtSquare2() { BigInteger result = grains.grainsOnSquare(2); assertThat(result).isEqualTo(new BigInteger("2")); @@ -25,6 +28,7 @@ public void countAtSquare2() { @Disabled("Remove to run test") @Test + @DisplayName("grains on square 3") public void countAtSquare3() { BigInteger result = grains.grainsOnSquare(3); assertThat(result).isEqualTo(new BigInteger("4")); @@ -32,6 +36,7 @@ public void countAtSquare3() { @Disabled("Remove to run test") @Test + @DisplayName("grains on square 4") public void countAtSquare4() { BigInteger result = grains.grainsOnSquare(4); assertThat(result).isEqualTo(new BigInteger("8")); @@ -39,6 +44,7 @@ public void countAtSquare4() { @Disabled("Remove to run test") @Test + @DisplayName("grains on square 16") public void countAtSquare16() { BigInteger result = grains.grainsOnSquare(16); assertThat(result).isEqualTo(new BigInteger("32768")); @@ -46,6 +52,7 @@ public void countAtSquare16() { @Disabled("Remove to run test") @Test + @DisplayName("grains on square 32") public void countAtSquare32() { BigInteger result = grains.grainsOnSquare(32); assertThat(result).isEqualTo(new BigInteger("2147483648")); @@ -53,6 +60,7 @@ public void countAtSquare32() { @Disabled("Remove to run test") @Test + @DisplayName("grains on square 64") public void countAtSquare64() { BigInteger result = grains.grainsOnSquare(64); assertThat(result).isEqualTo(new BigInteger("9223372036854775808")); @@ -60,6 +68,7 @@ public void countAtSquare64() { @Disabled("Remove to run test") @Test + @DisplayName("square 0 is invalid") public void errorOnNullBoardSize() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> grains.grainsOnSquare(0)) @@ -68,6 +77,7 @@ public void errorOnNullBoardSize() { @Disabled("Remove to run test") @Test + @DisplayName("negative square is invalid") public void errorOnNegativeBoardSize() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> grains.grainsOnSquare(-1)) @@ -76,6 +86,7 @@ public void errorOnNegativeBoardSize() { @Disabled("Remove to run test") @Test + @DisplayName("square greater than 64 is invalid") public void errorOnExcessiveBoardSize() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> grains.grainsOnSquare(65)) @@ -84,6 +95,7 @@ public void errorOnExcessiveBoardSize() { @Disabled("Remove to run test") @Test + @DisplayName("returns the total number of grains on the board") public void totalNumberOfGrainsOnABoard() { BigInteger total = grains.grainsOnBoard(); assertThat(total).isEqualTo(new BigInteger("18446744073709551615")); diff --git a/exercises/practice/grep/src/test/java/GrepToolTest.java b/exercises/practice/grep/src/test/java/GrepToolTest.java index 54c194ca2..d6d27816d 100644 --- a/exercises/practice/grep/src/test/java/GrepToolTest.java +++ b/exercises/practice/grep/src/test/java/GrepToolTest.java @@ -1,6 +1,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -66,6 +67,7 @@ public void tearDown() throws IOException { } @Test + @DisplayName("One file, one match, no flags") public void oneFileOneMatchNoFlags() { String expected = "Of Atreus, Agamemnon, King of men."; @@ -80,6 +82,7 @@ public void oneFileOneMatchNoFlags() { @Disabled("Remove to run test") @Test + @DisplayName("One file, one match, print line numbers flag") public void oneFileOneMatchPrintLineNumbersFlag() { String expected = "2:Of that Forbidden Tree, whose mortal tast"; @@ -94,6 +97,7 @@ public void oneFileOneMatchPrintLineNumbersFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, one match, case-insensitive flag") public void oneFileOneMatchCaseInsensitiveFlag() { String expected = "Of that Forbidden Tree, whose mortal tast"; @@ -108,6 +112,7 @@ public void oneFileOneMatchCaseInsensitiveFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, one match, print file names flag") public void oneFileOneMatchPrintFileNamesFlag() { String expected = "paradise-lost.txt"; @@ -122,6 +127,7 @@ public void oneFileOneMatchPrintFileNamesFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, one match, match entire lines flag") public void oneFileOneMatchEntireLinesFlag() { String expected = "With loss of Eden, till one greater Man"; @@ -136,6 +142,7 @@ public void oneFileOneMatchEntireLinesFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, one match, multiple flags") public void oneFileOneMatchMultipleFlags() { String expected = "9:Of Atreus, Agamemnon, King of men."; @@ -150,6 +157,7 @@ public void oneFileOneMatchMultipleFlags() { @Disabled("Remove to run test") @Test + @DisplayName("One file, several matches, no flags") public void oneFileSeveralMatchesNoFlags() { String expected = "Nor how it may concern my modesty,\n" + "But I beseech your grace that I may know\n" @@ -166,6 +174,7 @@ public void oneFileSeveralMatchesNoFlags() { @Disabled("Remove to run test") @Test + @DisplayName("One file, several matches, print line numbers flag") public void oneFileSeveralMatchesPrintLineNumbersFlag() { String expected = "3:Nor how it may concern my modesty,\n" + "5:But I beseech your grace that I may know\n" @@ -182,6 +191,7 @@ public void oneFileSeveralMatchesPrintLineNumbersFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, several matches, match entire lines flag") public void oneFileSeveralMatchesMatchEntireLineFlag() { String expected = ""; @@ -196,6 +206,7 @@ public void oneFileSeveralMatchesMatchEntireLineFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, several matches, case-insensitive flag") public void oneFileSeveralMatchesCaseInsensitiveFlag() { String expected = "Achilles sing, O Goddess! Peleus' son;\n" + "The noble Chief Achilles from the son"; @@ -211,6 +222,7 @@ public void oneFileSeveralMatchesCaseInsensitiveFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, several matches, inverted flag") public void oneFileSeveralMatchesInvertedFlag() { String expected = "Brought Death into the World, and all our woe,\n" + "With loss of Eden, till one greater Man\n" @@ -229,6 +241,7 @@ public void oneFileSeveralMatchesInvertedFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, no matches, various flags") public void oneFileNoMatchesVariousFlags() { String expected = ""; @@ -243,6 +256,7 @@ public void oneFileNoMatchesVariousFlags() { @Disabled("Remove to run test") @Test + @DisplayName("One file, one match, file flag takes precedence over line flag") public void oneFileOneMatchFileFlagTakesPrecedenceOverLineFlag() { String expected = "iliad.txt"; @@ -257,6 +271,7 @@ public void oneFileOneMatchFileFlagTakesPrecedenceOverLineFlag() { @Disabled("Remove to run test") @Test + @DisplayName("One file, several matches, inverted and match entire lines flags") public void oneFileSeveralMatchesInvertedAndMatchEntireLinesFlags() { String expected = "Achilles sing, O Goddess! Peleus' son;\n" + "His wrath pernicious, who ten thousand woes\n" @@ -278,6 +293,7 @@ public void oneFileSeveralMatchesInvertedAndMatchEntireLinesFlags() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, one match, no flags") public void multipleFilesOneMatchNoFlags() { String expected = "iliad.txt:Of Atreus, Agamemnon, King of men."; @@ -292,6 +308,7 @@ public void multipleFilesOneMatchNoFlags() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, several matches, no flags") public void multipleFilesSeveralMatchesNoFlags() { String expected = "midsummer-night.txt:Nor how it may concern my modesty,\n" + "midsummer-night.txt:But I beseech your grace that I may know\n" @@ -308,6 +325,7 @@ public void multipleFilesSeveralMatchesNoFlags() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, several matches, print line numbers flag") public void multipleFilesSeveralMatchesPrintLineNumbersFlag() { String expected = "midsummer-night.txt:5:But I beseech your grace that I may know\n" + "midsummer-night.txt:6:The worst that may befall me in this case,\n" @@ -325,6 +343,7 @@ public void multipleFilesSeveralMatchesPrintLineNumbersFlag() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, one match, print file names flag") public void multipleFilesOneMatchPrintFileNamesFlag() { String expected = "iliad.txt\n" + "paradise-lost.txt"; @@ -340,6 +359,7 @@ public void multipleFilesOneMatchPrintFileNamesFlag() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, several matches, case-insensitive flag") public void multipleFilesSeveralMatchesCaseInsensitiveFlag() { String expected = "iliad.txt:Caused to Achaia's host, sent many a soul\n" + "iliad.txt:Illustrious into Ades premature,\n" @@ -363,6 +383,7 @@ public void multipleFilesSeveralMatchesCaseInsensitiveFlag() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, several matches, inverted flag") public void multipleFilesSeveralMatchesInvertedFlag() { String expected = "iliad.txt:Achilles sing, O Goddess! Peleus' son;\n" + "iliad.txt:The noble Chief Achilles from the son\n" @@ -379,6 +400,7 @@ public void multipleFilesSeveralMatchesInvertedFlag() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, one match, match entire lines flag") public void multipleFilesOneMatchEntireLinesFlag() { String expected = "midsummer-night.txt:But I beseech your grace that I may know"; @@ -393,6 +415,7 @@ public void multipleFilesOneMatchEntireLinesFlag() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, one match, multiple flags") public void multipleFilesOneMatchMultipleFlags() { String expected = "paradise-lost.txt:4:With loss of Eden, till one greater Man"; @@ -407,6 +430,7 @@ public void multipleFilesOneMatchMultipleFlags() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, no matches, various flags") public void multipleFilesNoMatchesVariousFlags() { String expected = ""; @@ -421,6 +445,7 @@ public void multipleFilesNoMatchesVariousFlags() { @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, several matches, file flag takes precedence over line number flag") public void multipleFilesSeveralMatchesFileFlagTakesPrecedenceOverLineNumberFlag() { String expected = "iliad.txt\n" + "paradise-lost.txt"; @@ -436,6 +461,7 @@ public void multipleFilesSeveralMatchesFileFlagTakesPrecedenceOverLineNumberFlag @Disabled("Remove to run test") @Test + @DisplayName("Multiple files, several matches, inverted and match entire lines flags") public void multipleFilesSeveralMatchesInvertedAndMatchEntireLinesFlags() { String expected = "iliad.txt:Achilles sing, O Goddess! Peleus' son;\n" + "iliad.txt:His wrath pernicious, who ten thousand woes\n" From 988132dd9d706912e4dbb1d51b3433a2c6c74d1b Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Tue, 9 Sep 2025 23:33:15 +0530 Subject: [PATCH 60/67] hamming, hello-world, high-scores, house (#3009) * hamming, hello-world, high-scores, house * Update HighScoresTest.java [no important files changed] --- .../hamming/src/test/java/HammingTest.java | 10 ++++++++++ .../hello-world/src/test/java/GreeterTest.java | 2 ++ .../high-scores/src/test/java/HighScoresTest.java | 13 +++++++++++++ .../practice/house/src/test/java/HouseTest.java | 15 +++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/exercises/practice/hamming/src/test/java/HammingTest.java b/exercises/practice/hamming/src/test/java/HammingTest.java index 5f9082e35..26f22491f 100644 --- a/exercises/practice/hamming/src/test/java/HammingTest.java +++ b/exercises/practice/hamming/src/test/java/HammingTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -7,36 +8,42 @@ public class HammingTest { @Test + @DisplayName("empty strands") public void testNoDistanceBetweenEmptyStrands() { assertThat(new Hamming("", "").getHammingDistance()).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("single letter identical strands") public void testNoDistanceBetweenShortIdenticalStrands() { assertThat(new Hamming("A", "A").getHammingDistance()).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("single letter different strands") public void testCompleteDistanceInSingleLetterDifferentStrands() { assertThat(new Hamming("G", "T").getHammingDistance()).isEqualTo(1); } @Disabled("Remove to run test") @Test + @DisplayName("long identical strands") public void testDistanceInLongIdenticalStrands() { assertThat(new Hamming("GGACTGAAATCTG", "GGACTGAAATCTG").getHammingDistance()).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("long different strands") public void testDistanceInLongDifferentStrands() { assertThat(new Hamming("GGACGGATTCTG", "AGGACGGATTCT").getHammingDistance()).isEqualTo(9); } @Disabled("Remove to run test") @Test + @DisplayName("disallow first strand longer") public void testValidatesFirstStrandNotLonger() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Hamming("AATG", "AAA")) @@ -45,6 +52,7 @@ public void testValidatesFirstStrandNotLonger() { @Disabled("Remove to run test") @Test + @DisplayName("disallow second strand longer") public void testValidatesSecondStrandNotLonger() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Hamming("ATA", "AGTG")) @@ -53,6 +61,7 @@ public void testValidatesSecondStrandNotLonger() { @Disabled("Remove to run test") @Test + @DisplayName("disallow left empty strand") public void testDisallowLeftEmptyStrand() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Hamming("", "G")) @@ -61,6 +70,7 @@ public void testDisallowLeftEmptyStrand() { @Disabled("Remove to run test") @Test + @DisplayName("disallow right empty strand") public void testDisallowRightEmptyStrand() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Hamming("G", "")) diff --git a/exercises/practice/hello-world/src/test/java/GreeterTest.java b/exercises/practice/hello-world/src/test/java/GreeterTest.java index fc25c1891..cd5fb79a3 100644 --- a/exercises/practice/hello-world/src/test/java/GreeterTest.java +++ b/exercises/practice/hello-world/src/test/java/GreeterTest.java @@ -1,3 +1,4 @@ +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -5,6 +6,7 @@ public class GreeterTest { @Test + @DisplayName("Say Hi!") public void testThatGreeterReturnsTheCorrectGreeting() { assertThat(new Greeter().getGreeting()).isEqualTo("Hello, World!"); } diff --git a/exercises/practice/high-scores/src/test/java/HighScoresTest.java b/exercises/practice/high-scores/src/test/java/HighScoresTest.java index 15fd8eaa4..282d09c6e 100644 --- a/exercises/practice/high-scores/src/test/java/HighScoresTest.java +++ b/exercises/practice/high-scores/src/test/java/HighScoresTest.java @@ -1,6 +1,7 @@ import java.util.Arrays; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -8,6 +9,7 @@ public class HighScoresTest { @Test + @DisplayName("List of scores") public void shouldReturnListOfScores() { HighScores highScores = new HighScores(Arrays.asList(30, 50, 20, 70)); assertThat(highScores.scores()).isEqualTo(Arrays.asList(30, 50, 20, 70)); @@ -15,6 +17,7 @@ public void shouldReturnListOfScores() { @Test @Disabled("Remove to run test") + @DisplayName("Latest score") public void shouldReturnLatestAddedScore() { HighScores highScores = new HighScores(Arrays.asList(100, 0, 90, 30)); assertThat(highScores.latest()).isEqualTo(30); @@ -22,6 +25,7 @@ public void shouldReturnLatestAddedScore() { @Test @Disabled("Remove to run test") + @DisplayName("Personal best") public void shouldReturnPersonalBest() { HighScores highScores = new HighScores(Arrays.asList(40, 100, 70)); assertThat(highScores.personalBest()).isEqualTo(100); @@ -29,6 +33,7 @@ public void shouldReturnPersonalBest() { @Test @Disabled("Remove to run test") + @DisplayName("Personal top three from a list of scores") public void shouldReturnPersonalTopThreeFromListOfScores() { HighScores highScores = new HighScores(Arrays.asList(10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70)); assertThat(highScores.personalTopThree()).isEqualTo(Arrays.asList(100, 90, 70)); @@ -36,6 +41,7 @@ public void shouldReturnPersonalTopThreeFromListOfScores() { @Test @Disabled("Remove to run test") + @DisplayName("Personal top highest to lowest") public void shouldReturnPersonalTopThreeSortedHighestToLowest() { HighScores highScores = new HighScores(Arrays.asList(20, 10, 30)); assertThat(highScores.personalTopThree()).isEqualTo(Arrays.asList(30, 20, 10)); @@ -43,6 +49,7 @@ public void shouldReturnPersonalTopThreeSortedHighestToLowest() { @Test @Disabled("Remove to run test") + @DisplayName("Personal top when there is a tie") public void shouldReturnPersonalTopThreeWhenThereIsATie() { HighScores highScores = new HighScores(Arrays.asList(40, 20, 40, 30)); assertThat(highScores.personalTopThree()).isEqualTo(Arrays.asList(40, 40, 30)); @@ -50,6 +57,7 @@ public void shouldReturnPersonalTopThreeWhenThereIsATie() { @Test @Disabled("Remove to run test") + @DisplayName("Personal top when there are less than 3") public void shouldReturnPersonalTopWhenThereIsLessThanThreeScores() { HighScores highScores = new HighScores(Arrays.asList(30, 70)); assertThat(highScores.personalTopThree()).isEqualTo(Arrays.asList(70, 30)); @@ -57,6 +65,7 @@ public void shouldReturnPersonalTopWhenThereIsLessThanThreeScores() { @Test @Disabled("Remove to run test") + @DisplayName("Personal top when there is only one") public void shouldReturnPersonalTopWhenThereIsOnlyOneScore() { HighScores highScores = new HighScores(Arrays.asList(40)); assertThat(highScores.personalTopThree()).isEqualTo(Arrays.asList(40)); @@ -64,6 +73,7 @@ public void shouldReturnPersonalTopWhenThereIsOnlyOneScore() { @Test @Disabled("Remove to run test") + @DisplayName("Latest score after personal top scores") public void callingLatestAfterPersonalTopThree() { HighScores highScores = new HighScores(Arrays.asList(70, 50, 20, 30)); highScores.personalTopThree(); @@ -72,6 +82,7 @@ public void callingLatestAfterPersonalTopThree() { @Test @Disabled("Remove to run test") + @DisplayName("Scores after personal top scores") public void callingScoresAfterPersonalTopThree() { HighScores highScores = new HighScores(Arrays.asList(30, 50, 20, 70)); highScores.personalTopThree(); @@ -80,6 +91,7 @@ public void callingScoresAfterPersonalTopThree() { @Test @Disabled("Remove to run test") + @DisplayName("Latest score after personal best") public void callingLatestAfterPersonalBest() { HighScores highScores = new HighScores(Arrays.asList(20, 70, 15, 25, 30)); highScores.personalBest(); @@ -88,6 +100,7 @@ public void callingLatestAfterPersonalBest() { @Test @Disabled("Remove to run test") + @DisplayName("Scores after personal best") public void callingScoresAfterPersonalBest() { HighScores highScores = new HighScores(Arrays.asList(20, 70, 15, 25, 30)); highScores.personalBest(); diff --git a/exercises/practice/house/src/test/java/HouseTest.java b/exercises/practice/house/src/test/java/HouseTest.java index 08c3d3c2e..4841a9d14 100644 --- a/exercises/practice/house/src/test/java/HouseTest.java +++ b/exercises/practice/house/src/test/java/HouseTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.BeforeEach; import static org.assertj.core.api.Assertions.assertThat; @@ -14,6 +15,7 @@ public void setup() { } @Test + @DisplayName("verse one - the house that jack built") public void verseOne() { assertThat(house.verse(1)).isEqualTo( "This is the house that Jack built."); @@ -21,6 +23,7 @@ public void verseOne() { @Disabled("Remove to run test") @Test + @DisplayName("verse two - the malt that lay") public void verseTwo() { assertThat(house.verse(2)).isEqualTo( "This is the malt " + @@ -30,6 +33,7 @@ public void verseTwo() { @Disabled("Remove to run test") @Test + @DisplayName("verse three - the rat that ate") public void verseThree() { assertThat(house.verse(3)).isEqualTo( "This is the rat " + @@ -39,6 +43,7 @@ public void verseThree() { @Disabled("Remove to run test") @Test + @DisplayName("verse four - the cat that killed") public void verseFour() { assertThat(house.verse(4)).isEqualTo( "This is the cat " + @@ -49,6 +54,7 @@ public void verseFour() { @Disabled("Remove to run test") @Test + @DisplayName("verse five - the dog that worried") public void verseFive() { assertThat(house.verse(5)).isEqualTo( "This is the dog " + @@ -60,6 +66,7 @@ public void verseFive() { @Disabled("Remove to run test") @Test + @DisplayName("verse six - the cow with the crumpled horn") public void verseSix() { assertThat(house.verse(6)).isEqualTo( "This is the cow with the crumpled horn " + @@ -72,6 +79,7 @@ public void verseSix() { @Disabled("Remove to run test") @Test + @DisplayName("verse seven - the maiden all forlorn") public void verseSeven() { assertThat(house.verse(7)).isEqualTo( "This is the maiden all forlorn " + @@ -85,6 +93,7 @@ public void verseSeven() { @Disabled("Remove to run test") @Test + @DisplayName("verse eight - the man all tattered and torn") public void verseEight() { assertThat(house.verse(8)).isEqualTo( "This is the man all tattered and torn " + @@ -99,6 +108,7 @@ public void verseEight() { @Disabled("Remove to run test") @Test + @DisplayName("verse nine - the priest all shaven and shorn") public void verseNine() { assertThat(house.verse(9)).isEqualTo( "This is the priest all shaven and shorn " + @@ -114,6 +124,7 @@ public void verseNine() { @Disabled("Remove to run test") @Test + @DisplayName("verse 10 - the rooster that crowed in the morn") public void verse10() { assertThat(house.verse(10)).isEqualTo( "This is the rooster that crowed in the morn " + @@ -130,6 +141,7 @@ public void verse10() { @Disabled("Remove to run test") @Test + @DisplayName("verse 11 - the farmer sowing his corn") public void verse11() { assertThat(house.verse(11)).isEqualTo( "This is the farmer sowing his corn " + @@ -147,6 +159,7 @@ public void verse11() { @Disabled("Remove to run test") @Test + @DisplayName("verse 12 - the horse and the hound and the horn") public void verse12() { assertThat(house.verse(12)).isEqualTo( "This is the horse and the hound and the horn " + @@ -165,6 +178,7 @@ public void verse12() { @Disabled("Remove to run test") @Test + @DisplayName("multiple verses") public void multipleVerses() { int startVerse = 4; int endVerse = 8; @@ -204,6 +218,7 @@ public void multipleVerses() { @Disabled("Remove to run test") @Test + @DisplayName("full rhyme") public void wholeRhyme() { assertThat(house.sing()).isEqualTo( "This is the house that Jack built.\n" + From dfba4ad496e803cd3f999623848f11a9ccaabfb4 Mon Sep 17 00:00:00 2001 From: Bibek Parajuli <132128163+bibekparajuli123@users.noreply.github.com> Date: Thu, 11 Sep 2025 09:48:24 +0530 Subject: [PATCH 61/67] Add displayname annotations (#3003) * Add DisplayName annotations to pov * Add DisplayName annotations to prime-factors * Add DisplayName annotations to protein-translation * Add DisplayName annotations to proverb * Add DisplayName annotations to pythagorean-triplet * Add DisplayName annotations to queen-attack * Add DisplayName annotations to rail-fence-cipher * Add DisplayName annotations to raindrops * Add DisplayName annotations to rational-numbers * Add DisplayName annotations to react * Add DisplayName annotations to queen-attack [no important files changed] --- .../practice/pov/src/test/java/PovTest.java | 15 +++++++ .../test/java/PrimeFactorsCalculatorTest.java | 13 ++++++ .../src/test/java/ProteinTranslatorTest.java | 32 ++++++++++++++ .../proverb/src/test/java/ProverbTest.java | 7 ++++ .../src/test/java/PythagoreanTripletTest.java | 8 ++++ .../test/java/QueenAttackCalculatorTest.java | 17 ++++++++ .../src/test/java/RailFenceCipherTest.java | 7 ++++ .../src/test/java/RaindropConverterTest.java | 19 +++++++++ .../src/test/java/RationalTest.java | 42 +++++++++++++++++++ .../react/src/test/java/ReactTest.java | 15 +++++++ 10 files changed, 175 insertions(+) diff --git a/exercises/practice/pov/src/test/java/PovTest.java b/exercises/practice/pov/src/test/java/PovTest.java index 4eeea86a8..844a9be8f 100644 --- a/exercises/practice/pov/src/test/java/PovTest.java +++ b/exercises/practice/pov/src/test/java/PovTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.List; @@ -9,6 +10,7 @@ public class PovTest { @Test + @DisplayName("Results in the same tree if the input tree is a singleton") public void testFromPovGivenSingletonTree() { Tree tree = Tree.of("x"); Tree expected = Tree.of("x"); @@ -17,6 +19,7 @@ public void testFromPovGivenSingletonTree() { @Disabled("Remove to run test") @Test + @DisplayName("Can reroot a tree with a parent and one sibling") public void testFromPovGivenTreeWithParentAndOneSibling() { Tree tree = Tree.of("parent", List.of(Tree.of("x"), @@ -30,6 +33,7 @@ public void testFromPovGivenTreeWithParentAndOneSibling() { @Disabled("Remove to run test") @Test + @DisplayName("Can reroot a tree with a parent and many siblings") public void testFromPovGivenTreeWithParentAndManySibling() { Tree tree = Tree.of("parent", List.of(Tree.of("x"), @@ -48,6 +52,7 @@ public void testFromPovGivenTreeWithParentAndManySibling() { @Disabled("Remove to run test") @Test + @DisplayName("an reroot a tree with new root deeply nested in the tree") public void testFromPovGivenTreeWithNewRootDeeplyNested() { Tree tree = Tree.of("level-0", List.of(Tree.of("level-1", @@ -66,6 +71,7 @@ public void testFromPovGivenTreeWithNewRootDeeplyNested() { @Disabled("Remove to run test") @Test + @DisplayName("Moves children of the new root to same level as former parent") public void testFromPovGivenMovesChildrenOfNewRootToSameLevelAsFormerParent() { Tree tree = Tree.of("parent", List.of(Tree.of("x", @@ -82,6 +88,7 @@ public void testFromPovGivenMovesChildrenOfNewRootToSameLevelAsFormerParent() { @Disabled("Remove to run test") @Test + @DisplayName("Can reroot a complex tree with cousins") public void testFromPovGivenComplexTreeWithCousins() { Tree tree = Tree.of("grandparent", List.of(Tree.of("parent", @@ -110,6 +117,7 @@ public void testFromPovGivenComplexTreeWithCousins() { @Disabled("Remove to run test") @Test + @DisplayName("Errors if target does not exist in a singleton tree") public void testFromPovGivenNonExistentTargetInSingletonTree() { Tree tree = Tree.of("x"); assertThatExceptionOfType(UnsupportedOperationException.class) @@ -119,6 +127,7 @@ public void testFromPovGivenNonExistentTargetInSingletonTree() { @Disabled("Remove to run test") @Test + @DisplayName("Errors if target does not exist in a larger tree") public void testFromPovGivenNonExistentTargetInLargeTree() { Tree tree = Tree.of("parent", List.of(Tree.of("x", @@ -134,6 +143,7 @@ public void testFromPovGivenNonExistentTargetInLargeTree() { @Disabled("Remove to run test") @Test + @DisplayName("Can find path to parent") public void testPathToCanFindPathToParent() { Tree tree = Tree.of("parent", List.of(Tree.of("x"), @@ -158,6 +168,7 @@ public void testPathToCanFindPathToSibling() { @Disabled("Remove to run test") @Test + @DisplayName("Can find path to cousin") public void testPathToCanFindPathToCousin() { Tree tree = Tree.of("grandparent", List.of(Tree.of("parent", @@ -176,6 +187,7 @@ public void testPathToCanFindPathToCousin() { @Disabled("Remove to run test") @Test + @DisplayName("Can find path not involving root") public void testPathToCanFindPathNotEnvolvingRoot() { Tree tree = Tree.of("grandparent", List.of(Tree.of("parent", @@ -189,6 +201,7 @@ public void testPathToCanFindPathNotEnvolvingRoot() { @Disabled("Remove to run test") @Test + @DisplayName("Can find path from nodes other than x") public void testPathToCanFindPathFromNodesOtherThanX() { Tree tree = Tree.of("parent", List.of(Tree.of("a"), @@ -202,6 +215,7 @@ public void testPathToCanFindPathFromNodesOtherThanX() { @Disabled("Remove to run test") @Test + @DisplayName("Errors if destination does not exist") public void testPathWhenDestinationDoesNotExist() { Tree tree = Tree.of("parent", List.of(Tree.of("x", @@ -217,6 +231,7 @@ public void testPathWhenDestinationDoesNotExist() { @Disabled("Remove to run test") @Test + @DisplayName("Errors if source does not exist") public void testPathWhenSourceDoesNotExist() { Tree tree = Tree.of("parent", List.of(Tree.of("x", diff --git a/exercises/practice/prime-factors/src/test/java/PrimeFactorsCalculatorTest.java b/exercises/practice/prime-factors/src/test/java/PrimeFactorsCalculatorTest.java index 62cc0e3d1..d6f48a710 100644 --- a/exercises/practice/prime-factors/src/test/java/PrimeFactorsCalculatorTest.java +++ b/exercises/practice/prime-factors/src/test/java/PrimeFactorsCalculatorTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,72 +15,84 @@ public void setUp() { } @Test + @DisplayName("no factors") public void testNoFactors() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(1L)).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("prime number") public void testPrimeNumber() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(2L)).containsExactly(2L); } @Disabled("Remove to run test") @Test + @DisplayName("another prime number") public void testAnotherPrimeNumber() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(3L)).containsExactly(3L); } @Disabled("Remove to run test") @Test + @DisplayName("square of a prime") public void testSquareOfAPrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(9L)).containsExactly(3L, 3L); } @Disabled("Remove to run test") @Test + @DisplayName("product of first prime") public void testProductOfFirstPrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(4L)).containsExactly(2L, 2L); } @Disabled("Remove to run test") @Test + @DisplayName("cube of a prime") public void testCubeOfAPrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(8L)).containsExactly(2L, 2L, 2L); } @Disabled("Remove to run test") @Test + @DisplayName("product of second prime") public void testProductOfSecondPrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(625L)).containsExactly(5L, 5L, 5L, 5L); } @Disabled("Remove to run test") @Test + @DisplayName("product of third prime") public void testProductOfThirdPrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(27L)).containsExactly(3L, 3L, 3L); } @Disabled("Remove to run test") @Test + @DisplayName("product of first and second prime") public void testProductOfFirstAndSecondPrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(6L)).containsExactly(2L, 3L); } @Disabled("Remove to run test") @Test + @DisplayName("product of primes and non-primes") public void testProductOfPrimesAndNonPrimes() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(12L)).containsExactly(2L, 2L, 3L); } @Disabled("Remove to run test") @Test + @DisplayName("product of primes") public void testProductOfPrimes() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(901255L)).containsExactly(5L, 17L, 23L, 461L); } @Disabled("Remove to run test") @Test + @DisplayName("factors include a large prime") public void testFactorsIncludingALargePrime() { assertThat(primeFactorsCalculator.calculatePrimeFactorsOf(93819012551L)).containsExactly(11L, 9539L, 894119L); } diff --git a/exercises/practice/protein-translation/src/test/java/ProteinTranslatorTest.java b/exercises/practice/protein-translation/src/test/java/ProteinTranslatorTest.java index 8da14515b..0eced2521 100644 --- a/exercises/practice/protein-translation/src/test/java/ProteinTranslatorTest.java +++ b/exercises/practice/protein-translation/src/test/java/ProteinTranslatorTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -15,126 +16,147 @@ public void setUp() { } @Test + @DisplayName("Empty RNA sequence results in no proteins") public void testEmptyRnaSequenceResultInNoproteins() { assertThat(proteinTranslator.translate("")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("Methionine RNA sequence") public void testMethionineRnaSequence() { assertThat(proteinTranslator.translate("AUG")).containsExactly("Methionine"); } @Disabled("Remove to run test") @Test + @DisplayName("Phenylalanine RNA sequence 1") public void testPhenylalanineRnaSequence1() { assertThat(proteinTranslator.translate("UUU")).containsExactly("Phenylalanine"); } @Disabled("Remove to run test") @Test + @DisplayName("Phenylalanine RNA sequence 2") public void testPhenylalanineRnaSequence2() { assertThat(proteinTranslator.translate("UUC")).containsExactly("Phenylalanine"); } @Disabled("Remove to run test") @Test + @DisplayName("Leucine RNA sequence 1") public void testLeucineRnaSequence1() { assertThat(proteinTranslator.translate("UUA")).containsExactly("Leucine"); } @Disabled("Remove to run test") @Test + @DisplayName("Leucine RNA sequence 2") public void testLeucineRnaSequence2() { assertThat(proteinTranslator.translate("UUG")).containsExactly("Leucine"); } @Disabled("Remove to run test") @Test + @DisplayName("Serine RNA sequence 1") public void testSerineRnaSequence1() { assertThat(proteinTranslator.translate("UCU")).containsExactly("Serine"); } @Disabled("Remove to run test") @Test + @DisplayName("Serine RNA sequence 2") public void testSerineRnaSequence2() { assertThat(proteinTranslator.translate("UCC")).containsExactly("Serine"); } @Disabled("Remove to run test") @Test + @DisplayName("Serine RNA sequence 3") public void testSerineRnaSequence3() { assertThat(proteinTranslator.translate("UCA")).containsExactly("Serine"); } @Disabled("Remove to run test") @Test + @DisplayName("Serine RNA sequence 4") public void testSerineRnaSequence4() { assertThat(proteinTranslator.translate("UCG")).containsExactly("Serine"); } @Disabled("Remove to run test") @Test + @DisplayName("Tyrosine RNA sequence 1") public void testTyrosineRnaSequence1() { assertThat(proteinTranslator.translate("UAU")).containsExactly("Tyrosine"); } @Disabled("Remove to run test") @Test + @DisplayName("Tyrosine RNA sequence 2") public void testTyrosineRnaSequence2() { assertThat(proteinTranslator.translate("UAC")).containsExactly("Tyrosine"); } @Disabled("Remove to run test") @Test + @DisplayName("Cysteine RNA sequence 1") public void testCysteineRnaSequence1() { assertThat(proteinTranslator.translate("UGU")).containsExactly("Cysteine"); } @Disabled("Remove to run test") @Test + @DisplayName("Cysteine RNA sequence 2") public void testCysteineRnaSequence2() { assertThat(proteinTranslator.translate("UGC")).containsExactly("Cysteine"); } @Disabled("Remove to run test") @Test + @DisplayName("Tryptophan RNA sequence") public void testTryptophanRnaSequence1() { assertThat(proteinTranslator.translate("UGG")).containsExactly("Tryptophan"); } @Disabled("Remove to run test") @Test + @DisplayName("STOP codon RNA sequence 1") public void testStopRnaSequence1() { assertThat(proteinTranslator.translate("UAA")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("STOP codon RNA sequence 2") public void testStopRnaSequence2() { assertThat(proteinTranslator.translate("UAG")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("STOP codon RNA sequence 3") public void testStopRnaSequence3() { assertThat(proteinTranslator.translate("UGA")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("Sequence of two protein codons translates into proteins") public void testSequenceOfTwoProteinCodonsTranslatesIntoProteins() { assertThat(proteinTranslator.translate("UUUUUU")).containsExactly("Phenylalanine", "Phenylalanine"); } @Disabled("Remove to run test") @Test + @DisplayName("Sequence of two different protein codons translates into proteins") public void testSequenceOfTwoDifferentProteinCodonsTranslatesIntoProteins() { assertThat(proteinTranslator.translate("UUAUUG")).containsExactly("Leucine", "Leucine"); } @Disabled("Remove to run test") @Test + @DisplayName("Translate RNA strand into correct protein list") public void testTranslationOfRnaToProteinList() { assertThat(proteinTranslator.translate("AUGUUUUGG")) .containsExactly("Methionine", "Phenylalanine", "Tryptophan"); @@ -142,30 +164,35 @@ public void testTranslationOfRnaToProteinList() { @Disabled("Remove to run test") @Test + @DisplayName("Translation stops if STOP codon at beginning of sequence") public void testTranslationStopsIfStopCodonAtBeginning() { assertThat(proteinTranslator.translate("UAGUGG")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("Translation stops if STOP codon at end of two-codon sequence") public void testTranslationStopsIfStopCodonAtEnd1() { assertThat(proteinTranslator.translate("UGGUAG")).containsExactly("Tryptophan"); } @Disabled("Remove to run test") @Test + @DisplayName("Translation stops if STOP codon at end of three-codon sequence") public void testTranslationStopsIfStopCodonAtEnd2() { assertThat(proteinTranslator.translate("AUGUUUUAA")).containsExactly("Methionine", "Phenylalanine"); } @Disabled("Remove to run test") @Test + @DisplayName("Translation stops if STOP codon in middle of three-codon sequence") public void testTranslationStopsIfStopCodonInMiddle1() { assertThat(proteinTranslator.translate("UGGUAGUGG")).containsExactly("Tryptophan"); } @Disabled("Remove to run test") @Test + @DisplayName("Translation stops if STOP codon in middle of six-codon sequence") public void testTranslationStopsIfStopCodonInMiddle2() { assertThat(proteinTranslator.translate("UGGUGUUAUUAAUGGUUU")) .containsExactly("Tryptophan", "Cysteine", "Tyrosine"); @@ -173,6 +200,7 @@ public void testTranslationStopsIfStopCodonInMiddle2() { @Disabled("Remove to run test") @Test + @DisplayName("Sequence of two non-STOP codons does not translate to a STOP codon") public void testSequenceOfTwoNonStopCodonsDoNotTranslateToAStopCodon() { assertThat(proteinTranslator.translate("AUGAUG")) .containsExactly("Methionine", "Methionine"); @@ -180,6 +208,7 @@ public void testSequenceOfTwoNonStopCodonsDoNotTranslateToAStopCodon() { @Disabled("Remove to run test") @Test + @DisplayName("Non-existing codon can't translate") public void testNonExistingCodonCantTranslate() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> proteinTranslator.translate("AAA")) @@ -188,6 +217,7 @@ public void testNonExistingCodonCantTranslate() { @Disabled("Remove to run test") @Test + @DisplayName("Unknown amino acids, not part of a codon, can't translate") public void testUnknownAminoAcidsNotPartOfACodonCantTranslate() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> proteinTranslator.translate("XYZ")) @@ -196,6 +226,7 @@ public void testUnknownAminoAcidsNotPartOfACodonCantTranslate() { @Disabled("Remove to run test") @Test + @DisplayName("Incomplete RNA sequence can't translate") public void testIncompleteRnaSequenceCantTranslate() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> proteinTranslator.translate("AUGU")) @@ -204,6 +235,7 @@ public void testIncompleteRnaSequenceCantTranslate() { @Disabled("Remove to run test") @Test + @DisplayName("Incomplete RNA sequence can translate if valid until a STOP codon") public void testIncompleteRnaSequenceCanTranslateIfValidUntilAStopCodon() { assertThat(proteinTranslator.translate("UUCUUCUAAUGGU")).containsExactly("Phenylalanine", "Phenylalanine"); } diff --git a/exercises/practice/proverb/src/test/java/ProverbTest.java b/exercises/practice/proverb/src/test/java/ProverbTest.java index 739911e64..854a34b4b 100644 --- a/exercises/practice/proverb/src/test/java/ProverbTest.java +++ b/exercises/practice/proverb/src/test/java/ProverbTest.java @@ -1,11 +1,13 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class ProverbTest { @Test + @DisplayName("zero pieces") public void zeroWordsAreGiven() { String[] words = new String[0]; @@ -14,6 +16,7 @@ public void zeroWordsAreGiven() { @Disabled("Remove to run test") @Test + @DisplayName("one piece") public void singlePieceOfProverb() { String[] words = new String[]{"nail"}; @@ -23,6 +26,7 @@ public void singlePieceOfProverb() { @Disabled("Remove to run test") @Test + @DisplayName("two pieces") public void twoPiecesOfProverb() { String[] words = new String[]{"nail", "shoe"}; @@ -34,6 +38,7 @@ public void twoPiecesOfProverb() { @Disabled("Remove to run test") @Test + @DisplayName("three pieces") public void shortChainOfConsequences() { String[] words = new String[]{"nail", "shoe", "horse"}; @@ -46,6 +51,7 @@ public void shortChainOfConsequences() { @Disabled("Remove to run test") @Test + @DisplayName("full proverb") public void fullProverb() { String[] words = new String[]{"nail", "shoe", "horse", "rider", "message", "battle", "kingdom"}; @@ -62,6 +68,7 @@ public void fullProverb() { @Disabled("Remove to run test") @Test + @DisplayName("four pieces modernized") public void fourPiecesModernizedProverb() { String[] words = new String[]{"pin", "gun", "soldier", "battle"}; diff --git a/exercises/practice/pythagorean-triplet/src/test/java/PythagoreanTripletTest.java b/exercises/practice/pythagorean-triplet/src/test/java/PythagoreanTripletTest.java index 83b2705c5..50361fb3a 100644 --- a/exercises/practice/pythagorean-triplet/src/test/java/PythagoreanTripletTest.java +++ b/exercises/practice/pythagorean-triplet/src/test/java/PythagoreanTripletTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -10,6 +11,7 @@ public class PythagoreanTripletTest { @Test + @DisplayName("triplets whose sum is 12") public void tripletsWhoseSumIs12() { List actual = PythagoreanTriplet @@ -23,6 +25,7 @@ public void tripletsWhoseSumIs12() { @Disabled("Remove to run test") @Test + @DisplayName("triplets whose sum is 108") public void tripletsWhoseSumIs108() { List actual = PythagoreanTriplet @@ -36,6 +39,7 @@ public void tripletsWhoseSumIs108() { @Disabled("Remove to run test") @Test + @DisplayName("triplets whose sum is 1000") public void tripletsWhoseSumIs1000() { List actual = PythagoreanTriplet @@ -50,6 +54,7 @@ public void tripletsWhoseSumIs1000() { @Disabled("Remove to run test") @Test + @DisplayName("no matching triplets for 1001") public void tripletsWhoseSumIs1001() { List actual = PythagoreanTriplet @@ -62,6 +67,7 @@ public void tripletsWhoseSumIs1001() { @Disabled("Remove to run test") @Test + @DisplayName("returns all matching triplets") public void tripletsWhoseSumIs90() { List actual = PythagoreanTriplet @@ -77,6 +83,7 @@ public void tripletsWhoseSumIs90() { @Disabled("Remove to run test") @Test + @DisplayName("several matching triplets") public void tripletsWhoseSumIs840() { List actual = PythagoreanTriplet @@ -117,6 +124,7 @@ public void tripletsWhoseSumIs840WithFactorsLessThanOrEqualTo370() { @Disabled("Remove to run test") @Test + @DisplayName("triplets for large number") public void tripletsWhoseSumIs30000() { List actual = PythagoreanTriplet diff --git a/exercises/practice/queen-attack/src/test/java/QueenAttackCalculatorTest.java b/exercises/practice/queen-attack/src/test/java/QueenAttackCalculatorTest.java index e6b65f5e6..c4f4f70d2 100644 --- a/exercises/practice/queen-attack/src/test/java/QueenAttackCalculatorTest.java +++ b/exercises/practice/queen-attack/src/test/java/QueenAttackCalculatorTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -8,12 +9,14 @@ public class QueenAttackCalculatorTest { @Test + @DisplayName("queen with a valid position") public void testCreateQueenWithAValidPosition() { new Queen(2, 2); } @Disabled("Remove to run test") @Test + @DisplayName("queen must have positive row") public void testCreateQueenMustHavePositiveRow() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Queen(-2, 2)) @@ -22,6 +25,7 @@ public void testCreateQueenMustHavePositiveRow() { @Disabled("Remove to run test") @Test + @DisplayName("queen must have row on board") public void testCreateQueenMustHaveRowOnBoard() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Queen(8, 4)) @@ -30,6 +34,7 @@ public void testCreateQueenMustHaveRowOnBoard() { @Disabled("Remove to run test") @Test + @DisplayName("queen must have positive column") public void testCreateQueenMustHavePositiveColumn() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Queen(2, -2)) @@ -38,6 +43,7 @@ public void testCreateQueenMustHavePositiveColumn() { @Disabled("Remove to run test") @Test + @DisplayName("queen must have column on board") public void testCreateQueenMustHaveColumnOnBoard() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new Queen(4, 8)) @@ -46,6 +52,7 @@ public void testCreateQueenMustHaveColumnOnBoard() { @Disabled("Remove to run test") @Test + @DisplayName("cannot attack") public void testQueensCannotAttack() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(2, 4), new Queen(6, 6)); @@ -55,6 +62,7 @@ public void testQueensCannotAttack() { @Disabled("Remove to run test") @Test + @DisplayName("can attack on same row") public void testQueensCanAttackOnTheSameRow() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(2, 4), new Queen(2, 6)); @@ -64,6 +72,7 @@ public void testQueensCanAttackOnTheSameRow() { @Disabled("Remove to run test") @Test + @DisplayName("can attack on same column") public void testQueensCanAttackOnTheSameColumn() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(4, 5), new Queen(2, 5)); @@ -73,6 +82,7 @@ public void testQueensCanAttackOnTheSameColumn() { @Disabled("Remove to run test") @Test + @DisplayName("can attack on first diagonal") public void testQueensCanAttackOnFirstDiagonal() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(2, 2), new Queen(0, 4)); @@ -82,6 +92,7 @@ public void testQueensCanAttackOnFirstDiagonal() { @Disabled("Remove to run test") @Test + @DisplayName("can attack on second diagonal") public void testQueensCanAttackOnSecondDiagonal() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(2, 2), new Queen(3, 1)); @@ -91,6 +102,7 @@ public void testQueensCanAttackOnSecondDiagonal() { @Disabled("Remove to run test") @Test + @DisplayName("can attack on third diagonal") public void testQueensCanAttackOnThirdDiagonal() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(2, 2), new Queen(1, 1)); @@ -100,6 +112,7 @@ public void testQueensCanAttackOnThirdDiagonal() { @Disabled("Remove to run test") @Test + @DisplayName("can attack on fourth diagonal") public void testQueensCanAttackOnFourthDiagonal() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(1, 7), new Queen(0, 6)); @@ -109,6 +122,10 @@ public void testQueensCanAttackOnFourthDiagonal() { @Disabled("Remove to run test") @Test + @DisplayName( + "cannot attack if falling diagonals are only the same " + + "when reflected across the longest falling diagonal" + ) public void testQueenCannotAttackIfFallingDiagonalsAreOnlyTheSameWhenReflectedAcrossTheLongestFallingDiagonal() { QueenAttackCalculator calculator = new QueenAttackCalculator(new Queen(4, 1), new Queen(2, 5)); diff --git a/exercises/practice/rail-fence-cipher/src/test/java/RailFenceCipherTest.java b/exercises/practice/rail-fence-cipher/src/test/java/RailFenceCipherTest.java index da51a090d..c18e6dd32 100644 --- a/exercises/practice/rail-fence-cipher/src/test/java/RailFenceCipherTest.java +++ b/exercises/practice/rail-fence-cipher/src/test/java/RailFenceCipherTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -8,6 +9,7 @@ public class RailFenceCipherTest { private RailFenceCipher railFenceCipher; @Test + @DisplayName("encode with two rails") public void encodeWithTwoRails() { railFenceCipher = new RailFenceCipher(2); assertThat(railFenceCipher.getEncryptedData("XOXOXOXOXOXOXOXOXO")) @@ -16,6 +18,7 @@ public void encodeWithTwoRails() { @Disabled("Remove to run test") @Test + @DisplayName("encode with three rails") public void encodeWithThreeRails() { railFenceCipher = new RailFenceCipher(3); assertThat(railFenceCipher.getEncryptedData("WEAREDISCOVEREDFLEEATONCE")) @@ -24,6 +27,7 @@ public void encodeWithThreeRails() { @Disabled("Remove to run test") @Test + @DisplayName("encode with ending in the middle") public void encodeWithEndingInTheMiddle() { railFenceCipher = new RailFenceCipher(4); assertThat(railFenceCipher.getEncryptedData("EXERCISES")) @@ -32,6 +36,7 @@ public void encodeWithEndingInTheMiddle() { @Disabled("Remove to run test") @Test + @DisplayName("decode with three rails") public void decodeWithThreeRails() { railFenceCipher = new RailFenceCipher(3); assertThat(railFenceCipher.getDecryptedData("TEITELHDVLSNHDTISEIIEA")) @@ -40,6 +45,7 @@ public void decodeWithThreeRails() { @Disabled("Remove to run test") @Test + @DisplayName("decode with five rails") public void decodeWithFiveRails() { railFenceCipher = new RailFenceCipher(5); assertThat(railFenceCipher.getDecryptedData("EIEXMSMESAORIWSCE")) @@ -48,6 +54,7 @@ public void decodeWithFiveRails() { @Disabled("Remove to run test") @Test + @DisplayName("decode with six rails") public void decodeWithSixRails() { railFenceCipher = new RailFenceCipher(6); assertThat(railFenceCipher.getDecryptedData("133714114238148966225439541018335470986172518171757571896261")) diff --git a/exercises/practice/raindrops/src/test/java/RaindropConverterTest.java b/exercises/practice/raindrops/src/test/java/RaindropConverterTest.java index bd16e9ef5..6499f2144 100644 --- a/exercises/practice/raindrops/src/test/java/RaindropConverterTest.java +++ b/exercises/practice/raindrops/src/test/java/RaindropConverterTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -8,108 +9,126 @@ public class RaindropConverterTest { private RaindropConverter raindropConverter = new RaindropConverter(); @Test + @DisplayName("the sound for 1 is 1") public void soundFor1Is1() { assertThat(raindropConverter.convert(1)).isEqualTo("1"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 3 is Pling") public void soundFor3IsPling() { assertThat(raindropConverter.convert(3)).isEqualTo("Pling"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 5 is Plang") public void soundFor5IsPlang() { assertThat(raindropConverter.convert(5)).isEqualTo("Plang"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 7 is Plong") public void soundFor7IsPlong() { assertThat(raindropConverter.convert(7)).isEqualTo("Plong"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 6 is Pling as it has a factor 3") public void soundFor6IsPlingAsItHasFactor3() { assertThat(raindropConverter.convert(6)).isEqualTo("Pling"); } @Disabled("Remove to run test") @Test + @DisplayName("2 to the power 3 does not make a raindrop sound as 3 is the exponent not the base") public void noSoundFor2Cubed() { assertThat(raindropConverter.convert(8)).isEqualTo("8"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 9 is Pling as it has a factor 3") public void soundFor9IsPlingAsItHasFactor3() { assertThat(raindropConverter.convert(9)).isEqualTo("Pling"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 10 is Plang as it has a factor 5") public void soundFor10IsPlangAsItHasFactor5() { assertThat(raindropConverter.convert(10)).isEqualTo("Plang"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 14 is Plong as it has a factor of 7") public void soundFor14IsPlongAsItHasFactor7() { assertThat(raindropConverter.convert(14)).isEqualTo("Plong"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 15 is PlingPlang as it has factors 3 and 5") public void soundFor15IsPlingPlangAsItHasFactors3And5() { assertThat(raindropConverter.convert(15)).isEqualTo("PlingPlang"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 21 is PlingPlong as it has factors 3 and 7") public void soundFor21IsPlingPlongAsItHasFactors3And7() { assertThat(raindropConverter.convert(21)).isEqualTo("PlingPlong"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 25 is Plang as it has a factor 5") public void soundFor25IsPlangAsItHasFactor5() { assertThat(raindropConverter.convert(25)).isEqualTo("Plang"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 27 is Pling as it has a factor 3") public void soundFor27IsPlingAsItHasFactor3() { assertThat(raindropConverter.convert(27)).isEqualTo("Pling"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 35 is PlangPlong as it has factors 5 and 7") public void soundFor35IsPlangPlongAsItHasFactors5And7() { assertThat(raindropConverter.convert(35)).isEqualTo("PlangPlong"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 49 is Plong as it has a factor 7") public void soundFor49IsPlongAsItHasFactor7() { assertThat(raindropConverter.convert(49)).isEqualTo("Plong"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 52 is 52") public void noSoundFor52() { assertThat(raindropConverter.convert(52)).isEqualTo("52"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 105 is PlingPlangPlong as it has factors 3, 5 and 7") public void soundFor105IsPlingPlangPlongAsItHasFactors3And5And7() { assertThat(raindropConverter.convert(105)).isEqualTo("PlingPlangPlong"); } @Disabled("Remove to run test") @Test + @DisplayName("the sound for 3125 is Plang as it has a factor 5") public void soundFor3125IsPlangAsItHasFactor5() { assertThat(raindropConverter.convert(3125)).isEqualTo("Plang"); } diff --git a/exercises/practice/rational-numbers/src/test/java/RationalTest.java b/exercises/practice/rational-numbers/src/test/java/RationalTest.java index 8a26c474a..91f8b3e49 100644 --- a/exercises/practice/rational-numbers/src/test/java/RationalTest.java +++ b/exercises/practice/rational-numbers/src/test/java/RationalTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -17,6 +18,7 @@ private void assertDoublesEqual(double x, double y) { // Tests @Test + @DisplayName("Add two positive rational numbers") public void testAddTwoPositiveRationalNumbers() { Rational expected = new Rational(7, 6); Rational actual = new Rational(1, 2).add(new Rational(2, 3)); @@ -25,6 +27,7 @@ public void testAddTwoPositiveRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Add a positive rational number and a negative rational number") public void testAddAPositiveRationalNumberAndANegativeRationalNumber() { Rational expected = new Rational(-1, 6); Rational actual = new Rational(1, 2).add(new Rational(-2, 3)); @@ -33,6 +36,7 @@ public void testAddAPositiveRationalNumberAndANegativeRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Add two negative rational numbers") public void testAddTwoNegativeRationalNumbers() { Rational expected = new Rational(-7, 6); Rational actual = new Rational(-1, 2).add(new Rational(-2, 3)); @@ -41,6 +45,7 @@ public void testAddTwoNegativeRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Add a rational number to its additive inverse") public void testAddARationalNumberToItsAdditiveInverse() { Rational expected = new Rational(0, 1); Rational actual = new Rational(1, 2).add(new Rational(-1, 2)); @@ -49,6 +54,7 @@ public void testAddARationalNumberToItsAdditiveInverse() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract two positive rational numbers") public void testSubtractTwoPositiveRationalNumbers() { Rational expected = new Rational(-1, 6); Rational actual = new Rational(1, 2).subtract(new Rational(2, 3)); @@ -57,6 +63,7 @@ public void testSubtractTwoPositiveRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract a positive rational number and a negative rational number") public void testSubtractAPositiveRationalNumberAndANegativeRationalNumber() { Rational expected = new Rational(7, 6); Rational actual = new Rational(1, 2).subtract(new Rational(-2, 3)); @@ -65,6 +72,7 @@ public void testSubtractAPositiveRationalNumberAndANegativeRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract two negative rational numbers") public void testSubtractTwoNegativeRationalNumbers() { Rational expected = new Rational(1, 6); Rational actual = new Rational(-1, 2).subtract(new Rational(-2, 3)); @@ -73,6 +81,7 @@ public void testSubtractTwoNegativeRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Subtract a rational number from itself") public void testSubtractARationalNumberFromItself() { Rational expected = new Rational(0, 1); Rational actual = new Rational(1, 2).subtract(new Rational(1, 2)); @@ -81,6 +90,7 @@ public void testSubtractARationalNumberFromItself() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply two positive rational numbers") public void testMultiplyTwoPositiveRationalNumbers() { Rational expected = new Rational(1, 3); Rational actual = new Rational(1, 2).multiply(new Rational(2, 3)); @@ -89,6 +99,7 @@ public void testMultiplyTwoPositiveRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply a negative rational number by a positive rational number") public void testMultiplyANegativeRationalNumberByAPositiveRationalNumber() { Rational expected = new Rational(-1, 3); Rational actual = new Rational(-1, 2).multiply(new Rational(2, 3)); @@ -97,6 +108,7 @@ public void testMultiplyANegativeRationalNumberByAPositiveRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply two negative rational numbers") public void testMultiplyTwoNegativeRationalNumbers() { Rational expected = new Rational(1, 3); Rational actual = new Rational(-1, 2).multiply(new Rational(-2, 3)); @@ -105,6 +117,7 @@ public void testMultiplyTwoNegativeRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply a rational number by its reciprocal") public void testMultiplyARationalNumberByItsReciprocal() { Rational expected = new Rational(1, 1); Rational actual = new Rational(1, 2).multiply(new Rational(2, 1)); @@ -113,6 +126,7 @@ public void testMultiplyARationalNumberByItsReciprocal() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply a rational number by 1") public void testMultiplyARationalNumberByOne() { Rational expected = new Rational(1, 2); Rational actual = new Rational(1, 2).multiply(new Rational(1, 1)); @@ -121,6 +135,7 @@ public void testMultiplyARationalNumberByOne() { @Disabled("Remove to run test") @Test + @DisplayName("Multiply a rational number by 0") public void testMultiplyARationalNumberByZero() { Rational expected = new Rational(0, 1); Rational actual = new Rational(1, 2).multiply(new Rational(0, 1)); @@ -129,6 +144,7 @@ public void testMultiplyARationalNumberByZero() { @Disabled("Remove to run test") @Test + @DisplayName("Divide two positive rational numbers") public void testDivideTwoPositiveRationalNumbers() { Rational expected = new Rational(3, 4); Rational actual = new Rational(1, 2).divide(new Rational(2, 3)); @@ -137,6 +153,7 @@ public void testDivideTwoPositiveRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Divide a positive rational number by a negative rational number") public void testDivideAPositiveRationalNumberByANegativeRationalNumber() { Rational expected = new Rational(-3, 4); Rational actual = new Rational(1, 2).divide(new Rational(-2, 3)); @@ -145,6 +162,7 @@ public void testDivideAPositiveRationalNumberByANegativeRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Divide two negative rational numbers") public void testDivideTwoNegativeRationalNumbers() { Rational expected = new Rational(3, 4); Rational actual = new Rational(-1, 2).divide(new Rational(-2, 3)); @@ -153,6 +171,7 @@ public void testDivideTwoNegativeRationalNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Divide a rational number by 1") public void testDivideARationalNumberByOne() { Rational expected = new Rational(1, 2); Rational actual = new Rational(1, 2).divide(new Rational(1, 1)); @@ -161,6 +180,7 @@ public void testDivideARationalNumberByOne() { @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a positive rational number") public void testAbsoluteValueOfAPositiveRationalNumber() { Rational expected = new Rational(1, 2); Rational actual = new Rational(1, 2).abs(); @@ -169,6 +189,7 @@ public void testAbsoluteValueOfAPositiveRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a positive rational number with negative numerator and denominator") public void testAbsoluteValueOfAPositiveRationalNumberWithNegativeNumeratorAndDenominator() { Rational expected = new Rational(1, 2); Rational actual = new Rational(-1, -2).abs(); @@ -177,6 +198,7 @@ public void testAbsoluteValueOfAPositiveRationalNumberWithNegativeNumeratorAndDe @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a negative rational number") public void testAbsoluteValueOfANegativeRationalNumber() { Rational expected = new Rational(1, 2); Rational actual = new Rational(-1, 2).abs(); @@ -185,6 +207,7 @@ public void testAbsoluteValueOfANegativeRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("\"Absolute value of a negative rational number with negative denominator") public void testAbsoluteValueOfANegativeRationalNumberWithNegativeDenominator() { Rational expected = new Rational(1, 2); Rational actual = new Rational(1, -2).abs(); @@ -193,6 +216,7 @@ public void testAbsoluteValueOfANegativeRationalNumberWithNegativeDenominator() @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of zero") public void testAbsoluteValueOfZero() { Rational expected = new Rational(0, 1); Rational actual = new Rational(0, 1).abs(); @@ -201,6 +225,7 @@ public void testAbsoluteValueOfZero() { @Disabled("Remove to run test") @Test + @DisplayName("Absolute value of a rational number is reduced to lowest terms") public void testAbsoluteValueOfARationalNumberIsReducedToLowestTerms() { Rational expected = new Rational(1, 2); Rational actual = new Rational(2, 4).abs(); @@ -209,6 +234,7 @@ public void testAbsoluteValueOfARationalNumberIsReducedToLowestTerms() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a positive rational number to a positive integer power") public void testRaiseAPositiveRationalNumberToAPositiveIntegerPower() { Rational expected = new Rational(1, 8); Rational actual = new Rational(1, 2).pow(3); @@ -217,6 +243,7 @@ public void testRaiseAPositiveRationalNumberToAPositiveIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a negative rational number to a positive integer power") public void testRaiseANegativeRationalNumberToAPositiveIntegerPower() { Rational expected = new Rational(-1, 8); Rational actual = new Rational(-1, 2).pow(3); @@ -225,6 +252,7 @@ public void testRaiseANegativeRationalNumberToAPositiveIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a positive rational number to a negative integer power") public void testRaiseAPositiveRationalNumberToANegativeIntegerPower() { Rational expected = new Rational(25, 9); Rational actual = new Rational(3, 5).pow(-2); @@ -233,6 +261,7 @@ public void testRaiseAPositiveRationalNumberToANegativeIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("\"Raise a negative rational number to an even negative integer power") public void testRaiseANegativeRationalNumberToAnEvenNegativeIntegerPower() { Rational expected = new Rational(25, 9); Rational actual = new Rational(-3, 5).pow(-2); @@ -241,6 +270,7 @@ public void testRaiseANegativeRationalNumberToAnEvenNegativeIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a negative rational number to an odd negative integer power") public void testRaiseANegativeRationalNumberToAnOddNegativeIntegerPower() { Rational expected = new Rational(-125, 27); Rational actual = new Rational(-3, 5).pow(-3); @@ -249,6 +279,7 @@ public void testRaiseANegativeRationalNumberToAnOddNegativeIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("Raise zero to an integer power") public void testRaiseZeroToAnIntegerPower() { Rational expected = new Rational(0, 1); Rational actual = new Rational(0, 1).pow(5); @@ -257,6 +288,7 @@ public void testRaiseZeroToAnIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("Raise one to an integer power") public void testRaiseOneToAnIntegerPower() { Rational expected = new Rational(1, 1); Rational actual = new Rational(1, 1).pow(4); @@ -265,6 +297,7 @@ public void testRaiseOneToAnIntegerPower() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a positive rational number to the power of zero") public void testRaiseAPositiveRationalNumberToThePowerOfZero() { Rational expected = new Rational(1, 1); Rational actual = new Rational(-1, 2).pow(0); @@ -273,6 +306,7 @@ public void testRaiseAPositiveRationalNumberToThePowerOfZero() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a real number to a positive rational number") public void testRaiseARealNumberToAPositiveRationalNumber() { double expected = 16.0; double actual = new Rational(4, 3).exp(8.0); @@ -281,6 +315,7 @@ public void testRaiseARealNumberToAPositiveRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Raise a real number to a negative rational number") public void testRaiseARealNumberToANegativeRationalNumber() { double expected = 1.0 / 3; double actual = new Rational(-1, 2).exp(9); @@ -289,6 +324,7 @@ public void testRaiseARealNumberToANegativeRationalNumber() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce a positive rational number to lowest terms") public void testReduceAPositiveRationalNumberToLowestTerms() { Rational expected = new Rational(1, 2); Rational actual = new Rational(2, 4); @@ -297,6 +333,7 @@ public void testReduceAPositiveRationalNumberToLowestTerms() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce places the minus sign on the numerator") public void testReducePlacesTheMinusSignOnTheNumerator() { Rational expected = new Rational(-3, 4); Rational actual = new Rational(3, -4); @@ -305,6 +342,7 @@ public void testReducePlacesTheMinusSignOnTheNumerator() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce a negative rational number to lowest terms") public void testReduceANegativeRationalNumberToLowestTerms() { Rational expected = new Rational(-2, 3); Rational actual = new Rational(-4, 6); @@ -313,6 +351,7 @@ public void testReduceANegativeRationalNumberToLowestTerms() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce a rational number with a negative denominator to lowest terms") public void testReduceARationalNumberWithANegativeDenominatorToLowestTerms() { Rational expected = new Rational(-1, 3); Rational actual = new Rational(3, -9); @@ -321,6 +360,7 @@ public void testReduceARationalNumberWithANegativeDenominatorToLowestTerms() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce zero to lowest terms") public void testReduceZeroToLowestTerms() { Rational expected = new Rational(0, 1); Rational actual = new Rational(0, 6); @@ -329,6 +369,7 @@ public void testReduceZeroToLowestTerms() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce an integer to lowest terms") public void testReduceAnIntegerToLowestTerms() { Rational expected = new Rational(-2, 1); Rational actual = new Rational(-14, 7); @@ -337,6 +378,7 @@ public void testReduceAnIntegerToLowestTerms() { @Disabled("Remove to run test") @Test + @DisplayName("Reduce one to lowest terms") public void testReduceOneToLowestTerms() { Rational expected = new Rational(1, 1); Rational actual = new Rational(13, 13); diff --git a/exercises/practice/react/src/test/java/ReactTest.java b/exercises/practice/react/src/test/java/ReactTest.java index fd2047353..e7c18a063 100644 --- a/exercises/practice/react/src/test/java/ReactTest.java +++ b/exercises/practice/react/src/test/java/ReactTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -10,6 +11,7 @@ public class ReactTest { @Test + @DisplayName("input cells have a value") public void testInputCellHasValue() { var input = React.inputCell(10); @@ -18,6 +20,7 @@ public void testInputCellHasValue() { @Disabled("Remove to run") @Test + @DisplayName("an input cell's value can be set") public void testInputCellValueCanBeSet() { var input = React.inputCell(4); input.setValue(20); @@ -27,6 +30,7 @@ public void testInputCellValueCanBeSet() { @Disabled("Remove to run") @Test + @DisplayName("compute cells calculate initial value") public void testComputeCellCalculateInitialValue() { var input = React.inputCell(1); var output = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -36,6 +40,7 @@ public void testComputeCellCalculateInitialValue() { @Disabled("Remove to run") @Test + @DisplayName("compute cells take inputs in the right order") public void testComputeCellsInTheRightOrder() { var first = React.inputCell(1); var second = React.inputCell(2); @@ -46,6 +51,7 @@ public void testComputeCellsInTheRightOrder() { @Disabled("Remove to run") @Test + @DisplayName("compute cells update value when dependencies are changed") public void testComputeCellsUpdateValueWhenDependenciesAreChanged() { var input = React.inputCell(1); var output = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -56,6 +62,7 @@ public void testComputeCellsUpdateValueWhenDependenciesAreChanged() { @Disabled("Remove to run") @Test + @DisplayName("compute cells can depend on other compute cells") public void testComputeCellsCanDependOnOtherComputeCells() { var input = React.inputCell(1); var timesTwo = React.computeCell(list -> list.get(0) * 2, List.of(input)); @@ -70,6 +77,7 @@ public void testComputeCellsCanDependOnOtherComputeCells() { @Disabled("Remove to run") @Test + @DisplayName("compute cells fire callbacks") public void testComputeCellsFireCallbacks() { var input = React.inputCell(1); var output = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -83,6 +91,7 @@ public void testComputeCellsFireCallbacks() { @Disabled("Remove to run") @Test + @DisplayName("callback cells only fire on change") public void testCallbacksOnlyFireOnChange() { var input = React.inputCell(1); var output = React.computeCell(list -> list.get(0) < 3 ? 111 : 222, List.of(input)); @@ -99,6 +108,7 @@ public void testCallbacksOnlyFireOnChange() { @Disabled("Remove to run") @Test + @DisplayName("callbacks do not report already reported values") public void testCallbacksDoNotReportAlreadyReportedValues() { var input = React.inputCell(1); var output = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -116,6 +126,7 @@ public void testCallbacksDoNotReportAlreadyReportedValues() { @Disabled("Remove to run") @Test + @DisplayName("callbacks can fire from multiple cells") public void testCallbacksCanFireFromMultipleCells() { var input = React.inputCell(1); var plusOne = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -134,6 +145,7 @@ public void testCallbacksCanFireFromMultipleCells() { @Disabled("Remove to run") @Test + @DisplayName("callbacks can be added and removed") public void testCallbacksCanBeAddedAndRemoved() { var input = React.inputCell(11); var output = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -166,6 +178,7 @@ public void testCallbacksCanBeAddedAndRemoved() { @Disabled("Remove to run") @Test + @DisplayName("removing a callback multiple times doesn't interfere with other callbacks") public void testRemovingACallbackMultipleTimesDoesntInterfereWithOtherCallbacks() { var input = React.inputCell(1); var output = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -189,6 +202,7 @@ public void testRemovingACallbackMultipleTimesDoesntInterfereWithOtherCallbacks( @Disabled("Remove to run") @Test + @DisplayName("callbacks should only be called once even if multiple dependencies change") public void testCallbacksShouldOnlyBeCalledOnceEvenIfMultipleDependenciesChange() { var input = React.inputCell(1); var plusOne = React.computeCell(list -> list.get(0) + 1, List.of(input)); @@ -205,6 +219,7 @@ public void testCallbacksShouldOnlyBeCalledOnceEvenIfMultipleDependenciesChange( @Disabled("Remove to run") @Test + @DisplayName("callbacks should not be called if dependencies change but output value doesn't change") public void testCallbacksShouldNotBeCalledIfDependenciesChangeButOutputValueDoesntChange() { var input = React.inputCell(1); var plusOne = React.computeCell(list -> list.get(0) + 1, List.of(input)); From cd054b6966037ab107d70170aaf1ea8fc0a8c6a7 Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Sun, 14 Sep 2025 17:30:39 +0530 Subject: [PATCH 62/67] intergalactic-transmission, isbn-verifier, isogram (#3011) * intergalactic-transmission, isbn-verifier, isogram * Update IsbnVerifierTest.java [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../java/IntergalacticTransmissionTest.java | 27 +++++++++++++++++++ .../src/test/java/IsbnVerifierTest.java | 21 +++++++++++++++ .../src/test/java/IsogramCheckerTest.java | 15 +++++++++++ 3 files changed, 63 insertions(+) diff --git a/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java b/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java index c4ecdef3a..9c26e13e5 100644 --- a/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java +++ b/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import java.util.List; @@ -9,6 +10,7 @@ public class IntergalacticTransmissionTest { @Test + @DisplayName("calculate transmit sequences") public void calculateTransmitSequencesEmptyMessage() { List input = List.of(); List expected = List.of(); @@ -18,6 +20,7 @@ public void calculateTransmitSequencesEmptyMessage() { @Disabled("Remove to run test") @Test + @DisplayName("0x00 is transmitted as 0x0000") public void calculateTransmitSequences0x00IsTransmittedAs0x0000() { List input = List.of(0x00); List expected = List.of(0x00, 0x00); @@ -27,6 +30,7 @@ public void calculateTransmitSequences0x00IsTransmittedAs0x0000() { @Disabled("Remove to run test") @Test + @DisplayName("0x02 is transmitted as 0x0300") public void calculateTransmitSequences0x02IsTransmittedAs0x0300() { List input = List.of(0x02); List expected = List.of(0x03, 0x00); @@ -36,6 +40,7 @@ public void calculateTransmitSequences0x02IsTransmittedAs0x0300() { @Disabled("Remove to run test") @Test + @DisplayName("0x06 is transmitted as 0x0600") public void calculateTransmitSequences0x06IsTransmittedAs0x0600() { List input = List.of(0x06); List expected = List.of(0x06, 0x00); @@ -45,6 +50,7 @@ public void calculateTransmitSequences0x06IsTransmittedAs0x0600() { @Disabled("Remove to run test") @Test + @DisplayName("0x05 is transmitted as 0x0581") public void calculateTransmitSequences0x05IsTransmittedAs0x0581() { List input = List.of(0x05); List expected = List.of(0x05, 0x81); @@ -54,6 +60,7 @@ public void calculateTransmitSequences0x05IsTransmittedAs0x0581() { @Disabled("Remove to run test") @Test + @DisplayName("0x29 is transmitted as 0x2881") public void calculateTransmitSequences0x29IsTransmittedAs0x2881() { List input = List.of(0x29); List expected = List.of(0x28, 0x81); @@ -63,6 +70,7 @@ public void calculateTransmitSequences0x29IsTransmittedAs0x2881() { @Disabled("Remove to run test") @Test + @DisplayName("0xc001c0de is transmitted as 0xc000711be1") public void calculateTransmitSequences0xc001c0deIsTransmittedAs0xc000711be1() { List input = List.of(0xc0, 0x01, 0xc0, 0xde); List expected = List.of(0xc0, 0x00, 0x71, 0x1b, 0xe1); @@ -72,6 +80,7 @@ public void calculateTransmitSequences0xc001c0deIsTransmittedAs0xc000711be1() { @Disabled("Remove to run test") @Test + @DisplayName("six byte message") public void calculateTransmitSequencesSixByteMessage() { List input = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x21); List expected = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0x84); @@ -81,6 +90,7 @@ public void calculateTransmitSequencesSixByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("at 7 bytes long, no padding is required") public void calculateTransmitSequencesSevenByteMessage() { List input = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x31, 0x21); List expected = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x42); @@ -90,6 +100,7 @@ public void calculateTransmitSequencesSevenByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("eight byte message") public void calculateTransmitSequencesEightByteMessage() { List input = List.of(0xc0, 0x01, 0x13, 0x37, 0xc0, 0xde, 0x21, 0x21); List expected = List.of(0xc0, 0x00, 0x44, 0x66, 0x7d, 0x06, 0x78, 0x42, 0x21, 0x81); @@ -99,6 +110,7 @@ public void calculateTransmitSequencesEightByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("twenty byte message") public void calculateTransmitSequencesTwentyByteMessage() { List input = List.of( 0x45, 0x78, 0x65, 0x72, 0x63, 0x69, 0x73, 0x6d, 0x20, 0x69, @@ -113,6 +125,7 @@ public void calculateTransmitSequencesTwentyByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("empty message") public void decodeReceivedMessagesEmptyMessage() { List input = List.of(); List expected = List.of(); @@ -122,6 +135,7 @@ public void decodeReceivedMessagesEmptyMessage() { @Disabled("Remove to run test") @Test + @DisplayName("zero message") public void decodeReceivedMessagesZeroMessage() { List input = List.of(0x00, 0x00); List expected = List.of(0x00); @@ -131,6 +145,7 @@ public void decodeReceivedMessagesZeroMessage() { @Disabled("Remove to run test") @Test + @DisplayName("0x0300 is decoded to 0x02") public void decodeReceivedMessages0x0300IsDecodedTo0x02() { List input = List.of(0x03, 0x00); List expected = List.of(0x02); @@ -140,6 +155,7 @@ public void decodeReceivedMessages0x0300IsDecodedTo0x02() { @Disabled("Remove to run test") @Test + @DisplayName("0x0581 is decoded to 0x05") public void decodeReceivedMessages0x0581IsDecodedTo0x05() { List input = List.of(0x05, 0x81); List expected = List.of(0x05); @@ -149,6 +165,7 @@ public void decodeReceivedMessages0x0581IsDecodedTo0x05() { @Disabled("Remove to run test") @Test + @DisplayName("0x2881 is decoded to 0x29") public void decodeReceivedMessages0x2881IsDecodedTo0x29() { List input = List.of(0x28, 0x81); List expected = List.of(0x29); @@ -158,6 +175,7 @@ public void decodeReceivedMessages0x2881IsDecodedTo0x29() { @Disabled("Remove to run test") @Test + @DisplayName("first byte has wrong parity") public void decodeFirstByteWrongParity() { List input = List.of(0x07, 0x00); assertThrows(IllegalArgumentException.class, () @@ -166,6 +184,7 @@ public void decodeFirstByteWrongParity() { @Disabled("Remove to run test") @Test + @DisplayName("second byte has wrong parity") public void decodeSecondByteWrongParity() { List input = List.of(0x03, 0x68); assertThrows(IllegalArgumentException.class, () @@ -174,6 +193,7 @@ public void decodeSecondByteWrongParity() { @Disabled("Remove to run test") @Test + @DisplayName("0xcf4b00 is decoded to 0xce94") public void decode0xcf4b00To0xce94() { List input = List.of(0xcf, 0x4b, 0x00); List expected = List.of(0xce, 0x94); @@ -183,6 +203,7 @@ public void decode0xcf4b00To0xce94() { @Disabled("Remove to run test") @Test + @DisplayName("0xe2566500 is decoded to 0xe2ad90") public void decode0xe2566500To0xe2ad90() { List input = List.of(0xe2, 0x56, 0x65, 0x00); List expected = List.of(0xe2, 0xad, 0x90); @@ -192,6 +213,7 @@ public void decode0xe2566500To0xe2ad90() { @Disabled("Remove to run test") @Test + @DisplayName("six byte message") public void decodeSixByteMessage() { List input = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0x84); List expected = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x21); @@ -201,6 +223,7 @@ public void decodeSixByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("seven byte message") public void decodeSevenByteMessage() { List input = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x42); List expected = List.of(0x47, 0x72, 0x65, 0x61, 0x74, 0x31, 0x21); @@ -210,6 +233,7 @@ public void decodeSevenByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("last byte has wrong parity") public void decodeLastByteWrongParity() { List input = List.of(0x47, 0xb8, 0x99, 0xac, 0x17, 0xa0, 0xc5, 0x43); assertThrows(IllegalArgumentException.class, () @@ -218,6 +242,7 @@ public void decodeLastByteWrongParity() { @Disabled("Remove to run test") @Test + @DisplayName("eight byte message") public void decodeEightByteMessage() { List input = List.of(0xc0, 0x00, 0x44, 0x66, 0x7d, 0x06, 0x78, 0x42, 0x21, 0x81); List expected = List.of(0xc0, 0x01, 0x13, 0x37, 0xc0, 0xde, 0x21, 0x21); @@ -227,6 +252,7 @@ public void decodeEightByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("twenty byte message") public void decodeTwentyByteMessage() { List input = List.of( 0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, 0x1b, @@ -240,6 +266,7 @@ public void decodeTwentyByteMessage() { @Disabled("Remove to run test") @Test + @DisplayName("wrong parity on 16th byte") public void decodeWrongParityOn16thByte() { List input = List.of( 0x44, 0xbd, 0x18, 0xaf, 0x27, 0x1b, 0xa5, 0xe7, 0x6c, 0x90, diff --git a/exercises/practice/isbn-verifier/src/test/java/IsbnVerifierTest.java b/exercises/practice/isbn-verifier/src/test/java/IsbnVerifierTest.java index 65483a772..6ad959b9b 100644 --- a/exercises/practice/isbn-verifier/src/test/java/IsbnVerifierTest.java +++ b/exercises/practice/isbn-verifier/src/test/java/IsbnVerifierTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -13,120 +14,140 @@ public void setUp() { } @Test + @DisplayName("valid isbn") public void validIsbnNumber() { assertThat(isbnVerifier.isValid("3-598-21508-8")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid isbn check digit") public void invalidIsbnCheckDigit() { assertThat(isbnVerifier.isValid("3-598-21508-9")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("valid isbn with a check digit of 10") public void validIsbnNumberWithCheckDigitOfTen() { assertThat(isbnVerifier.isValid("3-598-21507-X")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("valid isbn with check digit padded with letters is invalid") public void validIsbnNumberWithCheckDigitPaddedWithLettersIsInvalid() { assertThat(isbnVerifier.isValid("ABCDEFG3-598-21507-XQWERTYUI")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("check digit is a character other than X") public void checkDigitIsACharacterOtherThanX() { assertThat(isbnVerifier.isValid("3-598-21507-A")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid check digit in isbn is not treated as zero") public void invalidCheckDigitInIsbn() { assertThat(isbnVerifier.isValid("4-598-21507-B")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid character in isbn is not treated as zero") public void invalidCharacterInIsbn() { assertThat(isbnVerifier.isValid("3-598-P1581-X")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("X is only valid as a check digit") public void xIsOnlyValidAsACheckDigit() { assertThat(isbnVerifier.isValid("3-598-2X507-9")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("valid isbn without separating dashes") public void validIsbnWithoutSeparatingDashes() { assertThat(isbnVerifier.isValid("3598215088")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("isbn without separating dashes and X as check digit") public void isbnWithoutSeparatingDashesAndXAsCheckDigit() { assertThat(isbnVerifier.isValid("359821507X")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("isbn without check digit and dashes") public void isbnWithoutCheckDigitAndDashes() { assertThat(isbnVerifier.isValid("359821507")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("too long isbn and no dashes") public void tooLongIsbnAndNoDashes() { assertThat(isbnVerifier.isValid("3598215078X")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("too short isbn") public void tooShortIsbn() { assertThat(isbnVerifier.isValid("00")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("isbn without check digit") public void isbnWithoutCheckDigit() { assertThat(isbnVerifier.isValid("3-598-21507")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("check digit of X should not be used for 0") public void checkDigitOfXShouldNotBeUsedForZero() { assertThat(isbnVerifier.isValid("3-598-21515-X")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("empty isbn") public void emptyIsbn() { assertThat(isbnVerifier.isValid("")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("input is 9 characters") public void inputIsNineCharacters() { assertThat(isbnVerifier.isValid("134456729")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid characters are not ignored after checking length") public void invalidCharactersAreNotIgnoredAfterCheckingLength() { assertThat(isbnVerifier.isValid("3132P34035")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid characters are not ignored before checking length") public void invalidCharactersAreNotIgnoredBeforeCheckingLength() { assertThat(isbnVerifier.isValid("3598P215088")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("input is too long but contains a valid isbn") public void inputIsTooLongButContainsAValidIsbn() { assertThat(isbnVerifier.isValid("98245726788")).isFalse(); } diff --git a/exercises/practice/isogram/src/test/java/IsogramCheckerTest.java b/exercises/practice/isogram/src/test/java/IsogramCheckerTest.java index 88fccfc29..cdc73f73c 100644 --- a/exercises/practice/isogram/src/test/java/IsogramCheckerTest.java +++ b/exercises/practice/isogram/src/test/java/IsogramCheckerTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -13,84 +14,98 @@ public void setUp() { } @Test + @DisplayName("empty string") public void testEmptyString() { assertThat(isogramChecker.isIsogram("")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("isogram with only lower case characters") public void testLowercaseIsogram() { assertThat(isogramChecker.isIsogram("isogram")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("word with one duplicated character") public void testNotIsogram() { assertThat(isogramChecker.isIsogram("eleven")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("word with one duplicated character from the end of the alphabet") public void testDuplicateEndAlphabet() { assertThat(isogramChecker.isIsogram("zzyzx")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("longest reported english isogram") public void testMediumLongIsogram() { assertThat(isogramChecker.isIsogram("subdermatoglyphic")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("word with duplicated character in mixed case") public void testCaseInsensitive() { assertThat(isogramChecker.isIsogram("Alphabet")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("word with duplicated character in mixed case, lowercase first") public void testDuplicateMixedCase() { assertThat(isogramChecker.isIsogram("alphAbet")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("hypothetical isogrammic word with hyphen") public void testIsogramWithHyphen() { assertThat(isogramChecker.isIsogram("thumbscrew-japingly")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("hypothetical word with duplicated character following hyphen") public void testIsogramWithDuplicatedCharAfterHyphen() { assertThat(isogramChecker.isIsogram("thumbscrew-jappingly")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("isogram with duplicated hyphen") public void testIsogramWithDuplicatedHyphen() { assertThat(isogramChecker.isIsogram("six-year-old")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("made-up name that is an isogram") public void testMadeUpNameThatIsAnIsogram() { assertThat(isogramChecker.isIsogram("Emily Jung Schwartzkopf")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("duplicated character in the middle") public void testDuplicatedCharacterInTheMiddleIsNotIsogram() { assertThat(isogramChecker.isIsogram("accentor")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("same first and last characters") public void testSameFirstAndLast() { assertThat(new IsogramChecker().isIsogram("angola")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("word with duplicated character and with two hyphens") public void testDuplicatedCharacterAndTwoHyphens() { assertThat(new IsogramChecker().isIsogram("up-to-date")).isFalse(); } From 489bc89e8da7360313394cd41bcd9e4c27914d19 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:50:33 +0800 Subject: [PATCH 63/67] =?UTF-8?q?=F0=9F=A4=96=20Auto-sync=20docs,=20metada?= =?UTF-8?q?ta,=20and=20filepaths=20(#3015)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- exercises/practice/relative-distance/.docs/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/relative-distance/.docs/introduction.md b/exercises/practice/relative-distance/.docs/introduction.md index cb9fee6c7..34073b40a 100644 --- a/exercises/practice/relative-distance/.docs/introduction.md +++ b/exercises/practice/relative-distance/.docs/introduction.md @@ -9,4 +9,4 @@ Your algorithm will determine the **degree of separation** between two individua Will your app help crown a perfect match? -[islendiga-app]: http://www.islendingaapp.is/information-in-english/ +[islendiga-app]: https://web.archive.org/web/20250816223614/http://www.islendingaapp.is/information-in-english/ From b4a8b9f992f411f39f568558f00bff756351ab43 Mon Sep 17 00:00:00 2001 From: Kah Goh Date: Mon, 15 Sep 2025 19:43:06 +0800 Subject: [PATCH 64/67] Update documentation for practice exercise differences (#3012) Co-authored-by: Jagdish Prajapati --- .../contributing-to-practice-exercises.md | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/reference/contributing-to-practice-exercises.md b/reference/contributing-to-practice-exercises.md index 75b25ddd5..e5fbb2c78 100644 --- a/reference/contributing-to-practice-exercises.md +++ b/reference/contributing-to-practice-exercises.md @@ -1,24 +1,33 @@ # Contributing to Practice Exercises -## Adding a new Practice Exercise +There are two types of practice exercises in the Java track. + +- The most common are come from the [problem specifications repository][problem-specifications]. + The repository contains data that is common for all implementations, such as the introduction, instructions and the test cases. -The easiest way to add a new exercise to the Java track is to port an exercise from another track. -That means that you take an exercise that has already been implemented in another language, and you implement it in this track. +- The others _do_ not have data in the problem specification and are specific to the Java track. -To add a completely new exercise you need to open a pull request to the [problem specifications repository][problem-specifications]. -Any completely new exercise needs to be added and accepted there before it can be added to the Java track. +If you are looking to add a completely new exercise that is not from the [problem specifications repository][problem-specifications], please open a discussion in the forums in the [Java][forum-java] category for Java specific exercises or the [Building Exercism][forum-building-exercism] category for adding to the [problem specifications repository][problem-specifications]. -Before porting an exercise to the Java track, please review the [practice exercise guide][docs-building-exercises-practice]. +If the exercise is already in the problem specifications, please check that no one else has claimed the issue or pull request. +An issue can be "claimed" by asking (in a comment on the issue) if you can work on it. -Please make sure no one else has a pull request open to implement your chosen exercise before you start. +## Adding a new Practice Exercise -It might also be a good idea to open a Pull Request to make it clear to others that you are working on this exercise. -This can just be a Pull Request with an empty commit that states which new exercise you're working on. -[Mark the Pull Request as Draft][github-draft-pr] to let the maintainers know that it's not ready for review yet. +Before adding an exercise to the Java track, please review the [practice exercise guide][docs-building-exercises-practice]. + +The easiest way to get started is to use [configlet][docs-configlet] to provide some of the scaffolding. + +```sh +configlet create --practice-exercise +``` + +This will create the necessary entries in `config.json`, create the directory and pulls the exercise's document files from the problem specifications repository. The Java specific details you need to know about adding an exercise are: -- Please add an entry to the `exercises` array in `config.json`. +- Please check the entry added by configlet to the `exercises` array in `config.json`. + The entries are sorted by difficulty, then name. You can find details about what should be in that entry at the [Exercism docs][docs-building-config-json]. You can also look at other entries in `config.json` as examples and try to mimic them. @@ -30,11 +39,12 @@ The Java specific details you need to know about adding an exercise are: In the `resources/exercise-template` you will find a template to get you started. See [The Problem Submodules](../CONTRIBUTING.md#the-problem-submodules) section for details on how each exercise submodule is structured. -- Use the configlet to [generate documentation and metadata files][docs-building-configlet-sync-new-exercise] for the new exercise. +- Please copy and add the Gradle files and directories from the `resources/exercise-template` directory. + This should allow you to run Gradle from the exercise's directory. - Make sure you've followed the [track policies](../POLICIES.md), especially the ones for exercise added/updated. -Hopefully that should be enough information to help you port an exercise to the Java track. +Hopefully that should be enough information to help you add a practice exercise to the Java track. Feel free to open an issue or post in the [Building Exercism][forum-building-exercism] category of the [Exercism forum][forum] if you have any questions, and we'll try and answer as soon as we can. ## Updating the documentation for an exercise @@ -48,8 +58,8 @@ bin/configlet sync --docs --metadata --update --exercise ## Updating the tests for an exercise -The tests for practice exercises are based on the canonical data in [problem specification][problem-specifications]. -Each practice exercise should contain a `.meta/tests.toml` file containing the test cases that were implemented for that exercise. +For exercises from the [problem specification][problem-specifications], the tests are based on the canonical data. +The practice exercise should contain a `.meta/tests.toml` file containing the test cases that were implemented for that exercise. In case the canonical data for an exercise changes, this file can be updated by running the configlet from the root of this repository: @@ -60,10 +70,12 @@ bin/configlet sync --tests --update --exercise Note that this only updates the `.meta/tests.toml` file. Any tests that were added or updated should be implemented in the exercise's unit tests! +Syncing is _not_ required for Java specific exercises. + [docs-building-config-json]: https://exercism.org/docs/building/tracks/config-json -[docs-building-configlet-sync-new-exercise]: https://exercism.org/docs/building/configlet/sync#h-using-sync-when-adding-a-new-exercise-to-a-track [docs-building-exercises-practice]: https://exercism.org/docs/building/tracks/practice-exercises +[docs-configlet]: https://exercism.org/docs/building/configlet [forum]: https://forum.exercism.org/ [forum-building-exercism]: https://forum.exercism.org/c/exercism/building-exercism/125 -[github-draft-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request +[forum-java]: https://forum.exercism.org/c/programming/java/91 [problem-specifications]: https://github.com/exercism/problem-specifications/tree/main/exercises From 03bfe7482304dee2eb756eb5571a33017f84dacb Mon Sep 17 00:00:00 2001 From: Atharv Patil Date: Mon, 15 Sep 2025 17:22:04 +0530 Subject: [PATCH 65/67] kindergarten-garden, knapsack, largest-series-product, leap (#3014) * kindergarten-garten, knapsack, largest-series-product, leap * Update KindergartenGardenTest.java * Update KnapsackTest.java [no important files changed] --------- Co-authored-by: Jagdish Prajapati --- .../src/test/java/KindergartenGardenTest.java | 22 ++++++++++++++++--- .../knapsack/src/test/java/KnapsackTest.java | 10 ++++++++- .../LargestSeriesProductCalculatorTest.java | 15 +++++++++++++ .../practice/leap/src/test/java/LeapTest.java | 10 +++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/exercises/practice/kindergarten-garden/src/test/java/KindergartenGardenTest.java b/exercises/practice/kindergarten-garden/src/test/java/KindergartenGardenTest.java index eb7ab61f2..e2b511869 100644 --- a/exercises/practice/kindergarten-garden/src/test/java/KindergartenGardenTest.java +++ b/exercises/practice/kindergarten-garden/src/test/java/KindergartenGardenTest.java @@ -1,12 +1,12 @@ - -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; - +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class KindergartenGardenTest { @Test + @DisplayName("garden with single student") public void singleStudent() { String garden = "RC\nGG"; String student = "Alice"; @@ -17,6 +17,7 @@ public void singleStudent() { @Disabled("Remove to run test") @Test + @DisplayName("different garden with single student") public void singleStudent2() { String garden = "VC\nRC"; String student = "Alice"; @@ -27,6 +28,7 @@ public void singleStudent2() { @Disabled("Remove to run test") @Test + @DisplayName("garden with two students") public void twoStudents() { String garden = "VVCG\nVVRC"; String student = "Bob"; @@ -37,6 +39,7 @@ public void twoStudents() { @Disabled("Remove to run test") @Test + @DisplayName("second student's garden") public void oneGardenSecondStudent() { String garden = "VVCCGG\nVVCCGG"; String student = "Bob"; @@ -47,6 +50,7 @@ public void oneGardenSecondStudent() { @Disabled("Remove to run test") @Test + @DisplayName("third student's garden") public void oneGardenThirdStudent() { String garden = "VVCCGG\nVVCCGG"; String student = "Charlie"; @@ -57,6 +61,7 @@ public void oneGardenThirdStudent() { @Disabled("Remove to run test") @Test + @DisplayName("for Alice, first student's garden") public void fullGardenForAlice() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Alice"; @@ -67,6 +72,7 @@ public void fullGardenForAlice() { @Disabled("Remove to run test") @Test + @DisplayName("for Bob, second student's garden") public void fullGardenForBob() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Bob"; @@ -77,6 +83,7 @@ public void fullGardenForBob() { @Disabled("Remove to run test") @Test + @DisplayName("for Charlie") public void fullGardenForCharlie() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Charlie"; @@ -87,6 +94,7 @@ public void fullGardenForCharlie() { @Disabled("Remove to run test") @Test + @DisplayName("for David") public void fullGardenForDavid() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "David"; @@ -97,6 +105,7 @@ public void fullGardenForDavid() { @Disabled("Remove to run test") @Test + @DisplayName("for Eve") public void fullGardenForEve() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Eve"; @@ -107,6 +116,7 @@ public void fullGardenForEve() { @Disabled("Remove to run test") @Test + @DisplayName("for Fred") public void fullGardenForFred() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Fred"; @@ -117,6 +127,7 @@ public void fullGardenForFred() { @Disabled("Remove to run test") @Test + @DisplayName("for Ginny") public void fullGardenForGinny() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Ginny"; @@ -127,6 +138,7 @@ public void fullGardenForGinny() { @Disabled("Remove to run test") @Test + @DisplayName("for Harriet") public void fullGardenForHarriet() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Harriet"; @@ -137,6 +149,7 @@ public void fullGardenForHarriet() { @Disabled("Remove to run test") @Test + @DisplayName("for Ileana") public void fullGardenForIleana() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Ileana"; @@ -147,6 +160,7 @@ public void fullGardenForIleana() { @Disabled("Remove to run test") @Test + @DisplayName("for Joseph") public void fullGardenForJoseph() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Joseph"; @@ -157,6 +171,7 @@ public void fullGardenForJoseph() { @Disabled("Remove to run test") @Test + @DisplayName("for Kincaid, second to last student's garden") public void fullGardenForKincaid() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Kincaid"; @@ -167,6 +182,7 @@ public void fullGardenForKincaid() { @Disabled("Remove to run test") @Test + @DisplayName("for Larry, last student's garden") public void fullGardenForLarry() { String garden = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV"; String student = "Larry"; diff --git a/exercises/practice/knapsack/src/test/java/KnapsackTest.java b/exercises/practice/knapsack/src/test/java/KnapsackTest.java index 205d0e56e..166c12cbe 100644 --- a/exercises/practice/knapsack/src/test/java/KnapsackTest.java +++ b/exercises/practice/knapsack/src/test/java/KnapsackTest.java @@ -1,5 +1,6 @@ -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.util.List; @@ -17,6 +18,7 @@ public void setup() { } @Test + @DisplayName("no items") public void testNoItems() { List items = List.of(); assertThat(knapsack.maximumValue(100, items)).isEqualTo(0); @@ -24,6 +26,7 @@ public void testNoItems() { @Disabled("Remove to run test") @Test + @DisplayName("one item, too heavy") public void testOneItemTooHeavy() { List items = List.of( new Item(100, 1) @@ -34,6 +37,7 @@ public void testOneItemTooHeavy() { @Disabled("Remove to run test") @Test + @DisplayName("five items (cannot be greedy by weight)") public void testFiveItemsCannotBeGreedyByWeight() { List items = List.of( new Item(2, 5), @@ -48,6 +52,7 @@ public void testFiveItemsCannotBeGreedyByWeight() { @Disabled("Remove to run test") @Test + @DisplayName("five items (cannot be greedy by value)") public void testFiveItemsCannotBeGreedyByValue() { List items = List.of( new Item(2, 20), @@ -62,6 +67,7 @@ public void testFiveItemsCannotBeGreedyByValue() { @Disabled("Remove to run test") @Test + @DisplayName("example knapsack") public void testExampleKnapsack() { List items = List.of( new Item(5, 10), @@ -75,6 +81,7 @@ public void testExampleKnapsack() { @Disabled("Remove to run test") @Test + @DisplayName("8 items") public void testEightItems() { List items = List.of( new Item(25, 350), @@ -92,6 +99,7 @@ public void testEightItems() { @Disabled("Remove to run test") @Test + @DisplayName("15 items") public void testFifteenItems() { List items = List.of( new Item(70, 135), diff --git a/exercises/practice/largest-series-product/src/test/java/LargestSeriesProductCalculatorTest.java b/exercises/practice/largest-series-product/src/test/java/LargestSeriesProductCalculatorTest.java index ee7f8a8b2..8a2c48e20 100644 --- a/exercises/practice/largest-series-product/src/test/java/LargestSeriesProductCalculatorTest.java +++ b/exercises/practice/largest-series-product/src/test/java/LargestSeriesProductCalculatorTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -7,6 +8,7 @@ public class LargestSeriesProductCalculatorTest { @Test + @DisplayName("finds the largest product if span equals length") public void testCorrectlyCalculatesLargestProductWhenSeriesLengthEqualsStringToSearchLength() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("29"); long expectedProduct = 18; @@ -18,6 +20,7 @@ public void testCorrectlyCalculatesLargestProductWhenSeriesLengthEqualsStringToS @Disabled("Remove to run test") @Test + @DisplayName("can find the largest product of 2 with numbers in order") public void testCorrectlyCalculatesLargestProductOfLengthTwoWithNumbersInOrder() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("0123456789"); long expectedProduct = 72; @@ -29,6 +32,7 @@ public void testCorrectlyCalculatesLargestProductOfLengthTwoWithNumbersInOrder() @Disabled("Remove to run test") @Test + @DisplayName("can find the largest product of 2") public void testCorrectlyCalculatesLargestProductOfLengthTwoWithNumbersNotInOrder() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("576802143"); long expectedProduct = 48; @@ -40,6 +44,7 @@ public void testCorrectlyCalculatesLargestProductOfLengthTwoWithNumbersNotInOrde @Disabled("Remove to run test") @Test + @DisplayName("can find the largest product of 3 with numbers in order") public void testCorrectlyCalculatesLargestProductOfLengthThreeWithNumbersInOrder() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("0123456789"); long expectedProduct = 504; @@ -51,6 +56,7 @@ public void testCorrectlyCalculatesLargestProductOfLengthThreeWithNumbersInOrder @Disabled("Remove to run test") @Test + @DisplayName("can find the largest product of 3") public void testCorrectlyCalculatesLargestProductOfLengthThreeWithNumbersNotInOrder() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("1027839564"); long expectedProduct = 270; @@ -62,6 +68,7 @@ public void testCorrectlyCalculatesLargestProductOfLengthThreeWithNumbersNotInOr @Disabled("Remove to run test") @Test + @DisplayName("can find the largest product of 5 with numbers in order") public void testCorrectlyCalculatesLargestProductOfLengthFiveWithNumbersInOrder() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("0123456789"); long expectedProduct = 15120; @@ -73,6 +80,7 @@ public void testCorrectlyCalculatesLargestProductOfLengthFiveWithNumbersInOrder( @Disabled("Remove to run test") @Test + @DisplayName("can get the largest product of a big number") public void testCorrectlyCalculatesLargestProductInLongStringToSearchV1() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator( "73167176531330624919225119674426574742355349194934"); @@ -86,6 +94,7 @@ public void testCorrectlyCalculatesLargestProductInLongStringToSearchV1() { @Disabled("Remove to run test") @Test + @DisplayName("reports zero if the only digits are zero") public void testCorrectlyCalculatesLargestProductOfZeroIfAllDigitsAreZeroes() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("0000"); long expectedProduct = 0; @@ -97,6 +106,7 @@ public void testCorrectlyCalculatesLargestProductOfZeroIfAllDigitsAreZeroes() { @Disabled("Remove to run test") @Test + @DisplayName("reports zero if all spans include zero") public void testCorrectlyCalculatesLargestProductOfZeroIfAllSeriesOfGivenLengthContainZero() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("99099"); long expectedProduct = 0; @@ -108,6 +118,7 @@ public void testCorrectlyCalculatesLargestProductOfZeroIfAllSeriesOfGivenLengthC @Disabled("Remove to run test") @Test + @DisplayName("rejects span longer than string length") public void testSeriesLengthLongerThanLengthOfStringToTestIsRejected() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("123"); @@ -118,6 +129,7 @@ public void testSeriesLengthLongerThanLengthOfStringToTestIsRejected() { @Disabled("Remove to run test") @Test + @DisplayName("reports 1 for empty string and empty product (0 span)") public void testEmptyStringToSearchAndSeriesOfNonZeroLengthIsRejected() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator(""); @@ -128,6 +140,7 @@ public void testEmptyStringToSearchAndSeriesOfNonZeroLengthIsRejected() { @Disabled("Remove to run test") @Test + @DisplayName("rejects invalid character in digits") public void testStringToSearchContainingNonDigitCharacterIsRejected() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new LargestSeriesProductCalculator("1234a5")) @@ -136,6 +149,7 @@ public void testStringToSearchContainingNonDigitCharacterIsRejected() { @Disabled("Remove to run test") @Test + @DisplayName("rejects negative span") public void testNegativeSeriesLengthIsRejected() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("12345"); @@ -146,6 +160,7 @@ public void testNegativeSeriesLengthIsRejected() { @Disabled("Remove to run test") @Test + @DisplayName("integer overflow") public void testForIntegerOverflow() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator("9999999999"); long expectedProduct = 3486784401L; diff --git a/exercises/practice/leap/src/test/java/LeapTest.java b/exercises/practice/leap/src/test/java/LeapTest.java index b5234d36d..b61e8c2d9 100644 --- a/exercises/practice/leap/src/test/java/LeapTest.java +++ b/exercises/practice/leap/src/test/java/LeapTest.java @@ -1,5 +1,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -14,54 +15,63 @@ public void setup() { } @Test + @DisplayName("year not divisible by 4 in common year") public void testYearNotDivBy4InCommonYear() { assertThat(leap.isLeapYear(2015)).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 2, not divisible by 4 in common year\"") public void testYearDivBy2NotDivBy4InCommonYear() { assertThat(leap.isLeapYear(1970)).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 4, not divisible by 100 in leap year") public void testYearDivBy4NotDivBy100InLeapYear() { assertThat(leap.isLeapYear(1996)).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 4 and 5 is still a leap year") public void testYearDivBy4And5InLeapYear() { assertThat(leap.isLeapYear(1960)).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 100, not divisible by 400 in common year") public void testYearDivBy100NotDivBy400InCommonYear() { assertThat(leap.isLeapYear(2100)).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 100 but not by 3 is still not a leap year") public void testYearDivBy100NotDivBy3IsNotLeapYear() { assertThat(leap.isLeapYear(1900)).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 400 is leap year") public void testYearDivBy400InLeapYear() { assertThat(leap.isLeapYear(2000)).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 400 but not by 125 is still a leap year") public void testYearDivBy400NotDivBy125IsLeapYear() { assertThat(leap.isLeapYear(2400)).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("year divisible by 200, not divisible by 400 in common year") public void testYearDivBy200NotDivBy400InCommonYear() { assertThat(leap.isLeapYear(1800)).isFalse(); } From 902f1a6f7b69b879dd2e07a958672a9d0341368d Mon Sep 17 00:00:00 2001 From: Kah Goh Date: Mon, 15 Sep 2025 20:10:39 +0800 Subject: [PATCH 66/67] Make squeaky clean more beginner friendly (#3013) This change: - Adds more detail to the chars concept and introduction to try help students get closer to the intended solution the first time. During mentoring, it was noticed there were solutions using String.replace, which was not intended. - Replace Unicode link from chars concept. In 02d488e7 the concept was updated to remove Unicode. However, this link seems to have been forgotten. - Replace "if-else-statements" with "strings" as a prerequisite for squeaky clean. The solution seems to require obtaining the characters in the String and looping over them. "if-else-statements" is also a prerequisite for "arrays", so students will still have to "if-else-statements" before squeaky clean. Co-authored-by: Jagdish Prajapati --- concepts/chars/.meta/config.json | 4 +- concepts/chars/about.md | 108 +++++++++++++++++- concepts/chars/introduction.md | 87 +++++++++++++- concepts/chars/links.json | 8 +- config.json | 1 + .../squeaky-clean/.docs/introduction.md | 87 +++++++++++++- .../concept/squeaky-clean/.meta/config.json | 1 + 7 files changed, 275 insertions(+), 21 deletions(-) diff --git a/concepts/chars/.meta/config.json b/concepts/chars/.meta/config.json index 7f86b573e..bcf21a72c 100644 --- a/concepts/chars/.meta/config.json +++ b/concepts/chars/.meta/config.json @@ -3,5 +3,7 @@ "authors": [ "ystromm" ], - "contributors": [] + "contributors": [ + "kahgoh" + ] } diff --git a/concepts/chars/about.md b/concepts/chars/about.md index ff5616166..d6ae4bd75 100644 --- a/concepts/chars/about.md +++ b/concepts/chars/about.md @@ -1,6 +1,108 @@ # About -`char`s are generally easy to use. -They can be extracted from strings, added back (by means of a string builder), defined and initialised using literals with single quotes, as in `char ch = 'A';`, assigned and compared. +The Java `char` primitive type is a 16 bit representation of a single Unicode character. -The Character class encapsulates the char value. +~~~~exercism/note +The `char` type is based on the [original Unicode specification][unicode-specification], which used 16 bits to represent characters. +This is enough to cover most of the common letters and covers characters in the range 0x0000 to 0xFFFF. +The specification has since expanded the range of possible characters up to 0x01FFFF. + +[unicode-specification]: https://www.unicode.org/versions/Unicode1.0.0/ +~~~~ + +Multiple `char`s can comprise a string, such as `"word"`, or `char`s can be processed independently. +A `char` literal is surrounded by single quotes (e.g. `'A'`). + +```java +char lowerA = 'a'; +char upperB = 'B'; +``` + +## Getting the `char`s of a `String` + +The `String.toCharArray` method returns a String's chars as an array. +As mentioned in [arrays][concept-arrays], you can use a `for` loop to iterate over the array. + +```java +String text = "Hello"; +char[] asArray = text.toCharArray(); + +for (char ch: asArray) { + System.out.println(ch); +} + +// Outputs: +// H +// e +// l +// l +// o +``` + +## The [Character][docs-character] class + +There are many builtin library methods to inspect and manipulate `char`s. +These can be found as static methods of the [`java.lang.Character`][docs-character] class. +Here are some examples: + +```java +Character.isWhitespace(' '); // true +Character.isWhitespace('#'); // false + +Character.isLetter('a'); // true +Character.isLetter('3'); // false + +Character.isDigit('6'); // true +Character.isDigit('?'); // false +``` + +~~~~exercism/note +Some methods in the Character class have an overload so that it can take either an `char` or `int`. +For example, `isDigit` has one that accepts a [`char`][is-digit-char] and another an [`int`][is-digit-int]. +As mentioned earlier, the `char` type can only represent the characters in the range from 0x0000 to 0xFFFF. +The `int`, however, can represent all characters, hence the `int` overloads. + +[is-digit-char]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isDigit(char) +[is-digit-int]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html#isDigit(int) +~~~~ + +## Adding a `char` to a `String` + +The `+` operator can be used to add a `char` to a `String`. + +```java +'a' + " banana" // => "a banana" +"banana " + 'a' // => "banana a" +``` + +~~~~exercism/caution +Becareful _not_ to use `+` to join two `char`s together to form a `String`! +Adding two `char`s this way gives an `int`, _not_ a `String`! +For example: + +```java +'b' + 'c'; +// => 197 (not the String "bc") +``` + +This is because Java promotes the `char` to an `int` (see [4.2 Primitive Types and Values ][jls-primitives] of the [Java Language Specification][jls-main]). + +[jls-main]: https://docs.oracle.com/javase/specs/jls/se21/html/ +[jls-primitives]: https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2 +~~~~ + +However, when there are many characters to be added, it can be more efficient to use a [`StringBuilder`][docs-stringBuilder] instead: + +```java +StringBuilder builder = new StringBuilder(); +builder.append('a'); +builder.append('b'); +builder.append('c'); + +String builtString = builder.toString(); +// => abc +``` + +[concept-arrays]: https://exercism.org/tracks/java/concepts/arrays +[docs-character]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html +[docs-stringBuilder]: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/StringBuilder.html diff --git a/concepts/chars/introduction.md b/concepts/chars/introduction.md index 12fdaf086..bb80baba7 100644 --- a/concepts/chars/introduction.md +++ b/concepts/chars/introduction.md @@ -1,12 +1,87 @@ # Introduction -The Java `char` type represents the smallest addressable components of text. -Multiple `char`s can comprise a string such as `"word"` or `char`s can be processed independently. -Their literals have single quotes e.g. `'A'`. +## chars + +The Java `char` primitive type is a 16 bit representation of a single character. +Multiple `char`s can comprise a string, such as `"word"`, or `char`s can be processed independently. +A `char` literal is surrounded by single quotes (e.g. `'A'`). + +```java +char lowerA = 'a'; +char upperB = 'B'; +``` + +## Getting the `char`s of a `String` + +The `String.toCharArray` method returns a String's chars as an array. +As mentioned in arrays, you can use a `for` loop to iterate over the array. + +```java +String text = "Hello"; +char[] asArray = text.toCharArray(); + +for (char ch: asArray) { + System.out.println(ch); +} + +// Outputs: +// H +// e +// l +// l +// o +``` + +## The Character class There are many builtin library methods to inspect and manipulate `char`s. These can be found as static methods of the `java.lang.Character` class. +Here are some examples: + +```java +Character.isWhitespace(' '); // true +Character.isWhitespace('#'); // false + +Character.isLetter('a'); // true +Character.isLetter('3'); // false + +Character.isDigit('6'); // true +Character.isDigit('?'); // false +``` + +## Adding a `char` to a `String` + +The `+` operator can be used to add a `char` to a `String`. + +```java +'a' + " banana" // => "a banana" +"banana " + 'a' // => "banana a" +``` + +~~~~exercism/caution +Becareful _not_ to use `+` to join two `char`s together to form a `String`! +Adding two `char`s this way gives an `int`, _not_ a `String`! +For example: + +```java +'b' + 'c'; +// => 197 (not the String "bc") +``` + +This is because Java promotes the `char` to an `int` (see [4.2 Primitive Types and Values ][jls-primitives] of the [Java Language Specification][jls-main]). + +[jls-main]: https://docs.oracle.com/javase/specs/jls/se21/html/ +[jls-primitives]: https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2 +~~~~ + +However, when there are many characters to be added, it can be more efficient to use a `StringBuilder` instead: + +```java +StringBuilder builder = new StringBuilder(); +builder.append('a'); +builder.append('b'); +builder.append('c'); -`char`s are sometimes used in conjunction with a `StringBuilder` object. -This object has methods that allow a string to be constructed character by character and manipulated. -At the end of the process `toString` can be called on it to output a complete string. +String builtString = builder.toString(); +// => abc +``` diff --git a/concepts/chars/links.json b/concepts/chars/links.json index bf2b4a182..b78281499 100644 --- a/concepts/chars/links.json +++ b/concepts/chars/links.json @@ -1,12 +1,10 @@ [ { - "url":"https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html", + "url":"https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Character.html", "description":"javadoc" }, { - "url": "https://docs.oracle.com/javase/tutorial/i18n/text/unicode.html", - "description": "unicode" + "url": "https://dev.java/learn/numbers-strings/characters/", + "description": "characters" } - - ] diff --git a/config.json b/config.json index fa08384ff..a9a5b8e39 100644 --- a/config.json +++ b/config.json @@ -105,6 +105,7 @@ "chars" ], "prerequisites": [ + "arrays", "strings" ], "status": "active" diff --git a/exercises/concept/squeaky-clean/.docs/introduction.md b/exercises/concept/squeaky-clean/.docs/introduction.md index 15e81f074..016475918 100644 --- a/exercises/concept/squeaky-clean/.docs/introduction.md +++ b/exercises/concept/squeaky-clean/.docs/introduction.md @@ -2,13 +2,88 @@ ## Chars -The Java `char` type represents the smallest addressable components of text. -Multiple `char`s can comprise a string such as `"word"` or `char`s can be processed independently. -Their literals have single quotes e.g. `'A'`. +### chars + +The Java `char` primitive type is a 16 bit representation of a single character. +Multiple `char`s can comprise a string, such as `"word"`, or `char`s can be processed independently. +A `char` literal is surrounded by single quotes (e.g. `'A'`). + +```java +char lowerA = 'a'; +char upperB = 'B'; +``` + +### Getting the `char`s of a `String` + +The `String.toCharArray` method returns a String's chars as an array. +As mentioned in arrays, you can use a `for` loop to iterate over the array. + +```java +String text = "Hello"; +char[] asArray = text.toCharArray(); + +for (char ch: asArray) { + System.out.println(ch); +} + +// Outputs: +// H +// e +// l +// l +// o +``` + +### The Character class There are many builtin library methods to inspect and manipulate `char`s. These can be found as static methods of the `java.lang.Character` class. +Here are some examples: + +```java +Character.isWhitespace(' '); // true +Character.isWhitespace('#'); // false + +Character.isLetter('a'); // true +Character.isLetter('3'); // false + +Character.isDigit('6'); // true +Character.isDigit('?'); // false +``` + +### Adding a `char` to a `String` + +The `+` operator can be used to add a `char` to a `String`. + +```java +'a' + " banana" // => "a banana" +"banana " + 'a' // => "banana a" +``` + +~~~~exercism/caution +Becareful _not_ to use `+` to join two `char`s together to form a `String`! +Adding two `char`s this way gives an `int`, _not_ a `String`! +For example: + +```java +'b' + 'c'; +// => 197 (not the String "bc") +``` + +This is because Java promotes the `char` to an `int` (see [4.2 Primitive Types and Values ][jls-primitives] of the [Java Language Specification][jls-main]). + +[jls-main]: https://docs.oracle.com/javase/specs/jls/se21/html/ +[jls-primitives]: https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2 +~~~~ + +However, when there are many characters to be added, it can be more efficient to use a `StringBuilder` instead: + +```java +StringBuilder builder = new StringBuilder(); +builder.append('a'); +builder.append('b'); +builder.append('c'); -`char`s are sometimes used in conjunction with a `StringBuilder` object. -This object has methods that allow a string to be constructed character by character and manipulated. -At the end of the process `toString` can be called on it to output a complete string. +String builtString = builder.toString(); +// => abc +``` diff --git a/exercises/concept/squeaky-clean/.meta/config.json b/exercises/concept/squeaky-clean/.meta/config.json index b8380b192..70df5ac7d 100644 --- a/exercises/concept/squeaky-clean/.meta/config.json +++ b/exercises/concept/squeaky-clean/.meta/config.json @@ -4,6 +4,7 @@ ], "contributors": [ "jagdish-15", + "kahgoh", "manumafe98", "mrDonoghue", "sanderploegsma" From cb30e854fe2f8dadea68b02670924d3e9137eb97 Mon Sep 17 00:00:00 2001 From: Jagdish Prajapati Date: Mon, 15 Sep 2025 19:39:34 +0530 Subject: [PATCH 67/67] Run all tests if gradle file is changed (#3006) The tests are run using the `test-with-test-runner` script so that they are run with the Docker test runner. This ensures that changes to Gradle are tested in the test runner environment. --- .github/workflows/java.yml | 2 +- bin/test-changed-exercise | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index a966405ee..ad374a4f0 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -55,7 +55,7 @@ jobs: if: failure() test-changed: - name: Test changed exercises using java-test-runner + name: Test changed exercises using gradlew if: github.event_name == 'pull_request' runs-on: ubuntu-24.04 steps: diff --git a/bin/test-changed-exercise b/bin/test-changed-exercise index 776e940e1..debefeba3 100755 --- a/bin/test-changed-exercise +++ b/bin/test-changed-exercise @@ -13,9 +13,16 @@ MERGE_BASE=$(git merge-base HEAD origin/"$BASE_BRANCH") # Get changed files relative to merge base changed_files=$(git diff --name-only "$MERGE_BASE" HEAD) +# If any Gradle build file changed, run the full suite and exit +if echo "$changed_files" | grep -qE '\.(gradle|gradlew|bat)$|settings\.gradle'; then + echo "Gradle build files changed, running full test suite..." + ./bin/test-with-test-runner + exit 0 +fi + # Extract unique exercise directories changed_exercises=$(echo "$changed_files" | \ - grep -E '^exercises/(practice|concept)/[^/]+/.+\.(java|gradle)$' | \ + grep -E '^exercises/(practice|concept)/[^/]+/.+\.java$' | \ cut -d/ -f1-3 | sort -u) if [ -z "$changed_exercises" ]; then