diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e927dd613..d157b4521 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,6 +4,6 @@ updates: - package-ecosystem: 'github-actions' directory: '/' schedule: - interval: 'daily' + interval: 'monthly' labels: - 'x:size/tiny' diff --git a/.github/workflows/check-deprecated-exercises.yml b/.github/workflows/check-deprecated-exercises.yml new file mode 100644 index 000000000..0f011b79e --- /dev/null +++ b/.github/workflows/check-deprecated-exercises.yml @@ -0,0 +1,21 @@ +name: Deprecated + +on: + pull_request: + paths: + - "exercises/**" + push: + branches: + - main + workflow_dispatch: + +jobs: + test-deprecated: + name: Check for deprecated exercises + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + with: + fetch-depth: 0 + - name: Test deprecated exercises using test-deprecated-exercises + run: bin/test-deprecated-exercises diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml index e1e1a51e8..6dc57d7a0 100644 --- a/.github/workflows/java.yml +++ b/.github/workflows/java.yml @@ -13,11 +13,11 @@ 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@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Set up JDK 1.17 - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: java-version: 17 distribution: "temurin" @@ -27,11 +27,11 @@ jobs: lint: name: Lint Java files using Checkstyle - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Set up JDK 1.17 - uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 + uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 with: java-version: 17 distribution: "temurin" @@ -39,16 +39,60 @@ jobs: run: ./gradlew check --exclude-task test --continue working-directory: exercises - test: + test-all: name: Test all exercises using java-test-runner - runs-on: ubuntu-22.04 + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Test all exercises using java-test-runner run: bin/test-with-test-runner + - name: Print summary + run: | + if [ -f exercises/build/summary.txt ]; then + echo "===== TEST SUMMARY =====" + cat exercises/build/summary.txt + echo "========================" + else + echo "===== ALL TESTS PASSED =====" + echo "No summary file was generated." + echo "=============================" + fi + if: always() - name: Archive test results - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f with: name: test-results path: exercises/**/build/results.json if: failure() + + test-changed: + name: Test changed exercises using gradlew + if: github.event_name == 'pull_request' + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 + with: + fetch-depth: 0 + - name: Test changed exercises using java-test-runner + run: bin/test-changed-exercise + - name: Print summary + run: | + if [ -f exercises/build/summary.txt ]; then + echo "===== TEST SUMMARY =====" + cat exercises/build/summary.txt + echo "========================" + else + echo "===== ALL TESTS PASSED =====" + echo "No summary file was generated." + echo "=============================" + fi + if: always() + - name: Archive test results + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: test-results + path: | + exercises/**/build/results.txt + exercises/**/build/results.json + if: failure() diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index 5e323f8da..8339bd4db 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -15,8 +15,8 @@ permissions: jobs: lint: name: Lint Markdown files - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Lint markdown - uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 + uses: DavidAnson/markdownlint-cli2-action@07035fd053f7be764496c0f8d8f9f41f98305101 diff --git a/.github/workflows/ping-cross-track-maintainers-team.yml b/.github/workflows/ping-cross-track-maintainers-team.yml new file mode 100644 index 000000000..b6ec9c566 --- /dev/null +++ b/.github/workflows/ping-cross-track-maintainers-team.yml @@ -0,0 +1,16 @@ +name: Ping cross-track maintainers team + +on: + pull_request_target: + types: + - opened + +permissions: + pull-requests: write + +jobs: + ping: + if: github.repository_owner == 'exercism' # Stops this job from running on forks + uses: exercism/github-actions/.github/workflows/ping-cross-track-maintainers-team.yml@main + secrets: + github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }} 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de4283ca1..56151b2c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,7 +209,7 @@ Each problem/submodule has three source sets: ### Update/sync Gradle versions -Please read [How to Update Gradle](../reference/how-to-update-gradle.md) +Please read [How to Update Gradle](reference/how-to-update-gradle.md) ## Contributing to Concept Exercises diff --git a/POLICIES.md b/POLICIES.md index fa0b5132b..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/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 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/bin/test-changed-exercise b/bin/test-changed-exercise new file mode 100755 index 000000000..b18a488e8 --- /dev/null +++ b/bin/test-changed-exercise @@ -0,0 +1,78 @@ +#!/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) + +# 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$' | \ + 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 "----------------------------------------" + +summary_dir="exercises/build" +summary_file="${summary_dir}/summary.txt" +mkdir -p "$summary_dir" + +# Run tests +exit_code=0 +for dir in $changed_exercises; do + slug=$(basename "$dir") + + echo "========================================" + echo "=== Running tests for $slug ===" + echo "========================================" + + results_path="$dir/build/results.txt" + mkdir -p "$(dirname "$results_path")" + + if [[ $dir == exercises/practice/* ]]; then + ./exercises/gradlew -p exercises ":practice:$slug:test" 2>&1 | tee "$results_path" || true + elif [[ $dir == exercises/concept/* ]]; then + ./exercises/gradlew -p exercises ":concept:$slug:test" 2>&1 | tee "$results_path" || true + fi + + # Detect failure + if grep -q "FAILED" "$results_path"; then + exit_code=1 + + # Determine practice/slug or concept/slug + relative_path=$(echo "$dir" | sed 's|^exercises/||') + + # Create summary.txt with header only on first failure + if [ ! -f "$summary_file" ]; then + echo "The following exercises have test failures or test errors:" > "$summary_file" + fi + + # Append the correct path (practice/slug or concept/slug) + echo "$relative_path" >> "$summary_file" + fi + +done + +exit $exit_code \ No newline at end of file diff --git a/bin/test-deprecated-exercises b/bin/test-deprecated-exercises new file mode 100755 index 000000000..5260d6b6c --- /dev/null +++ b/bin/test-deprecated-exercises @@ -0,0 +1,42 @@ +#!/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)/' || true) +changed_exercises=$(echo "$changed_exercises" | cut -d/ -f1-3 | sort -u) + +# Early exit if no exercise changed +if [ -z "$changed_exercises" ]; then + echo "No exercises changed!" + exit 0 +fi + +# Load deprecated exercises from config.json +deprecated_exercises=$(jq -r ' + [ + (.exercises.concept[]? | select(.status=="deprecated") | "exercises/concept/" + .slug), + (.exercises.practice[]? | select(.status=="deprecated") | "exercises/practice/" + .slug) + ] | .[] +' config.json) + +# Check for deprecated ones +for ex in $changed_exercises; do + if echo "$deprecated_exercises" | grep -qx "$ex"; then + echo "❌ Deprecated exercise changed: $ex" + exit 1 + fi +done + +echo "✅ No deprecated exercises changed!" diff --git a/bin/test-with-test-runner b/bin/test-with-test-runner index eed2d74b0..5e5dc8889 100755 --- a/bin/test-with-test-runner +++ b/bin/test-with-test-runner @@ -12,6 +12,10 @@ docker pull exercism/java-test-runner exit_code=0 +summary_dir="exercises/build" +summary_file="${summary_dir}/summary.txt" +mkdir -p "$summary_dir" + function run_test_runner() { local slug=$1 local solution_dir=$2 @@ -56,6 +60,18 @@ function verify_exercise() { if [[ $(jq -r '.status' "${results_file}") != "pass" ]]; then echo "${slug}: ${implementation_file_key} solution did not pass the tests" + + # Determine practice/slug or concept/slug + local relative_path=$(echo "$dir" | sed -E 's|.*/exercises/||') + + # Create summary.txt with header only on first failure + if [ ! -f "$summary_file" ]; then + echo "The following exercises have test failures or test errors:" > "$summary_file" + fi + + # Append the correct path (practice/slug or concept/slug) + echo "$relative_path" >> "$summary_file" + exit_code=1 fi diff --git a/concepts/arrays/about.md b/concepts/arrays/about.md index b7588364b..45a5862fe 100644 --- a/concepts/arrays/about.md +++ b/concepts/arrays/about.md @@ -1,7 +1,7 @@ # About Arrays -In Java, data structures that can hold zero or more elements are known as _collections_. -An **array** is a collection that has a fixed size and whose elements must all be of the same type. +In Java, arrays are a way to store multiple values of the same type in a single structure. +Unlike other data structures, arrays have a fixed size once created. Elements can be assigned to an array or retrieved from it using an index. Java arrays use zero-based indexing: the first element's index is 0, the second element's index is 1, etc. @@ -44,7 +44,8 @@ int secondElement = twoInts[1]; Accessing an index that is outside of the valid indexes for the array results in an `IndexOutOfBoundsException`. Arrays can be manipulated by either calling an array instance's methods or properties, or by using the static methods defined in the `Arrays` class (typically only used in generic code). -The most commonly used property for arrays is its length which can be accessed like this: +The `length` property holds the length of an array. +It can be accessed like this: ```java int arrayLength = someArray.length; diff --git a/concepts/arrays/introduction.md b/concepts/arrays/introduction.md index 218b91869..b83cac31f 100644 --- a/concepts/arrays/introduction.md +++ b/concepts/arrays/introduction.md @@ -1,7 +1,7 @@ # Introduction to Arrays -In Java, data structures that can hold zero or more elements are known as _collections_. -An **array** is a collection that has a fixed size and whose elements must all be of the same type. +In Java, arrays are a way to store multiple values of the same type in a single structure. +Unlike other data structures, arrays have a fixed size once created. Elements can be assigned to an array or retrieved from it using an index. Java arrays use zero-based indexing: the first element's index is 0, the second element's index is 1, etc. @@ -44,7 +44,8 @@ int secondElement = twoInts[1]; Accessing an index that is outside of the valid indexes for the array results in an `IndexOutOfBoundsException`. Arrays can be manipulated by either calling an array instance's methods or properties, or by using the static methods defined in the `Arrays` class (typically only used in generic code). -The most commonly used property for arrays is its length which can be accessed like this: +The `length` property holds the length of an array. +It can be accessed like this: ```java int arrayLength = someArray.length; 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/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 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/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/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/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..86a820cf1 --- /dev/null +++ b/concepts/maps/about.md @@ -0,0 +1,173 @@ +# 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 [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.keySet(); // 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) +[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 new file mode 100644 index 000000000..60bfbe7e3 --- /dev/null +++ b/concepts/maps/introduction.md @@ -0,0 +1,72 @@ +# 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 [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.keySet(); // 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() +[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/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/concepts/nullability/about.md b/concepts/nullability/about.md index b2443051b..0c12d8b4e 100644 --- a/concepts/nullability/about.md +++ b/concepts/nullability/about.md @@ -2,11 +2,11 @@ In Java, the [`null` literal][null-keyword] is used to denote the absence of a value. -[Primitive variables][primitive-data-types] in java all have a default value and therefore can never be `null`. +[Primitive data types][primitive-data-types] in Java all have a default value and therefore can never be `null`. By convention, they start with a lowercase letter e.g `int`. -[Reference variables][reference-data-types] contain the memory address of an object and can have a value of null. -These variables usually start with an uppercase e.g `String`. +[Reference types][reference-data-types] contain the memory address of an object and can have a value of `null`. +They generally start with an uppercase letter, e.g. `String`. Attempting to assign a primitive variable a value of `null` will result in a compile time error as the variable always holds a default value of the type assigned. diff --git a/concepts/nullability/introduction.md b/concepts/nullability/introduction.md index 82683b4c3..e0b4cff07 100644 --- a/concepts/nullability/introduction.md +++ b/concepts/nullability/introduction.md @@ -2,11 +2,11 @@ In Java, the `null` literal is used to denote the absence of a value. -Primitive data types in java all have a default value and therefore can never be `null`. +Primitive data types in Java all have a default value and therefore can never be `null`. By convention, they start with a lowercase letter e.g `int`. -Reference types contain the memory address of an object can have a value of null. -These variables usually start with an uppercase e.g `String`. +Reference types contain the memory address of an object and can have a value of `null`. +They generally start with an uppercase letter, e.g. `String`. Attempting to assign a primitive variable a value of `null` will result in a compile time error as the variable always holds a primitive value of the type assigned. diff --git a/concepts/switch-statement/.meta/config.json b/concepts/switch-statement/.meta/config.json index ce9f08c55..62882afb5 100644 --- a/concepts/switch-statement/.meta/config.json +++ b/concepts/switch-statement/.meta/config.json @@ -4,5 +4,6 @@ "Azumix" ], "contributors": [ + "josealonso" ] } diff --git a/concepts/switch-statement/about.md b/concepts/switch-statement/about.md index 14e0f8432..2c89c5c04 100644 --- a/concepts/switch-statement/about.md +++ b/concepts/switch-statement/about.md @@ -145,9 +145,25 @@ 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. + +```java +String dayOfMonth = getDayOfMonth(); +String day = ""; +return switch (day) { + case "Tuesday" when dayOfMonth == 13 -> "Forbidden day!!"; + case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "Week day"; + case "Saturday", "Sunday" -> "Weekend"; + default -> "Unknown"; +}; +``` + +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/ [switch2]: https://howtodoinjava.com/java14/switch-expressions/ [oracle-doc]: https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html +[switch-on-Java-21]: https://blog.adamgamboa.dev/switch-expression-on-java-21/#3-guarded-pattern diff --git a/concepts/switch-statement/links.json b/concepts/switch-statement/links.json index 11502d493..c428d3386 100644 --- a/concepts/switch-statement/links.json +++ b/concepts/switch-statement/links.json @@ -14,5 +14,9 @@ { "url": "https://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html", "description": "oracle-doc" + }, + { + "url": "https://blog.adamgamboa.dev/switch-expression-on-java-21/#3-guarded-pattern", + "description": "switch-on-Java-21" } ] diff --git a/config.json b/config.json index 7e158cde5..484d807d0 100644 --- a/config.json +++ b/config.json @@ -105,13 +105,14 @@ "chars" ], "prerequisites": [ + "arrays", "strings" ], "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" @@ -308,6 +309,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": [ @@ -437,6 +451,17 @@ ], "difficulty": 2 }, + { + "slug": "dot-dsl", + "name": "DOT DSL", + "uuid": "03e1070d-a3ed-4664-9559-283e535c6bc4", + "practices": [], + "prerequisites": [ + "classes", + "lists" + ], + "difficulty": 2 + }, { "slug": "grains", "name": "Grains", @@ -785,6 +810,16 @@ ], "difficulty": 4 }, + { + "slug": "rate-limiter", + "name": "Rate Limiter", + "uuid": "b4b0c60e-4ce1-488e-948f-bcb6821c773c", + "practices": [], + "prerequisites": [ + "generic-types" + ], + "difficulty": 4 + }, { "slug": "rotational-cipher", "name": "Rotational Cipher", @@ -818,6 +853,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", @@ -830,6 +876,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", @@ -916,6 +974,17 @@ ], "difficulty": 5 }, + { + "slug": "game-of-life", + "name": "Conway's Game of Life", + "uuid": "749de7fc-3dcb-4231-9b4f-115d153af74f", + "practices": [], + "prerequisites": [ + "arrays", + "if-statements" + ], + "difficulty": 5 + }, { "slug": "grep", "name": "Grep", @@ -967,7 +1036,8 @@ "practices": [], "prerequisites": [ "if-else-statements", - "for-loops" + "for-loops", + "maps" ], "difficulty": 5 }, @@ -1018,6 +1088,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", @@ -1070,7 +1153,8 @@ "practices": [], "prerequisites": [ "for-loops", - "arrays" + "arrays", + "maps" ], "difficulty": 5 }, @@ -1122,7 +1206,8 @@ "practices": [], "prerequisites": [ "chars", - "exceptions" + "exceptions", + "maps" ], "difficulty": 6 }, @@ -1183,6 +1268,19 @@ "practices": [], "prerequisites": [ "foreach-loops", + "maps", + "strings" + ], + "difficulty": 6 + }, + { + "slug": "flower-field", + "name": "Flower Field", + "uuid": "bddd180a-d634-454a-af03-4d625f77e1e2", + "practices": [], + "prerequisites": [ + "constructors", + "lists", "strings" ], "difficulty": 6 @@ -1220,6 +1318,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", @@ -1237,12 +1343,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", @@ -1250,10 +1353,23 @@ "uuid": "38a405e8-619d-400f-b53c-2f06461fdf9d", "practices": [], "prerequisites": [ + "maps", "strings" ], "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", @@ -1425,7 +1541,8 @@ "uuid": "2e760ae2-fadd-4d31-9639-c4554e2826e9", "practices": [], "prerequisites": [ - "enums" + "enums", + "maps" ], "difficulty": 7 }, @@ -1473,7 +1590,8 @@ "chars", "if-else-statements", "lists", - "for-loops" + "for-loops", + "maps" ], "difficulty": 7 }, @@ -1534,7 +1652,8 @@ "prerequisites": [ "arrays", "strings", - "if-else-statements" + "if-else-statements", + "maps" ], "difficulty": 7 }, @@ -1681,6 +1800,7 @@ "exceptions", "for-loops", "if-else-statements", + "maps", "numbers" ], "difficulty": 8 @@ -1791,17 +1911,6 @@ "lists" ], "difficulty": 10 - }, - { - "slug": "game-of-life", - "name": "Conway's Game of Life", - "uuid": "749de7fc-3dcb-4231-9b4f-115d153af74f", - "practices": [], - "prerequisites": [ - "arrays", - "if-statements" - ], - "difficulty": 5 } ], "foregone": [ @@ -1894,6 +2003,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/docs/INSTALLATION.md b/docs/INSTALLATION.md index 239409351..57cf6d012 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -9,7 +9,7 @@ Here at Exercism we recommend using [Eclipse Temurin by Adoptium][adoptium]. ## Supported Java versions -Exercism's Java track supports Java 21 LTS and earlier. +Exercism's Java track supports Java 25 LTS and earlier. ## Installing Eclipse Temurin diff --git a/docs/TESTS.md b/docs/TESTS.md index d9389bf85..5eca6a558 100644 --- a/docs/TESTS.md +++ b/docs/TESTS.md @@ -81,6 +81,20 @@ Good luck! Have fun! _(Don't worry about the tests failing, at first, this is how you begin each exercise.)_ + If you get the following error: + + ```sh + ./gradlew: Permission denied + ``` + + Then the file is missing the execute permission. To fis this, run: + + ```sh + chmod +x ./gradlew + ``` + + And now you should be able to run the previous command. + 4. Solve the exercise. Find and work through the `instructions.append.md` guide ([view on GitHub][hello-world-tutorial]). Good luck! Have fun! diff --git a/exercises/build.gradle b/exercises/build.gradle index 734815214..c35dc4fc1 100644 --- a/exercises/build.gradle +++ b/exercises/build.gradle @@ -79,7 +79,7 @@ subprojects { // configuration of the linter checkstyle { toolVersion '10.7.0' - configFile file("checkstyle.xml") + configFile file("$rootDir/checkstyle.xml") sourceSets = [project.sourceSets.main, project.sourceSets.test] } diff --git a/exercises/checkstyle.xml b/exercises/checkstyle.xml index aee09b5d8..8746c0e28 100644 --- a/exercises/checkstyle.xml +++ b/exercises/checkstyle.xml @@ -155,7 +155,7 @@ page at http://checkstyle.sourceforge.net/config.html --> - + diff --git a/exercises/concept/annalyns-infiltration/.docs/instructions.md b/exercises/concept/annalyns-infiltration/.docs/instructions.md index 4ae00ea14..f8e35fd96 100644 --- a/exercises/concept/annalyns-infiltration/.docs/instructions.md +++ b/exercises/concept/annalyns-infiltration/.docs/instructions.md @@ -1,26 +1,35 @@ # Instructions -In this exercise, you'll be implementing the quest logic for a new RPG game a friend is developing. The game's main character is Annalyn, a brave girl with a fierce and loyal pet dog. Unfortunately, disaster strikes, as her best friend was kidnapped while searching for berries in the forest. Annalyn will try to find and free her best friend, optionally taking her dog with her on this quest. +In this exercise, you'll implement the quest logic for a new RPG game that a friend is developing. +The game's main character is Annalyn, a brave girl with a fierce and loyal pet dog. +Unfortunately, disaster strikes: her best friend was kidnapped while searching for berries in the forest. +Annalyn will try to find and rescue her friend, optionally taking her dog along on the quest. -After some time spent following her best friend's trail, she finds the camp in which her best friend is imprisoned. It turns out there are two kidnappers: a mighty knight and a cunning archer. +After some time spent following the trail, Annalyn discovers the camp where her friend is imprisoned. +It turns out there are two kidnappers: a mighty knight and a cunning archer. Having found the kidnappers, Annalyn considers which of the following actions she can engage in: -- Fast attack: a fast attack can be made if the knight is sleeping, as it takes time for him to get his armor on, so he will be vulnerable. -- Spy: the group can be spied upon if at least one of them is awake. Otherwise, spying is a waste of time. -- Signal prisoner: the prisoner can be signalled using bird sounds if the prisoner is awake and the archer is sleeping, as archers are trained in bird signaling, so they could intercept the message. -- _Free prisoner_: Annalyn can try sneaking into the camp to free the prisoner. - This is a risky thing to do and can only succeed in one of two ways: - - If Annalyn has her pet dog with her she can rescue the prisoner if the archer is asleep. +- Fast attack: a fast attack can be made if the knight is sleeping, as it takes time for him to put on his armor, leaving him vulnerable. +- Spy: the group can be spied upon if at least one of them is awake. + Otherwise, spying is a waste of time. +- Signal prisoner: the prisoner can be signaled using bird sounds if the prisoner is awake and the archer is sleeping. + Archers are trained in bird signaling and could intercept the message if they are awake. +- _Free prisoner_: Annalyn can attempt to sneak into the camp to free the prisoner. + This is risky and can only succeed in one of two ways: + - If Annalyn has her pet dog, she can rescue the prisoner if the archer is asleep. The knight is scared of the dog and the archer will not have time to get ready before Annalyn and the prisoner can escape. - - If Annalyn does not have her dog then she and the prisoner must be very sneaky! - Annalyn can free the prisoner if the prisoner is awake and the knight and archer are both sleeping, but if the prisoner is sleeping they can't be rescued: the prisoner would be startled by Annalyn's sudden appearance and wake up the knight and archer. + - If Annalyn does not have her pet dog, then she and the prisoner must be very sneaky! + Annalyn can free the prisoner if the prisoner is awake and both the knight and archer are sleeping. + However, if the prisoner is sleeping, they can't be rescued, as the prisoner would be startled by Annalyn's sudden appearance and wake up the knight and archer. -You have four tasks: to implement the logic for determining if the above actions are available based on the state of the three characters found in the forest and whether Annalyn's pet dog is present or not. +You have four tasks: to implement the logic for determining if the above actions are available based on the state of the three characters in the forest and whether Annalyn's pet dog is present or not. ## 1. Check if a fast attack can be made -Implement the (_static_) `AnnalynsInfiltration.canFastAttack()` method that takes a boolean value that indicates if the knight is awake. This method returns `true` if a fast attack can be made based on the state of the knight. Otherwise, returns `false`: +Implement the (_static_) `AnnalynsInfiltration.canFastAttack()` method, which takes a boolean value indicating whether the knight is awake. +This method returns `true` if a fast attack can be made based on the state of the knight. +Otherwise, it returns `false`: ```java boolean knightIsAwake = true; @@ -30,7 +39,9 @@ AnnalynsInfiltration.canFastAttack(knightIsAwake); ## 2. Check if the group can be spied upon -Implement the (_static_) `AnnalynsInfiltration.canSpy()` method that takes three boolean values, indicating if the knight, archer and the prisoner, respectively, are awake. The method returns `true` if the group can be spied upon, based on the state of the three characters. Otherwise, returns `false`: +Implement the (_static_) `AnnalynsInfiltration.canSpy()` method, which takes three boolean values indicating whether the knight, archer, and prisoner, respectively, are awake. +The method returns `true` if the group can be spied upon based on the state of the three characters. +Otherwise, it returns `false`: ```java boolean knightIsAwake = false; @@ -40,9 +51,11 @@ AnnalynsInfiltration.canSpy(knightIsAwake, archerIsAwake, prisonerIsAwake); // => true ``` -## 3. Check if the prisoner can be signalled +## 3. Check if the prisoner can be signaled -Implement the (_static_) `AnnalynsInfiltration.canSignalPrisoner()` method that takes two boolean values, indicating if the archer and the prisoner, respectively, are awake. The method returns `true` if the prisoner can be signalled, based on the state of the two characters. Otherwise, returns `false`: +Implement the (_static_) `AnnalynsInfiltration.canSignalPrisoner()` method, which takes two boolean values indicating whether the archer and the prisoner, respectively, are awake. +The method returns `true` if the prisoner can be signaled based on the state of the two characters. +Otherwise, it returns `false`: ```java boolean archerIsAwake = false; @@ -53,7 +66,11 @@ AnnalynsInfiltration.canSignalPrisoner(archerIsAwake, prisonerIsAwake); ## 4. Check if the prisoner can be freed -Implement the (_static_) `AnnalynsInfiltration.canFreePrisoner()` method that takes four boolean values. The first three parameters indicate if the knight, archer and the prisoner, respectively, are awake. The last parameter indicates if Annalyn's pet dog is present. The method returns `true` if the prisoner can be freed based on the state of the three characters and Annalyn's pet dog's presence. Otherwise, it returns `false`: +Implement the (_static_) `AnnalynsInfiltration.canFreePrisoner()` method, which takes four boolean values. +The first three parameters indicate whether the knight, archer, and prisoner, respectively, are awake. +The last parameter indicates whether Annalyn's pet dog is present. +The method returns `true` if the prisoner can be freed based on the state of the three characters and the presence of Annalyn's pet dog. +Otherwise, it returns `false`: ```java boolean knightIsAwake = false; diff --git a/exercises/concept/annalyns-infiltration/.docs/introduction.md b/exercises/concept/annalyns-infiltration/.docs/introduction.md index 46597333e..6ff36c4db 100644 --- a/exercises/concept/annalyns-infiltration/.docs/introduction.md +++ b/exercises/concept/annalyns-infiltration/.docs/introduction.md @@ -7,8 +7,8 @@ Booleans in Java are represented by the `boolean` type, which values can be eith Java supports three boolean operators: - `!` (NOT): negates the boolean -- `&&` (AND): takes two booleans and results in true if they're both true -- `||` (OR): results in true if any of the two booleans is true +- `&&` (AND): takes two booleans and returns `true` if they're both `true` +- `||` (OR): returns `true` if any of the two booleans is `true` ### Examples 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/annalyns-infiltration/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/annalyns-infiltration/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/annalyns-infiltration/gradlew b/exercises/concept/annalyns-infiltration/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/annalyns-infiltration/gradlew +++ b/exercises/concept/annalyns-infiltration/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/annalyns-infiltration/gradlew.bat b/exercises/concept/annalyns-infiltration/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/annalyns-infiltration/gradlew.bat +++ b/exercises/concept/annalyns-infiltration/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/annalyns-infiltration/src/test/java/AnnalynsInfiltrationTest.java b/exercises/concept/annalyns-infiltration/src/test/java/AnnalynsInfiltrationTest.java index 4f7fe3864..7514451bf 100644 --- a/exercises/concept/annalyns-infiltration/src/test/java/AnnalynsInfiltrationTest.java +++ b/exercises/concept/annalyns-infiltration/src/test/java/AnnalynsInfiltrationTest.java @@ -9,7 +9,7 @@ public class AnnalynsInfiltrationTest { @Test @Tag("task:1") @DisplayName("The canFastAttack method returns false when knight is awake") - public void cannot_execute_fast_attack_if_knight_is_awake() { + public void cannotExecuteFastAttackIfKnightIsAwake() { boolean knightIsAwake = true; assertThat(AnnalynsInfiltration.canFastAttack(knightIsAwake)).isFalse(); } @@ -17,7 +17,7 @@ public void cannot_execute_fast_attack_if_knight_is_awake() { @Test @Tag("task:1") @DisplayName("The canFastAttack method returns true when knight is sleeping") - public void can_execute_fast_attack_if_knight_is_sleeping() { + public void canExecuteFastAttackIfKnightIsSleeping() { boolean knightIsAwake = false; assertThat(AnnalynsInfiltration.canFastAttack(knightIsAwake)).isTrue(); } @@ -25,7 +25,7 @@ public void can_execute_fast_attack_if_knight_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns false when everyone is sleeping") - public void cannot_spy_if_everyone_is_sleeping() { + public void cannotSpyIfEveryoneIsSleeping() { boolean knightIsAwake = false; boolean archerIsAwake = false; boolean prisonerIsAwake = false; @@ -35,7 +35,7 @@ public void cannot_spy_if_everyone_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when everyone but knight is sleeping") - public void can_spy_if_everyone_but_knight_is_sleeping() { + public void canSpyIfEveryoneButKnightIsSleeping() { boolean knightIsAwake = true; boolean archerIsAwake = false; boolean prisonerIsAwake = false; @@ -45,7 +45,7 @@ public void can_spy_if_everyone_but_knight_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when everyone but archer is sleeping") - public void can_spy_if_everyone_but_archer_is_sleeping() { + public void canSpyIfEveryoneButArcherIsSleeping() { boolean knightIsAwake = false; boolean archerIsAwake = true; boolean prisonerIsAwake = false; @@ -55,7 +55,7 @@ public void can_spy_if_everyone_but_archer_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when everyone but prisoner is sleeping") - public void can_spy_if_everyone_but_prisoner_is_sleeping() { + public void canSpyIfEveryoneButPrisonerIsSleeping() { boolean knightIsAwake = false; boolean archerIsAwake = false; boolean prisonerIsAwake = true; @@ -65,7 +65,7 @@ public void can_spy_if_everyone_but_prisoner_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when only knight is sleeping") - public void can_spy_if_only_knight_is_sleeping() { + public void canSpyIfOnlyKnightIsSleeping() { boolean knightIsAwake = false; boolean archerIsAwake = true; boolean prisonerIsAwake = true; @@ -75,7 +75,7 @@ public void can_spy_if_only_knight_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when only archer is sleeping") - public void can_spy_if_only_archer_is_sleeping() { + public void canSpyIfOnlyArcherIsSleeping() { boolean knightIsAwake = true; boolean archerIsAwake = false; boolean prisonerIsAwake = true; @@ -85,7 +85,7 @@ public void can_spy_if_only_archer_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when only prisoner is sleeping") - public void can_spy_if_only_prisoner_is_sleeping() { + public void canSpyIfOnlyPrisonerIsSleeping() { boolean knightIsAwake = true; boolean archerIsAwake = true; boolean prisonerIsAwake = false; @@ -95,7 +95,7 @@ public void can_spy_if_only_prisoner_is_sleeping() { @Test @Tag("task:2") @DisplayName("The canSpy method returns true when everyone is awake") - public void can_spy_if_everyone_is_awake() { + public void canSpyIfEveryoneIsAwake() { boolean knightIsAwake = true; boolean archerIsAwake = true; boolean prisonerIsAwake = true; @@ -105,7 +105,7 @@ public void can_spy_if_everyone_is_awake() { @Test @Tag("task:3") @DisplayName("The canSignalPrisoner method returns true when prisoner is awake and archer is sleeping") - public void can_signal_prisoner_if_archer_is_sleeping_and_prisoner_is_awake() { + public void canSignalPrisonerIfArcherIsSleepingAndPrisonerIsAwake() { boolean archerIsAwake = false; boolean prisonerIsAwake = true; assertThat(AnnalynsInfiltration.canSignalPrisoner(archerIsAwake, prisonerIsAwake)).isTrue(); @@ -114,7 +114,7 @@ public void can_signal_prisoner_if_archer_is_sleeping_and_prisoner_is_awake() { @Test @Tag("task:3") @DisplayName("The canSignalPrisoner method returns false when prisoner is sleeping and archer is awake") - public void cannot_signal_prisoner_if_archer_is_awake_and_prisoner_is_sleeping() { + public void cannotSignalPrisonerIfArcherIsAwakeAndPrisonerIsSleeping() { boolean archerIsAwake = true; boolean prisonerIsAwake = false; assertThat(AnnalynsInfiltration.canSignalPrisoner(archerIsAwake, prisonerIsAwake)).isFalse(); @@ -123,7 +123,7 @@ public void cannot_signal_prisoner_if_archer_is_awake_and_prisoner_is_sleeping() @Test @Tag("task:3") @DisplayName("The canSignalPrisoner method returns false when both prisoner and archer are sleeping") - public void cannot_signal_prisoner_if_archer_and_prisoner_are_both_sleeping() { + public void cannotSignalPrisonerIfArcherAndPrisonerAreBothSleeping() { boolean archerIsAwake = false; boolean prisonerIsAwake = false; assertThat(AnnalynsInfiltration.canSignalPrisoner(archerIsAwake, prisonerIsAwake)).isFalse(); @@ -132,7 +132,7 @@ public void cannot_signal_prisoner_if_archer_and_prisoner_are_both_sleeping() { @Test @Tag("task:3") @DisplayName("The canSignalPrisoner method returns false when both prisoner and archer are awake") - public void cannot_signal_prisoner_if_archer_and_prisoner_are_both_awake() { + public void cannotSignalPrisonerIfArcherAndPrisonerAreBothAwake() { boolean archerIsAwake = true; boolean prisonerIsAwake = true; assertThat(AnnalynsInfiltration.canSignalPrisoner(archerIsAwake, prisonerIsAwake)).isFalse(); @@ -141,7 +141,7 @@ public void cannot_signal_prisoner_if_archer_and_prisoner_are_both_awake() { @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when everyone is awake and pet dog is present") - public void cannot_release_prisoner_if_everyone_is_awake_and_pet_dog_is_present() { + public void cannotReleasePrisonerIfEveryoneIsAwakeAndPetDogIsPresent() { boolean knightIsAwake = true; boolean archerIsAwake = true; boolean prisonerIsAwake = true; @@ -153,7 +153,7 @@ public void cannot_release_prisoner_if_everyone_is_awake_and_pet_dog_is_present( @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when everyone is awake and pet dog is absent") - public void cannot_release_prisoner_if_everyone_is_awake_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfEveryoneIsAwakeAndPetDogIsAbsent() { boolean knightIsAwake = true; boolean archerIsAwake = true; boolean prisonerIsAwake = true; @@ -165,7 +165,7 @@ public void cannot_release_prisoner_if_everyone_is_awake_and_pet_dog_is_absent() @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns true when everyone is sleeping and pet dog is present") - public void can_release_prisoner_if_everyone_is_asleep_and_pet_dog_is_present() { + public void canReleasePrisonerIfEveryoneIsAsleepAndPetDogIsPresent() { boolean knightIsAwake = false; boolean archerIsAwake = false; boolean prisonerIsAwake = false; @@ -177,7 +177,7 @@ public void can_release_prisoner_if_everyone_is_asleep_and_pet_dog_is_present() @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when everyone is sleeping and pet dog is absent") - public void cannot_release_prisoner_if_everyone_is_asleep_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfEveryoneIsAsleepAndPetDogIsAbsent() { boolean knightIsAwake = false; boolean archerIsAwake = false; boolean prisonerIsAwake = false; @@ -189,7 +189,7 @@ public void cannot_release_prisoner_if_everyone_is_asleep_and_pet_dog_is_absent( @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns true when only prisoner is awake and pet dog is present") - public void can_release_prisoner_if_only_prisoner_is_awake_and_pet_dog_is_present() { + public void canReleasePrisonerIfOnlyPrisonerIsAwakeAndPetDogIsPresent() { boolean knightIsAwake = false; boolean archerIsAwake = false; boolean prisonerIsAwake = true; @@ -201,7 +201,7 @@ public void can_release_prisoner_if_only_prisoner_is_awake_and_pet_dog_is_presen @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns true when only prisoner is awake and pet dog is absent") - public void can_release_prisoner_if_only_prisoner_is_awake_and_pet_dog_is_absent() { + public void canReleasePrisonerIfOnlyPrisonerIsAwakeAndPetDogIsAbsent() { boolean knightIsAwake = false; boolean archerIsAwake = false; boolean prisonerIsAwake = true; @@ -213,7 +213,7 @@ public void can_release_prisoner_if_only_prisoner_is_awake_and_pet_dog_is_absent @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only archer is awake and pet dog is present") - public void cannot_release_prisoner_if_only_archer_is_awake_and_pet_dog_is_present() { + public void cannotReleasePrisonerIfOnlyArcherIsAwakeAndPetDogIsPresent() { boolean knightIsAwake = false; boolean archerIsAwake = true; boolean prisonerIsAwake = false; @@ -225,7 +225,7 @@ public void cannot_release_prisoner_if_only_archer_is_awake_and_pet_dog_is_prese @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only archer is awake and pet dog is absent") - public void cannot_release_prisoner_if_only_archer_is_awake_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfOnlyArcherIsAwakeAndPetDogIsAbsent() { boolean knightIsAwake = false; boolean archerIsAwake = true; boolean prisonerIsAwake = false; @@ -237,7 +237,7 @@ public void cannot_release_prisoner_if_only_archer_is_awake_and_pet_dog_is_absen @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns true when only knight is awake and pet dog is present") - public void can_release_prisoner_if_only_knight_is_awake_and_pet_dog_is_present() { + public void canReleasePrisonerIfOnlyKnightIsAwakeAndPetDogIsPresent() { boolean knightIsAwake = true; boolean archerIsAwake = false; boolean prisonerIsAwake = false; @@ -249,7 +249,7 @@ public void can_release_prisoner_if_only_knight_is_awake_and_pet_dog_is_present( @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only knight is awake and pet dog is absent") - public void cannot_release_prisoner_if_only_knight_is_awake_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfOnlyKnightIsAwakeAndPetDogIsAbsent() { boolean knightIsAwake = true; boolean archerIsAwake = false; boolean prisonerIsAwake = false; @@ -261,7 +261,7 @@ public void cannot_release_prisoner_if_only_knight_is_awake_and_pet_dog_is_absen @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only knight is sleeping and pet dog is present") - public void cannot_release_prisoner_if_only_knight_is_asleep_and_pet_dog_is_present() { + public void cannotReleasePrisonerIfOnlyKnightIsAsleepAndPetDogIsPresent() { boolean knightIsAwake = false; boolean archerIsAwake = true; boolean prisonerIsAwake = true; @@ -273,7 +273,7 @@ public void cannot_release_prisoner_if_only_knight_is_asleep_and_pet_dog_is_pres @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only knight is sleeping and pet dog is absent") - public void cannot_release_prisoner_if_only_knight_is_asleep_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfOnlyKnightIsAsleepAndPetDogIsAbsent() { boolean knightIsAwake = false; boolean archerIsAwake = true; boolean prisonerIsAwake = true; @@ -285,7 +285,7 @@ public void cannot_release_prisoner_if_only_knight_is_asleep_and_pet_dog_is_abse @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns true when only archer is sleeping and pet dog is present") - public void can_release_prisoner_if_only_archer_is_asleep_and_pet_dog_is_present() { + public void canReleasePrisonerIfOnlyArcherIsAsleepAndPetDogIsPresent() { boolean knightIsAwake = true; boolean archerIsAwake = false; boolean prisonerIsAwake = true; @@ -297,7 +297,7 @@ public void can_release_prisoner_if_only_archer_is_asleep_and_pet_dog_is_present @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only archer is sleeping and pet dog is absent") - public void cannot_release_prisoner_if_only_archer_is_asleep_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfOnlyArcherIsAsleepAndPetDogIsAbsent() { boolean knightIsAwake = true; boolean archerIsAwake = false; boolean prisonerIsAwake = true; @@ -309,7 +309,7 @@ public void cannot_release_prisoner_if_only_archer_is_asleep_and_pet_dog_is_abse @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only prisoner is sleeping and pet dog is present") - public void cannot_release_prisoner_if_only_prisoner_is_asleep_and_pet_dog_is_present() { + public void cannotReleasePrisonerIfOnlyPrisonerIsAsleepAndPetDogIsPresent() { boolean knightIsAwake = true; boolean archerIsAwake = true; boolean prisonerIsAwake = false; @@ -321,7 +321,7 @@ public void cannot_release_prisoner_if_only_prisoner_is_asleep_and_pet_dog_is_pr @Test @Tag("task:4") @DisplayName("The canFreePrisoner method returns false when only prisoner is sleeping and pet dog is absent") - public void cannot_release_prisoner_if_only_prisoner_is_asleep_and_pet_dog_is_absent() { + public void cannotReleasePrisonerIfOnlyPrisonerIsAsleepAndPetDogIsAbsent() { boolean knightIsAwake = true; boolean archerIsAwake = true; boolean prisonerIsAwake = false; diff --git a/exercises/concept/bird-watcher/.docs/introduction.md b/exercises/concept/bird-watcher/.docs/introduction.md index c8b1bddbc..9cf029fc5 100644 --- a/exercises/concept/bird-watcher/.docs/introduction.md +++ b/exercises/concept/bird-watcher/.docs/introduction.md @@ -2,8 +2,8 @@ ## Arrays -In Java, data structures that can hold zero or more elements are known as _collections_. -An **array** is a collection that has a fixed size and whose elements must all be of the same type. +In Java, arrays are a way to store multiple values of the same type in a single structure. +Unlike other data structures, arrays have a fixed size once created. Elements can be assigned to an array or retrieved from it using an index. Java arrays use zero-based indexing: the first element's index is 0, the second element's index is 1, etc. @@ -46,7 +46,8 @@ int secondElement = twoInts[1]; Accessing an index that is outside of the valid indexes for the array results in an `IndexOutOfBoundsException`. Arrays can be manipulated by either calling an array instance's methods or properties, or by using the static methods defined in the `Arrays` class (typically only used in generic code). -The most commonly used property for arrays is its length which can be accessed like this: +The `length` property holds the length of an array. +It can be accessed like this: ```java int arrayLength = someArray.length; 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/bird-watcher/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/bird-watcher/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/bird-watcher/gradlew b/exercises/concept/bird-watcher/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/bird-watcher/gradlew +++ b/exercises/concept/bird-watcher/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/bird-watcher/gradlew.bat b/exercises/concept/bird-watcher/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/bird-watcher/gradlew.bat +++ b/exercises/concept/bird-watcher/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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") 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/.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 ``` 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/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/booking-up-for-beauty/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/booking-up-for-beauty/gradlew b/exercises/concept/booking-up-for-beauty/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/booking-up-for-beauty/gradlew +++ b/exercises/concept/booking-up-for-beauty/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/booking-up-for-beauty/gradlew.bat b/exercises/concept/booking-up-for-beauty/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/booking-up-for-beauty/gradlew.bat +++ b/exercises/concept/booking-up-for-beauty/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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. diff --git a/exercises/concept/calculator-conundrum/.meta/config.json b/exercises/concept/calculator-conundrum/.meta/config.json index ab5352d6c..1c6c65d24 100644 --- a/exercises/concept/calculator-conundrum/.meta/config.json +++ b/exercises/concept/calculator-conundrum/.meta/config.json @@ -4,6 +4,7 @@ "jmrunkle" ], "contributors": [ + "jagdish-15", "sanderploegsma" ], "files": { diff --git a/exercises/concept/calculator-conundrum/.meta/design.md b/exercises/concept/calculator-conundrum/.meta/design.md index 5163b002c..5134eb08a 100644 --- a/exercises/concept/calculator-conundrum/.meta/design.md +++ b/exercises/concept/calculator-conundrum/.meta/design.md @@ -18,3 +18,15 @@ - `conditionals-if`: know how to do conditional logic. - `switch-statement`: know how to work with a `switch` statement. + +## Analyzer + +This exercise could benefit from the following rules in the [analyzer]: + +- `essential`: Verify that the solution is using the try catch statement for the division. +- `actionable`: If the solution is wrapping all the code in a try catch statement, instruct the student to only use the `try catch` for the division statement +- `actionable`: If the solution uses only `if` statement, instruct the student that he/she can use the `switch case` statement to perform the operations. +- `informative`: If the solution does not throw the exception for `Operation cannot be null` and `Operation cannot be empty` at the beginning, suggest the fail-fast approach to the student. + Inform this way of implementation can be less error-prone and more readable as suggested by Martin Fowler: + +[analyzer]: https://github.com/exercism/java-analyzer diff --git a/exercises/concept/calculator-conundrum/.meta/src/reference/java/IllegalOperationException.java b/exercises/concept/calculator-conundrum/.meta/src/reference/java/IllegalOperationException.java deleted file mode 120000 index cb8ab2932..000000000 --- a/exercises/concept/calculator-conundrum/.meta/src/reference/java/IllegalOperationException.java +++ /dev/null @@ -1 +0,0 @@ -../../../../src/main/java/IllegalOperationException.java \ No newline at end of file diff --git a/exercises/concept/calculator-conundrum/.meta/src/reference/java/IllegalOperationException.java b/exercises/concept/calculator-conundrum/.meta/src/reference/java/IllegalOperationException.java new file mode 100644 index 000000000..78d0a8e79 --- /dev/null +++ b/exercises/concept/calculator-conundrum/.meta/src/reference/java/IllegalOperationException.java @@ -0,0 +1,9 @@ +public class IllegalOperationException extends RuntimeException { + public IllegalOperationException(String errorMessage) { + super(errorMessage); + } + + public IllegalOperationException(String errorMessage, Throwable cause) { + super(errorMessage, cause); + } +} 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/calculator-conundrum/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/calculator-conundrum/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/calculator-conundrum/gradlew b/exercises/concept/calculator-conundrum/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/calculator-conundrum/gradlew +++ b/exercises/concept/calculator-conundrum/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/calculator-conundrum/gradlew.bat b/exercises/concept/calculator-conundrum/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/calculator-conundrum/gradlew.bat +++ b/exercises/concept/calculator-conundrum/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/captains-log/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/captains-log/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/captains-log/gradlew b/exercises/concept/captains-log/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/captains-log/gradlew +++ b/exercises/concept/captains-log/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/captains-log/gradlew.bat b/exercises/concept/captains-log/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/captains-log/gradlew.bat +++ b/exercises/concept/captains-log/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/cars-assemble/.docs/instructions.md b/exercises/concept/cars-assemble/.docs/instructions.md index a01a68726..5ad3ea97f 100644 --- a/exercises/concept/cars-assemble/.docs/instructions.md +++ b/exercises/concept/cars-assemble/.docs/instructions.md @@ -13,7 +13,7 @@ You have two tasks. ## 1. Calculate the production rate per hour -Implement the `CarsAssemble.productionRatePerHour()` method to calculate the assembly line's production rate per hour, taking into account its current assembly line's speed : +Implement the `CarsAssemble.productionRatePerHour()` method to calculate the assembly line's production rate per hour, taking into account its current assembly line's speed and success rate: ```Java CarsAssemble.productionRatePerHour(6) 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/cars-assemble/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/cars-assemble/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/cars-assemble/gradlew b/exercises/concept/cars-assemble/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/cars-assemble/gradlew +++ b/exercises/concept/cars-assemble/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/cars-assemble/gradlew.bat b/exercises/concept/cars-assemble/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/cars-assemble/gradlew.bat +++ b/exercises/concept/cars-assemble/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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 e6441136f..000000000 Binary files a/exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/exercises/concept/football-match-reports/.docs/instructions.md b/exercises/concept/football-match-reports/.docs/instructions.md index f255c3a13..2c668b663 100644 --- a/exercises/concept/football-match-reports/.docs/instructions.md +++ b/exercises/concept/football-match-reports/.docs/instructions.md @@ -27,11 +27,11 @@ FootballMatchReports.onField(10); // => "striker" ``` -## 2. Raise an alert if an unknown shirt number is encountered +## 2. Output "invalid" if the shirt number is not part of the official list -Modify the `FootballMatchReports.onField()` method to throw an `IllegalArgumentException` when a shirt number outside the range 1-11 is processed. +Modify the `FootballMatchReports.onField()` method to return 'invalid' when a shirt number outside the range 1-11 is processed. ```java FootballMatchReports.onField(13); -// => Throw IllegalArgumentException +// => "invalid" ``` diff --git a/exercises/concept/football-match-reports/.meta/config.json b/exercises/concept/football-match-reports/.meta/config.json index 9b291514e..74d3894b7 100644 --- a/exercises/concept/football-match-reports/.meta/config.json +++ b/exercises/concept/football-match-reports/.meta/config.json @@ -2,6 +2,9 @@ "authors": [ "Azumix" ], + "contributors": [ + "AlvesJorge" + ], "files": { "solution": [ "src/main/java/FootballMatchReports.java" diff --git a/exercises/concept/football-match-reports/.meta/src/reference/java/FootballMatchReports.java b/exercises/concept/football-match-reports/.meta/src/reference/java/FootballMatchReports.java index d18f55ac6..c99abc7b0 100644 --- a/exercises/concept/football-match-reports/.meta/src/reference/java/FootballMatchReports.java +++ b/exercises/concept/football-match-reports/.meta/src/reference/java/FootballMatchReports.java @@ -30,7 +30,7 @@ public static String onField(int shirtNum) { playerDescription = "striker"; break; default: - throw new IllegalArgumentException(); + playerDescription = "invalid"; } return playerDescription; } 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/football-match-reports/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/football-match-reports/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/football-match-reports/gradlew b/exercises/concept/football-match-reports/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/football-match-reports/gradlew +++ b/exercises/concept/football-match-reports/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/football-match-reports/gradlew.bat b/exercises/concept/football-match-reports/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/football-match-reports/gradlew.bat +++ b/exercises/concept/football-match-reports/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/football-match-reports/src/test/java/FootballMatchReportsTest.java b/exercises/concept/football-match-reports/src/test/java/FootballMatchReportsTest.java index aeaeada1d..82024e6f5 100644 --- a/exercises/concept/football-match-reports/src/test/java/FootballMatchReportsTest.java +++ b/exercises/concept/football-match-reports/src/test/java/FootballMatchReportsTest.java @@ -3,35 +3,34 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class FootballMatchReportsTest { @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of player with shirt number 1") - public void test_goal() { + public void testGoal() { assertThat(FootballMatchReports.onField(1)).isEqualTo("goalie"); } @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of player with shirt number 2") - public void test_left_back() { + public void testLeftBack() { assertThat(FootballMatchReports.onField(2)).isEqualTo("left back"); } @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of player with shirt number 5") - public void test_right_back() { + public void testRightBack() { assertThat(FootballMatchReports.onField(5)).isEqualTo("right back"); } @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of players with shirt numbers 3 and 4") - public void test_center_back() { + public void testCenterBack() { assertThat(FootballMatchReports.onField(3)).isEqualTo("center back"); assertThat(FootballMatchReports.onField(4)).isEqualTo("center back"); } @@ -39,7 +38,7 @@ public void test_center_back() { @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of players with shirt numbers 6, 7 and 8") - public void test_midfielder() { + public void testMidfielder() { assertThat(FootballMatchReports.onField(6)).isEqualTo("midfielder"); assertThat(FootballMatchReports.onField(7)).isEqualTo("midfielder"); assertThat(FootballMatchReports.onField(8)).isEqualTo("midfielder"); @@ -48,37 +47,35 @@ public void test_midfielder() { @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of player with shirt number 9") - public void test_left_wing() { + public void testLeftWing() { assertThat(FootballMatchReports.onField(9)).isEqualTo("left wing"); } @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of player with shirt number 10") - public void test_striker() { + public void testStriker() { assertThat(FootballMatchReports.onField(10)).isEqualTo("striker"); } @Test @Tag("task:1") @DisplayName("The onField method returns the correct description of player with shirt number 11") - public void test_right_wing() { + public void testRightWing() { assertThat(FootballMatchReports.onField(11)).isEqualTo("right wing"); } @Test @Tag("task:2") - @DisplayName("The onField method throws IllegalArgumentException for unknown shirt number") - public void test_exception() { - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> FootballMatchReports.onField(13)); + @DisplayName("The onField method returns 'invalid' for invalid shirt number") + public void testException() { + assertThat(FootballMatchReports.onField(13)).isEqualTo("invalid"); } @Test @Tag("task:2") - @DisplayName("The onField method throws IllegalArgumentException for negative shirt number") - public void test_exception_negative_number() { - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> FootballMatchReports.onField(-1)); + @DisplayName("The onField method returns 'invalid' for negative shirt number") + public void testExceptionNegativeNumber() { + assertThat(FootballMatchReports.onField(-1)).isEqualTo("invalid"); } } 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/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/gotta-snatch-em-all/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/gotta-snatch-em-all/gradlew b/exercises/concept/gotta-snatch-em-all/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/gotta-snatch-em-all/gradlew +++ b/exercises/concept/gotta-snatch-em-all/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/gotta-snatch-em-all/gradlew.bat b/exercises/concept/gotta-snatch-em-all/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/gotta-snatch-em-all/gradlew.bat +++ b/exercises/concept/gotta-snatch-em-all/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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..8bedc370b --- /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-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 + +- 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-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) 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..e8f728f0f --- /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 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(44, "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..c407eea39 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.docs/introduction.md @@ -0,0 +1,74 @@ +# 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 [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.keySet(); // 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() +[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.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..e65e319f0 --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/.meta/config.json @@ -0,0 +1,20 @@ +{ + "authors": [ + "kahgoh" + ], + "files": { + "solution": [ + "src/main/java/DialingCodes.java" + ], + "test": [ + "src/test/java/DialingCodesTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/DialingCodes.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.properties similarity index 93% rename from exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.properties rename to exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/elons-toy-car/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/international-calling-connoisseur/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/elons-toy-car/gradlew b/exercises/concept/international-calling-connoisseur/gradlew similarity index 93% rename from exercises/concept/elons-toy-car/gradlew rename to exercises/concept/international-calling-connoisseur/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/elons-toy-car/gradlew +++ b/exercises/concept/international-calling-connoisseur/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/elons-toy-car/gradlew.bat b/exercises/concept/international-calling-connoisseur/gradlew.bat similarity index 94% rename from exercises/concept/elons-toy-car/gradlew.bat rename to exercises/concept/international-calling-connoisseur/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/elons-toy-car/gradlew.bat +++ b/exercises/concept/international-calling-connoisseur/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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..e88e7379f --- /dev/null +++ b/exercises/concept/international-calling-connoisseur/src/test/java/DialingCodesTest.java @@ -0,0 +1,141 @@ +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(44, "UK"); + + assertThat(dialingCodes.findDialingCode("UK")).isEqualTo(44); + } + + @Test + @Tag("task:5") + @DisplayName("findDialingCode returns null for country not yet added") + public void testFindDialingCodeWithUnlistedCountry() { + DialingCodes dialingCodes = new DialingCodes(); + dialingCodes.addNewDialingCode(44, "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"); + + assertThat(dialingCodes.getCodes()).containsOnly(entry(81, "Japan")); + } +} diff --git a/exercises/concept/elons-toy-car/.docs/hints.md b/exercises/concept/jedliks-toy-car/.docs/hints.md similarity index 100% rename from exercises/concept/elons-toy-car/.docs/hints.md rename to exercises/concept/jedliks-toy-car/.docs/hints.md diff --git a/exercises/concept/elons-toy-car/.docs/instructions.md b/exercises/concept/jedliks-toy-car/.docs/instructions.md similarity index 61% rename from exercises/concept/elons-toy-car/.docs/instructions.md rename to exercises/concept/jedliks-toy-car/.docs/instructions.md index a24720618..cb3f66561 100644 --- a/exercises/concept/elons-toy-car/.docs/instructions.md +++ b/exercises/concept/jedliks-toy-car/.docs/instructions.md @@ -15,38 +15,38 @@ You have six tasks, each of which will work with remote controlled car instances ## 1. Buy a brand-new remote controlled car -Implement the (_static_) `ElonsToyCar.buy()` method to return a brand-new remote controlled car instance: +Implement the (_static_) `JedliksToyCar.buy()` method to return a brand-new remote controlled car instance: ```java -ElonsToyCar car = ElonsToyCar.buy(); +JedliksToyCar car = JedliksToyCar.buy(); ``` ## 2. Display the distance driven -Implement the `ElonsToyCar.distanceDisplay()` method to return the distance as displayed on the LED display: +Implement the `JedliksToyCar.distanceDisplay()` method to return the distance as displayed on the LED display: ```java -ElonsToyCar car = ElonsToyCar.buy(); +JedliksToyCar car = JedliksToyCar.buy(); car.distanceDisplay(); // => "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 55% rename from exercises/concept/elons-toy-car/.meta/design.md rename to exercises/concept/jedliks-toy-car/.meta/design.md index f99cef1f7..2a1b40eb1 100644 --- a/exercises/concept/elons-toy-car/.meta/design.md +++ b/exercises/concept/jedliks-toy-car/.meta/design.md @@ -28,3 +28,15 @@ - `strings`: know how to do basic string interpolation. - `numbers`: know how to compare numbers. - `conditionals`: know how to do conditional logic. + +## Analyzer + +This exercise could benefit from the following rules in the [analyzer]: + +- `essential`: Verify that the solution keeps the void type for the drive function. +- `essential`: Verify that the solution has fields in the class +- `actionable`: If the solution defines the fields as `public`, instruct the student to use `private` and explain the encapsulation principle. +- `informative`: If the solution does not use a primitive as a type for the fields, inform the student to use it. + Explain that the values cannot be null and it is less error-prone + +[analyzer]: https://github.com/exercism/java-analyzer 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 86% rename from exercises/concept/elons-toy-car/build.gradle rename to exercises/concept/jedliks-toy-car/build.gradle index 1344305f7..dd3862eb9 100644 --- a/exercises/concept/elons-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/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..f8e1ee312 Binary files /dev/null and b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/concept/jedliks-toy-car/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/concept/jedliks-toy-car/gradlew b/exercises/concept/jedliks-toy-car/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/concept/jedliks-toy-car/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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/jedliks-toy-car/gradlew.bat b/exercises/concept/jedliks-toy-car/gradlew.bat new file mode 100644 index 000000000..c4bdd3ab8 --- /dev/null +++ b/exercises/concept/jedliks-toy-car/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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/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 71% 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..279cf07a0 100644 --- a/exercises/concept/elons-toy-car/src/test/java/ElonsToyCarTest.java +++ b/exercises/concept/jedliks-toy-car/src/test/java/JedliksToyCarTest.java @@ -4,45 +4,45 @@ 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(); + public void buyNewCarReturnsInstance() { + JedliksToyCar car = JedliksToyCar.buy(); assertThat(car).isNotNull(); } @Test @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(); + public void buyNewCarReturnsNewCarEachTime() { + JedliksToyCar car1 = JedliksToyCar.buy(); + JedliksToyCar car2 = JedliksToyCar.buy(); assertThat(car1).isNotEqualTo(car2); } @Test @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(); + public void newCarDistanceDisplay() { + JedliksToyCar car = new JedliksToyCar(); assertThat(car.distanceDisplay()).isEqualTo("Driven 0 meters"); } @Test @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(); + public void newCarBatteryDisplay() { + JedliksToyCar car = new JedliksToyCar(); assertThat(car.batteryDisplay()).isEqualTo("Battery at 100%"); } @Test @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(); + public void distanceDisplayAfterDrivingOnce() { + JedliksToyCar car = new JedliksToyCar(); car.drive(); assertThat(car.distanceDisplay()).isEqualTo("Driven 20 meters"); } @@ -50,8 +50,8 @@ public void distance_display_after_driving_once() { @Test @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(); + public void distanceDisplayAfterDrivingMultipleTimes() { + JedliksToyCar car = new JedliksToyCar(); for (int i = 0; i < 17; i++) { car.drive(); @@ -63,8 +63,8 @@ public void distance_display_after_driving_multiple_times() { @Test @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(); + public void batteryDisplayAfterDrivingOnce() { + JedliksToyCar car = new JedliksToyCar(); car.drive(); assertThat(car.batteryDisplay()).isEqualTo("Battery at 99%"); @@ -73,8 +73,8 @@ public void battery_display_after_driving_once() { @Test @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(); + public void batteryDisplayAfterDrivingMultipleTimes() { + JedliksToyCar car = new JedliksToyCar(); for (int i = 0; i < 23; i++) { car.drive(); @@ -86,8 +86,8 @@ public void battery_display_after_driving_multiple_times() { @Test @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(); + public void batteryDisplayWhenBatteryEmpty() { + JedliksToyCar car = new JedliksToyCar(); // Drain the battery for (int i = 0; i < 100; i++) { @@ -103,8 +103,8 @@ public void battery_display_when_battery_empty() { @Test @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(); + public void distanceDisplayWhenBatteryEmpty() { + JedliksToyCar car = new JedliksToyCar(); // Drain the battery for (int i = 0; i < 100; i++) { 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/karls-languages/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/karls-languages/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/karls-languages/gradlew b/exercises/concept/karls-languages/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/karls-languages/gradlew +++ b/exercises/concept/karls-languages/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/karls-languages/gradlew.bat b/exercises/concept/karls-languages/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/karls-languages/gradlew.bat +++ b/exercises/concept/karls-languages/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/lasagna/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/lasagna/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/lasagna/gradlew b/exercises/concept/lasagna/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/lasagna/gradlew +++ b/exercises/concept/lasagna/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/lasagna/gradlew.bat b/exercises/concept/lasagna/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/lasagna/gradlew.bat +++ b/exercises/concept/lasagna/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/lasagna/src/test/java/LasagnaTest.java b/exercises/concept/lasagna/src/test/java/LasagnaTest.java index 7b1676129..6200d47c5 100644 --- a/exercises/concept/lasagna/src/test/java/LasagnaTest.java +++ b/exercises/concept/lasagna/src/test/java/LasagnaTest.java @@ -11,7 +11,7 @@ public class LasagnaTest { @Test @Tag("task:1") @DisplayName("Implemented the expectedMinutesInOven method") - public void implemented_expected_minutes_in_oven() { + public void implementedExpectedMinutesInOven() { assertThat(new Lasagna().hasMethod("expectedMinutesInOven")) .withFailMessage("Method expectedMinutesInOven must be created") .isTrue(); @@ -26,14 +26,14 @@ public void implemented_expected_minutes_in_oven() { @Test @Tag("task:1") @DisplayName("The expectedMinutesInOven method returns the correct value") - public void expected_minutes_in_oven() { + public void expectedMinutesInOven() { assertThat(new Lasagna().expectedMinutesInOven()).isEqualTo(40); } @Test @Tag("task:2") @DisplayName("Implemented the remainingMinutesInOven method") - public void implemented_remaining_minutes_in_oven() { + public void implementedRemainingMinutesInOven() { assertThat(new Lasagna().hasMethod("remainingMinutesInOven", int.class)) .withFailMessage("Method remainingMinutesInOven must be created") .isTrue(); @@ -48,14 +48,14 @@ public void implemented_remaining_minutes_in_oven() { @Test @Tag("task:2") @DisplayName("The remainingMinutesInOven method calculates and returns the correct value") - public void remaining_minutes_in_oven() { + public void remainingMinutesInOven() { assertThat(new Lasagna().remainingMinutesInOven(25)).isEqualTo(15); } @Test @Tag("task:3") @DisplayName("Implemented the preparationTimeInMinutes method") - public void implemented_preparation_time_in_minutes() { + public void implementedPreparationTimeInMinutes() { assertThat(new Lasagna().hasMethod("preparationTimeInMinutes", int.class)) .withFailMessage("Method preparationTimeInMinutes must be created") .isTrue(); @@ -70,21 +70,21 @@ public void implemented_preparation_time_in_minutes() { @Test @Tag("task:3") @DisplayName("The preparationTimeInMinutes method calculates the correct value for single layer") - public void preparation_time_in_minutes_for_one_layer() { + public void preparationTimeInMinutesForOneLayer() { assertThat(new Lasagna().preparationTimeInMinutes(1)).isEqualTo(2); } @Test @Tag("task:3") @DisplayName("The preparationTimeInMinutes method calculates the correct value for multiple layers") - public void preparation_time_in_minutes_for_multiple_layers() { + public void preparationTimeInMinutesForMultipleLayers() { assertThat(new Lasagna().preparationTimeInMinutes(4)).isEqualTo(8); } @Test @Tag("task:4") @DisplayName("Implemented the totalTimeInMinutes method") - public void implemented_total_time_in_minutes() { + public void implementedTotalTimeInMinutes() { assertThat(new Lasagna().hasMethod("totalTimeInMinutes", int.class, int.class)) .withFailMessage("Method totalTimeInMinutes must be created") .isTrue(); @@ -99,14 +99,14 @@ public void implemented_total_time_in_minutes() { @Test @Tag("task:4") @DisplayName("The totalTimeInMinutes method calculates the correct value for single layer") - public void total_time_in_minutes_for_one_layer() { + public void totalTimeInMinutesForOneLayer() { assertThat(new Lasagna().totalTimeInMinutes(1, 30)).isEqualTo(32); } @Test @Tag("task:4") @DisplayName("The totalTimeInMinutes method calculates the correct value for multiple layers") - public void total_time_in_minutes_for_multiple_layers() { + public void totalTimeInMinutesForMultipleLayers() { assertThat(new Lasagna().totalTimeInMinutes(4, 8)).isEqualTo(16); } } 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/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/log-levels/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/log-levels/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/log-levels/gradlew b/exercises/concept/log-levels/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/log-levels/gradlew +++ b/exercises/concept/log-levels/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/log-levels/gradlew.bat b/exercises/concept/log-levels/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/log-levels/gradlew.bat +++ b/exercises/concept/log-levels/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/log-levels/src/test/java/LogLevelsTest.java b/exercises/concept/log-levels/src/test/java/LogLevelsTest.java index 2fa83e375..65dd46629 100644 --- a/exercises/concept/log-levels/src/test/java/LogLevelsTest.java +++ b/exercises/concept/log-levels/src/test/java/LogLevelsTest.java @@ -8,56 +8,56 @@ public class LogLevelsTest { @Test @Tag("task:1") @DisplayName("The message method returns the log line's message of an error log") - public void error_message() { + public void errorMessage() { assertThat(LogLevels.message("[ERROR]: Stack overflow")).isEqualTo("Stack overflow"); } @Test @Tag("task:1") @DisplayName("The message method returns the log line's message of a warning log") - public void warning_message() { + public void warningMessage() { assertThat(LogLevels.message("[WARNING]: Disk almost full")).isEqualTo("Disk almost full"); } @Test @Tag("task:1") @DisplayName("The message method returns the log line's message of an info log") - public void info_message() { + public void infoMessage() { assertThat(LogLevels.message("[INFO]: File moved")).isEqualTo("File moved"); } @Test @Tag("task:1") @DisplayName("The message method returns the log line's message after removing leading and trailing spaces") - public void message_with_leading_and_trailing_white_space() { + public void messageWithLeadingAndTrailingWhiteSpace() { assertThat(LogLevels.message("[WARNING]: \tTimezone not set \r\n")).isEqualTo("Timezone not set"); } @Test @Tag("task:2") @DisplayName("The logLevel method returns the log level of an error log line") - public void error_log_level() { + public void errorLogLevel() { assertThat(LogLevels.logLevel("[ERROR]: Disk full")).isEqualTo("error"); } @Test @Tag("task:2") @DisplayName("The logLevel method returns the log level of a warning log line") - public void warning_log_level() { + public void warningLogLevel() { assertThat(LogLevels.logLevel("[WARNING]: Unsafe password")).isEqualTo("warning"); } @Test @Tag("task:2") @DisplayName("The logLevel method returns the log level of an info log line") - public void info_log_level() { + public void infoLogLevel() { assertThat(LogLevels.logLevel("[INFO]: Timezone changed")).isEqualTo("info"); } @Test @Tag("task:3") @DisplayName("The reformat method correctly reformats an error log line") - public void error_reformat() { + public void errorReformat() { assertThat(LogLevels.reformat("[ERROR]: Segmentation fault")) .isEqualTo("Segmentation fault (error)"); } @@ -65,7 +65,7 @@ public void error_reformat() { @Test @Tag("task:3") @DisplayName("The reformat method correctly reformats a warning log line") - public void warning_reformat() { + public void warningReformat() { assertThat(LogLevels.reformat("[WARNING]: Decreased performance")) .isEqualTo("Decreased performance (warning)"); } @@ -73,7 +73,7 @@ public void warning_reformat() { @Test @Tag("task:3") @DisplayName("The reformat method correctly reformats an info log line") - public void info_reformat() { + public void infoReformat() { assertThat(LogLevels.reformat("[INFO]: Disk defragmented")) .isEqualTo("Disk defragmented (info)"); } @@ -81,7 +81,7 @@ public void info_reformat() { @Test @Tag("task:3") @DisplayName("The reformat method correctly reformats an error log line removing spaces") - public void reformat_with_leading_and_trailing_white_space() { + public void reformatWithLeadingAndTrailingWhiteSpace() { assertThat(LogLevels.reformat("[ERROR]: \t Corrupt disk\t \t \r\n")) .isEqualTo("Corrupt disk (error)"); } 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/logs-logs-logs/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/logs-logs-logs/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/logs-logs-logs/gradlew b/exercises/concept/logs-logs-logs/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/logs-logs-logs/gradlew +++ b/exercises/concept/logs-logs-logs/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/logs-logs-logs/gradlew.bat b/exercises/concept/logs-logs-logs/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/logs-logs-logs/gradlew.bat +++ b/exercises/concept/logs-logs-logs/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/need-for-speed/.docs/instructions.md b/exercises/concept/need-for-speed/.docs/instructions.md index b08fae4c3..e5c478efa 100644 --- a/exercises/concept/need-for-speed/.docs/instructions.md +++ b/exercises/concept/need-for-speed/.docs/instructions.md @@ -77,15 +77,15 @@ int speed = 5; int batteryDrain = 2; var car = new NeedForSpeed(speed, batteryDrain); -int distance = 100; -var race = new RaceTrack(distance); +int distance1 = 100; +var race1 = new RaceTrack(distance1); -car.distanceDriven() -// => 0 +int distance2 = 300; +var race2 = new RaceTrack(distance2); -race.canFinishRace(car); +race1.canFinishRace(car); // => true -car.distanceDriven() -// => 100 +race2.canFinishRace(car); +// => false ``` 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/need-for-speed/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/need-for-speed/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/need-for-speed/gradlew b/exercises/concept/need-for-speed/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/need-for-speed/gradlew +++ b/exercises/concept/need-for-speed/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/need-for-speed/gradlew.bat b/exercises/concept/need-for-speed/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/need-for-speed/gradlew.bat +++ b/exercises/concept/need-for-speed/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/need-for-speed/src/test/java/NeedForSpeedTest.java b/exercises/concept/need-for-speed/src/test/java/NeedForSpeedTest.java index a5f130e3f..2848f34fb 100644 --- a/exercises/concept/need-for-speed/src/test/java/NeedForSpeedTest.java +++ b/exercises/concept/need-for-speed/src/test/java/NeedForSpeedTest.java @@ -10,7 +10,7 @@ public class NeedForSpeedTest { @Test @Tag("task:3") @DisplayName("The distanceDriven method returns 0 on a new car") - public void new_remote_control_car_has_not_driven_any_distance() { + public void newRemoteControlCarHasNotDrivenAnyDistance() { int speed = 10; int batteryDrain = 2; var car = new NeedForSpeed(speed, batteryDrain); @@ -21,7 +21,7 @@ public void new_remote_control_car_has_not_driven_any_distance() { @Test @Tag("task:3") @DisplayName("The distanceDriven method returns 5 after driving once") - public void drive_increases_distance_driven_with_speed() { + public void driveIncreasesDistanceDrivenWithSpeed() { int speed = 5; int batteryDrain = 1; var car = new NeedForSpeed(speed, batteryDrain); @@ -34,7 +34,7 @@ public void drive_increases_distance_driven_with_speed() { @Test @Tag("task:3") @DisplayName("The distanceDriven method returns the correct distance after driving multiple times") - public void drive_does_not_increase_distance_driven_when_battery_drained() { + public void driveDoesNotIncreaseDistanceDrivenWhenBatteryDrained() { int speed = 9; int batteryDrain = 50; var car = new NeedForSpeed(speed, batteryDrain); @@ -52,7 +52,7 @@ public void drive_does_not_increase_distance_driven_when_battery_drained() { @Test @Tag("task:4") @DisplayName("The batteryDrained method returns false when car never drove") - public void new_remote_control_car_battery_is_not_drained() { + public void newRemoteControlCarBatteryIsNotDrained() { int speed = 15; int batteryDrain = 3; var car = new NeedForSpeed(speed, batteryDrain); @@ -63,7 +63,7 @@ public void new_remote_control_car_battery_is_not_drained() { @Test @Tag("task:4") @DisplayName("The batteryDrained method returns false when there's not enough battery") - public void new_remote_control_car_that_can_only_drive_once() { + public void newRemoteControlCarThatCanOnlyDriveOnce() { var car = new NeedForSpeed(1, 99); car.drive(); assertThat(car.batteryDrained()).isTrue(); @@ -74,7 +74,7 @@ public void new_remote_control_car_that_can_only_drive_once() { @Test @Tag("task:4") @DisplayName("The batteryDrained method returns false when car battery did not completely drain after driving") - public void drive_to_almost_drain_battery() { + public void driveToAlmostDrainBattery() { int speed = 2; int batteryDrain = 1; var car = new NeedForSpeed(speed, batteryDrain); @@ -90,7 +90,7 @@ public void drive_to_almost_drain_battery() { @Test @Tag("task:4") @DisplayName("The batteryDrained method returns true when battery completely drained after driving") - public void drive_until_battery_is_drained() { + public void driveUntilBatteryIsDrained() { int speed = 2; int batteryDrain = 1; var car = new NeedForSpeed(speed, batteryDrain); @@ -106,7 +106,7 @@ public void drive_until_battery_is_drained() { @Test @Tag("task:5") @DisplayName("The distanceDriven method returns 0 on a new nitro car") - public void nitro_car_has_not_driven_any_distance() { + public void nitroCarHasNotDrivenAnyDistance() { var car = NeedForSpeed.nitro(); assertThat(car.distanceDriven()).isEqualTo(0); } @@ -114,7 +114,7 @@ public void nitro_car_has_not_driven_any_distance() { @Test @Tag("task:5") @DisplayName("The batteryDrained method returns false when nitro car never drove") - public void nitro_car_has_battery_not_drained() { + public void nitroCarHasBatteryNotDrained() { var car = NeedForSpeed.nitro(); assertThat(car.batteryDrained()).isFalse(); } @@ -122,7 +122,7 @@ public void nitro_car_has_battery_not_drained() { @Test @Tag("task:5") @DisplayName("The distanceDriven method returns the correct distance after driving a nitro car") - public void nitro_car_has_correct_speed() { + public void nitroCarHasCorrectSpeed() { var car = NeedForSpeed.nitro(); car.drive(); assertThat(car.distanceDriven()).isEqualTo(50); @@ -131,7 +131,7 @@ public void nitro_car_has_correct_speed() { @Test @Tag("task:5") @DisplayName("The batteryDrained method returns false when nitro battery did not completely drain after driving") - public void nitro_has_correct_battery_drain() { + public void nitroHasCorrectBatteryDrain() { var car = NeedForSpeed.nitro(); // The battery is almost drained @@ -145,7 +145,7 @@ public void nitro_has_correct_battery_drain() { @Test @Tag("task:5") @DisplayName("The batteryDrained method returns true when nitro battery completely drained after driving") - public void nitro_battery_completely_drains() { + public void nitroBatteryCompletelyDrains() { var car = NeedForSpeed.nitro(); // The battery is drained @@ -159,7 +159,7 @@ public void nitro_battery_completely_drains() { @Test @Tag("task:6") @DisplayName("The canFinishRace method returns true when car can finish a race") - public void car_can_finish_with_car_that_can_easily_finish() { + public void carCanFinishWithCarThatCanEasilyFinish() { int speed = 10; int batteryDrain = 2; var car = new NeedForSpeed(speed, batteryDrain); @@ -173,7 +173,7 @@ public void car_can_finish_with_car_that_can_easily_finish() { @Test @Tag("task:6") @DisplayName("The canFinishRace method returns true when car can just finish a race") - public void car_can_finish_with_car_that_can_just_finish() { + public void carCanFinishWithCarThatCanJustFinish() { int speed = 2; int batteryDrain = 10; var car = new NeedForSpeed(speed, batteryDrain); @@ -187,7 +187,7 @@ public void car_can_finish_with_car_that_can_just_finish() { @Test @Tag("task:6") @DisplayName("The canFinishRace method returns false when car just cannot finish a race") - public void car_can_finish_with_car_that_just_cannot_finish() { + public void carCanFinishWithCarThatJustCannotFinish() { int speed = 3; int batteryDrain = 20; var car = new NeedForSpeed(speed, batteryDrain); @@ -201,7 +201,7 @@ public void car_can_finish_with_car_that_just_cannot_finish() { @Test @Tag("task:6") @DisplayName("The canFinishRace method returns false when car cannot finish a race") - public void car_can_finish_with_car_that_cannot_finish() { + public void carCanFinishWithCarThatCannotFinish() { int speed = 1; int batteryDrain = 20; var car = new NeedForSpeed(speed, batteryDrain); 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/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/remote-control-competition/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/remote-control-competition/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/remote-control-competition/gradlew b/exercises/concept/remote-control-competition/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/remote-control-competition/gradlew +++ b/exercises/concept/remote-control-competition/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/remote-control-competition/gradlew.bat b/exercises/concept/remote-control-competition/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/remote-control-competition/gradlew.bat +++ b/exercises/concept/remote-control-competition/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/salary-calculator/.meta/src/reference/java/SalaryCalculator.java b/exercises/concept/salary-calculator/.meta/src/reference/java/SalaryCalculator.java index c6e6ea140..fac9e15c3 100644 --- a/exercises/concept/salary-calculator/.meta/src/reference/java/SalaryCalculator.java +++ b/exercises/concept/salary-calculator/.meta/src/reference/java/SalaryCalculator.java @@ -8,11 +8,11 @@ public int bonusMultiplier(int productsSold) { return productsSold < 20 ? 10 : 13; } - public double bonusForProductsSold (int productsSold) { + public double bonusForProductsSold(int productsSold) { return productsSold * bonusMultiplier(productsSold); } - public double finalSalary (int daysSkipped, int productsSold) { + public double finalSalary(int daysSkipped, int productsSold) { double finalSalary = 1000.0 * salaryMultiplier(daysSkipped) + bonusForProductsSold(productsSold); return finalSalary > 2000.0 ? 2000.0 : finalSalary; } 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/salary-calculator/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/salary-calculator/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/salary-calculator/gradlew b/exercises/concept/salary-calculator/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/salary-calculator/gradlew +++ b/exercises/concept/salary-calculator/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/salary-calculator/gradlew.bat b/exercises/concept/salary-calculator/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/salary-calculator/gradlew.bat +++ b/exercises/concept/salary-calculator/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/secrets/.docs/introduction.md b/exercises/concept/secrets/.docs/introduction.md index a281ec084..132d725f0 100644 --- a/exercises/concept/secrets/.docs/introduction.md +++ b/exercises/concept/secrets/.docs/introduction.md @@ -57,7 +57,7 @@ Otherwise, the result's bit is 0. #### Bitwise OR The bitwise OR (`|`) operator takes two values and performs an OR on each bit. -It compares each bit from the first value with the bit in thes same position from the second value. +It compares each bit from the first value with the bit in the same position from the second value. If either bit is 1, the result's bit is 1. Otherwise, it is 0. 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/secrets/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/secrets/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/secrets/gradlew b/exercises/concept/secrets/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/secrets/gradlew +++ b/exercises/concept/secrets/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/secrets/gradlew.bat b/exercises/concept/secrets/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/secrets/gradlew.bat +++ b/exercises/concept/secrets/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/squeaky-clean/.docs/instructions.md b/exercises/concept/squeaky-clean/.docs/instructions.md index 5b092e56a..6b0419d2f 100644 --- a/exercises/concept/squeaky-clean/.docs/instructions.md +++ b/exercises/concept/squeaky-clean/.docs/instructions.md @@ -29,11 +29,14 @@ SqueakyClean.clean("a-bc"); ## 3. Convert leetspeak to normal text Modify the (_static_) `SqueakyClean.clean()` method to convert [leetspeak][leet-speak] to normal text. -For simplicity we will only be using `4`, `3`, `0`, `1` and `7` from the table. + +For simplicity we will only be replacing `4`, `3`, `0`, `1` and `7` with `a`, `e`, `o`, `l`, and `t`, respectively. ```java SqueakyClean.clean("H3ll0 W0rld"); // => "Hello_World" +SqueakyClean.clean("4 73s7"); +// => "a_test" ``` ## 4. Omit characters that are not letters 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 0750fd7af..70df5ac7d 100644 --- a/exercises/concept/squeaky-clean/.meta/config.json +++ b/exercises/concept/squeaky-clean/.meta/config.json @@ -3,8 +3,11 @@ "ystromm" ], "contributors": [ - "sanderploegsma", - "manumafe98" + "jagdish-15", + "kahgoh", + "manumafe98", + "mrDonoghue", + "sanderploegsma" ], "files": { "solution": [ 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/squeaky-clean/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/squeaky-clean/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/squeaky-clean/gradlew b/exercises/concept/squeaky-clean/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/squeaky-clean/gradlew +++ b/exercises/concept/squeaky-clean/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/squeaky-clean/gradlew.bat b/exercises/concept/squeaky-clean/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/squeaky-clean/gradlew.bat +++ b/exercises/concept/squeaky-clean/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/squeaky-clean/src/test/java/SqueakyCleanTest.java b/exercises/concept/squeaky-clean/src/test/java/SqueakyCleanTest.java index 19688de55..e85a60495 100644 --- a/exercises/concept/squeaky-clean/src/test/java/SqueakyCleanTest.java +++ b/exercises/concept/squeaky-clean/src/test/java/SqueakyCleanTest.java @@ -16,7 +16,7 @@ public void empty() { @Test @Tag("task:1") @DisplayName("The clean method returns the same string when invoked on a single letter string") - public void single_letter() { + public void singleLetter() { assertThat(SqueakyClean.clean("A")).isEqualTo("A"); } @@ -37,56 +37,56 @@ public void spaces() { @Test @Tag("task:1") @DisplayName("The clean method replaces leading and trailing whitespaces with underscores") - public void leading_and_trailing_spaces() { + public void leadingAndTrailingSpaces() { assertThat(SqueakyClean.clean(" myId ")).isEqualTo("_myId_"); } @Test @Tag("task:2") @DisplayName("The clean method converts kebab to camel case after removing a dash") - public void kebab_to_camel_case() { + public void kebabToCamelCase() { assertThat(SqueakyClean.clean("a-bc")).isEqualTo("aBc"); } @Test @Tag("task:2") @DisplayName("The clean method returns a string in camel case after removing a dash and replaces a whitespace") - public void kebab_to_camel_case_and_number() { + public void kebabToCamelCaseAndNumber() { assertThat(SqueakyClean.clean("a-C ")).isEqualTo("aC_"); } @Test @Tag("task:2") @DisplayName("The clean method returns a string in camel case and replaces leading and trailing whitespaces") - public void kebab_to_camel_case_and_spaces() { + public void kebabToCamelCaseAndSpaces() { assertThat(SqueakyClean.clean(" hello-world ")).isEqualTo("_helloWorld_"); } @Test @Tag("task:3") @DisplayName("The clean method converts leetspeak to normal text after replacing numbers with chars") - public void leetspeak_to_normal_text() { + public void leetspeakToNormalText() { assertThat(SqueakyClean.clean("H3ll0 W0rld")).isEqualTo("Hello_World"); } @Test @Tag("task:3") @DisplayName("The clean method converts leetspeak to normal text with spaces and special characters") - public void leetspeak_to_normal_text_with_spaces_and_special_characters() { + public void leetspeakToNormalTextWithSpacesAndSpecialCharacters() { assertThat(SqueakyClean.clean("¡1337sp34k is fun!")).isEqualTo("leetspeak_is_fun"); } @Test @Tag("task:4") @DisplayName("The clean method removes all characters that are not letters") - public void special_characters() { + public void specialCharacters() { assertThat(SqueakyClean.clean("a$#.b")).isEqualTo("ab"); } @Test @Tag("task:4") @DisplayName("The clean method removes all characters that are not letters and replaces spaces") - public void special_characters_and_spaces() { + public void specialCharactersAndSpaces() { assertThat(SqueakyClean.clean("¡hello world!. ")).isEqualTo("hello_world_"); } } diff --git a/exercises/concept/tim-from-marketing/.docs/introduction.md b/exercises/concept/tim-from-marketing/.docs/introduction.md index 8d7497357..51ae4574a 100644 --- a/exercises/concept/tim-from-marketing/.docs/introduction.md +++ b/exercises/concept/tim-from-marketing/.docs/introduction.md @@ -4,11 +4,11 @@ In Java, the `null` literal is used to denote the absence of a value. -Primitive data types in java all have a default value and therefore can never be `null`. +Primitive data types in Java all have a default value and therefore can never be `null`. By convention, they start with a lowercase letter e.g `int`. -Reference types contain the memory address of an object can have a value of null. -These variables usually start with an uppercase e.g `String`. +Reference types contain the memory address of an object and can have a value of `null`. +They generally start with an uppercase letter, e.g. `String`. Attempting to assign a primitive variable a value of `null` will result in a compile time error as the variable always holds a primitive value of the type assigned. 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/tim-from-marketing/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/tim-from-marketing/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/tim-from-marketing/gradlew b/exercises/concept/tim-from-marketing/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/tim-from-marketing/gradlew +++ b/exercises/concept/tim-from-marketing/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/tim-from-marketing/gradlew.bat b/exercises/concept/tim-from-marketing/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/tim-from-marketing/gradlew.bat +++ b/exercises/concept/tim-from-marketing/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/concept/wizards-and-warriors-2/.meta/config.json b/exercises/concept/wizards-and-warriors-2/.meta/config.json index ea8457857..ec5eca2b8 100644 --- a/exercises/concept/wizards-and-warriors-2/.meta/config.json +++ b/exercises/concept/wizards-and-warriors-2/.meta/config.json @@ -3,6 +3,7 @@ "sougat818" ], "contributors": [ + "jagdish-15", "sanderploegsma" ], "files": { diff --git a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Character.java b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Character.java deleted file mode 120000 index 680015fad..000000000 --- a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Character.java +++ /dev/null @@ -1 +0,0 @@ -../../../../src/main/java/Character.java \ No newline at end of file diff --git a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Character.java b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Character.java new file mode 100644 index 000000000..d7a4a4add --- /dev/null +++ b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Character.java @@ -0,0 +1,29 @@ +public class Character { + private String characterClass; + private int level; + private int hitPoints; + + public String getCharacterClass() { + return characterClass; + } + + public void setCharacterClass(String characterClass) { + this.characterClass = characterClass; + } + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } + + public int getHitPoints() { + return hitPoints; + } + + public void setHitPoints(int hitPoints) { + this.hitPoints = hitPoints; + } +} diff --git a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Destination.java b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Destination.java deleted file mode 120000 index ea889c0eb..000000000 --- a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Destination.java +++ /dev/null @@ -1 +0,0 @@ -../../../../src/main/java/Destination.java \ No newline at end of file diff --git a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Destination.java b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Destination.java new file mode 100644 index 000000000..09f2ce4e3 --- /dev/null +++ b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/Destination.java @@ -0,0 +1,20 @@ +public class Destination { + private String name; + private int inhabitants; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getInhabitants() { + return inhabitants; + } + + public void setInhabitants(int inhabitants) { + this.inhabitants = inhabitants; + } +} diff --git a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/TravelMethod.java b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/TravelMethod.java deleted file mode 120000 index 224dbadad..000000000 --- a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/TravelMethod.java +++ /dev/null @@ -1 +0,0 @@ -../../../../src/main/java/TravelMethod.java \ No newline at end of file diff --git a/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/TravelMethod.java b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/TravelMethod.java new file mode 100644 index 000000000..59d258ca8 --- /dev/null +++ b/exercises/concept/wizards-and-warriors-2/.meta/src/reference/java/TravelMethod.java @@ -0,0 +1,4 @@ +public enum TravelMethod { + WALKING, + HORSEBACK +} 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-2/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/wizards-and-warriors-2/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/wizards-and-warriors-2/gradlew b/exercises/concept/wizards-and-warriors-2/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/wizards-and-warriors-2/gradlew +++ b/exercises/concept/wizards-and-warriors-2/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/wizards-and-warriors-2/gradlew.bat b/exercises/concept/wizards-and-warriors-2/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/wizards-and-warriors-2/gradlew.bat +++ b/exercises/concept/wizards-and-warriors-2/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.jar b/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.jar and b/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.properties b/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/concept/wizards-and-warriors/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/concept/wizards-and-warriors/gradlew b/exercises/concept/wizards-and-warriors/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/concept/wizards-and-warriors/gradlew +++ b/exercises/concept/wizards-and-warriors/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/concept/wizards-and-warriors/gradlew.bat b/exercises/concept/wizards-and-warriors/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/concept/wizards-and-warriors/gradlew.bat +++ b/exercises/concept/wizards-and-warriors/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/gradle/wrapper/gradle-wrapper.jar b/exercises/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/gradle/wrapper/gradle-wrapper.jar and b/exercises/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/gradle/wrapper/gradle-wrapper.properties b/exercises/gradle/wrapper/gradle-wrapper.properties index b82aa23a4..23449a2b5 100644 --- a/exercises/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/exercises/gradlew b/exercises/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/gradlew +++ b/exercises/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/gradlew.bat b/exercises/gradlew.bat index 93e3f59f1..c4bdd3ab8 100644 --- a/exercises/gradlew.bat +++ b/exercises/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ 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. +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 @@ -57,22 +59,21 @@ 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. +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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/acronym/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/acronym/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/acronym/gradlew b/exercises/practice/acronym/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/acronym/gradlew +++ b/exercises/practice/acronym/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/acronym/gradlew.bat b/exercises/practice/acronym/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/acronym/gradlew.bat +++ b/exercises/practice/acronym/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/acronym/src/test/java/AcronymTest.java b/exercises/practice/acronym/src/test/java/AcronymTest.java index 4cc09f4cf..09b419c4a 100644 --- a/exercises/practice/acronym/src/test/java/AcronymTest.java +++ b/exercises/practice/acronym/src/test/java/AcronymTest.java @@ -1,11 +1,13 @@ 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 AcronymTest { - + @Test + @DisplayName("basic") public void basic() { assertThat(new Acronym("Portable Network Graphics").get()) .isEqualTo("PNG"); @@ -13,6 +15,7 @@ public void basic() { @Disabled("Remove to run test") @Test + @DisplayName("lowercase words") public void lowercaseWords() { assertThat(new Acronym("Ruby on Rails").get()) .isEqualTo("ROR"); @@ -20,6 +23,7 @@ public void lowercaseWords() { @Disabled("Remove to run test") @Test + @DisplayName("punctuation") public void punctuation() { assertThat(new Acronym("First In, First Out").get()) .isEqualTo("FIFO"); @@ -27,6 +31,7 @@ public void punctuation() { @Disabled("Remove to run test") @Test + @DisplayName("all caps word") public void nonAcronymAllCapsWord() { assertThat(new Acronym("GNU Image Manipulation Program").get()) .isEqualTo("GIMP"); @@ -34,6 +39,7 @@ public void nonAcronymAllCapsWord() { @Disabled("Remove to run test") @Test + @DisplayName("punctuation without whitespace") public void punctuationWithoutWhitespace() { assertThat(new Acronym("Complementary metal-oxide semiconductor").get()) .isEqualTo("CMOS"); @@ -41,6 +47,7 @@ public void punctuationWithoutWhitespace() { @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"); @@ -48,6 +55,7 @@ public void veryLongAbbreviation() { @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"); @@ -55,6 +63,7 @@ public void consecutiveDelimiters() { @Disabled("Remove to run test") @Test + @DisplayName("apostrophes") public void apostrophes() { assertThat(new Acronym("Halley's Comet").get()) .isEqualTo("HC"); @@ -62,6 +71,7 @@ public void apostrophes() { @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/.docs/instructions.md b/exercises/practice/affine-cipher/.docs/instructions.md index 4eff918de..1603dbbce 100644 --- a/exercises/practice/affine-cipher/.docs/instructions.md +++ b/exercises/practice/affine-cipher/.docs/instructions.md @@ -4,7 +4,7 @@ Create an implementation of the affine cipher, an ancient encryption system crea The affine cipher is a type of monoalphabetic substitution cipher. Each character is mapped to its numeric equivalent, encrypted with a mathematical function and then converted to the letter relating to its new numeric value. -Although all monoalphabetic ciphers are weak, the affine cipher is much stronger than the atbash cipher, because it has many more keys. +Although all monoalphabetic ciphers are weak, the affine cipher is much stronger than the Atbash cipher, because it has many more keys. [//]: # " monoalphabetic as spelled by Merriam-Webster, compare to polyalphabetic " @@ -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/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/affine-cipher/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/affine-cipher/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/affine-cipher/gradlew b/exercises/practice/affine-cipher/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/affine-cipher/gradlew +++ b/exercises/practice/affine-cipher/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/affine-cipher/gradlew.bat b/exercises/practice/affine-cipher/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/affine-cipher/gradlew.bat +++ b/exercises/practice/affine-cipher/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/affine-cipher/src/test/java/AffineCipherTest.java b/exercises/practice/affine-cipher/src/test/java/AffineCipherTest.java index d37c96ade..25bad1a0b 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 - public void testEncodeO_M_G() { + @DisplayName("encode O M G") + public void testEncodeOMGWithSpaces() { 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/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/all-your-base/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/all-your-base/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/all-your-base/gradlew b/exercises/practice/all-your-base/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/all-your-base/gradlew +++ b/exercises/practice/all-your-base/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/all-your-base/gradlew.bat b/exercises/practice/all-your-base/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/all-your-base/gradlew.bat +++ b/exercises/practice/all-your-base/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/allergies/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/allergies/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/allergies/gradlew b/exercises/practice/allergies/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/allergies/gradlew +++ b/exercises/practice/allergies/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/allergies/gradlew.bat b/exercises/practice/allergies/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/allergies/gradlew.bat +++ b/exercises/practice/allergies/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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); 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/alphametics/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/alphametics/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/alphametics/gradlew b/exercises/practice/alphametics/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/alphametics/gradlew +++ b/exercises/practice/alphametics/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/alphametics/gradlew.bat b/exercises/practice/alphametics/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/alphametics/gradlew.bat +++ b/exercises/practice/alphametics/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.docs/instructions.append.md b/exercises/practice/anagram/.docs/instructions.append.md new file mode 100644 index 000000000..8d71a920b --- /dev/null +++ b/exercises/practice/anagram/.docs/instructions.append.md @@ -0,0 +1,3 @@ +# Instructions Append + +The anagrams can be returned in any order. 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/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/anagram/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/anagram/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/anagram/gradlew b/exercises/practice/anagram/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/anagram/gradlew +++ b/exercises/practice/anagram/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/anagram/gradlew.bat b/exercises/practice/anagram/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/anagram/gradlew.bat +++ b/exercises/practice/anagram/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/armstrong-numbers/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/armstrong-numbers/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/armstrong-numbers/gradlew b/exercises/practice/armstrong-numbers/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/armstrong-numbers/gradlew +++ b/exercises/practice/armstrong-numbers/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/armstrong-numbers/gradlew.bat b/exercises/practice/armstrong-numbers/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/armstrong-numbers/gradlew.bat +++ b/exercises/practice/armstrong-numbers/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.docs/instructions.md b/exercises/practice/atbash-cipher/.docs/instructions.md index 21ca2ce0a..1e7627b1e 100644 --- a/exercises/practice/atbash-cipher/.docs/instructions.md +++ b/exercises/practice/atbash-cipher/.docs/instructions.md @@ -1,6 +1,6 @@ # Instructions -Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East. +Create an implementation of the Atbash cipher, an ancient encryption system created in the Middle East. The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards. The first letter is replaced with the last letter, the second with the second-last, and so on. diff --git a/exercises/practice/atbash-cipher/.meta/config.json b/exercises/practice/atbash-cipher/.meta/config.json index 8236a85bd..41761efcd 100644 --- a/exercises/practice/atbash-cipher/.meta/config.json +++ b/exercises/practice/atbash-cipher/.meta/config.json @@ -39,7 +39,7 @@ "build.gradle" ] }, - "blurb": "Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.", + "blurb": "Create an implementation of the Atbash cipher, an ancient encryption system created in the Middle East.", "source": "Wikipedia", "source_url": "https://en.wikipedia.org/wiki/Atbash" } 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/atbash-cipher/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/atbash-cipher/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/atbash-cipher/gradlew b/exercises/practice/atbash-cipher/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/atbash-cipher/gradlew +++ b/exercises/practice/atbash-cipher/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/atbash-cipher/gradlew.bat b/exercises/practice/atbash-cipher/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/atbash-cipher/gradlew.bat +++ b/exercises/practice/atbash-cipher/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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"); diff --git a/exercises/practice/bank-account/.approaches/config.json b/exercises/practice/bank-account/.approaches/config.json new file mode 100644 index 000000000..1739dcd4a --- /dev/null +++ b/exercises/practice/bank-account/.approaches/config.json @@ -0,0 +1,45 @@ +{ + "introduction": { + "authors": [ + "kahgoh" + ] + }, + "approaches": [ + { + "uuid": "78d753b0-aa58-43dc-83c0-9ea41496de84", + "slug": "synchronized-methods", + "title": "Synchronized methods", + "blurb": "Use synchronized methods to prevent methods from running simultaneously in different threads", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "5f3b0152-02eb-40d5-9104-5edc30b4447e", + "slug": "synchronized-statements", + "title": "Synchronized statements", + "blurb": "Use an object to prevent threads from running blocks of code at the same time", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "0acd6f2b-27d0-4ae6-9c22-22b0b2047039", + "slug": "reentrant-lock", + "title": "Reentrant lock", + "blurb": "Use an ReentrantLock to explicitly acquire and release locks", + "authors": [ + "kahgoh" + ] + }, + { + "uuid": "4ad42f88-f750-4af9-bbbd-d8b2dc5e8078", + "slug": "readwrite-lock", + "title": "Readwrite lock", + "blurb": "Use separate read and write locks to achieve greater concurrency", + "authors": [ + "kahgoh" + ] + } + ] +} diff --git a/exercises/practice/bank-account/.approaches/introduction.md b/exercises/practice/bank-account/.approaches/introduction.md new file mode 100644 index 000000000..ac8d3c506 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/introduction.md @@ -0,0 +1,351 @@ +# Introduction + +In Bank Account, you are tasked with implementing a number of operations that can be performed on a bank account. +However, these operations may be performed by multiple threads at the same time. + +## General guidance + +The key to solving Bank Account is to prevent an account from being updated from multiple threads at the same time. +For example, consider an account that begins with $0. +A $10 deposit is made twice. +Each transaction starts a thread. +If the threads happen to start simultaneously, they might both see the account starting $0. +They each add $10 to this amount and update the account accordingly. +In this case, each thread sets the amount to $10 even though the transaction was made twice. + +The problem here is that both threads saw that there was $0 in the account prior to adding the deposit. +Instead, each thread needs to take turns to process the deposit for the account so that they can process the transaction one at a time. +This way, the later thread can "see" the deposited amount from the first thread. + +## Approach: Synchronized methods + +```java +class BankAccount { + private int balance = 0; + private boolean isClosed = true; + + synchronized void open() throws BankAccountActionInvalidException { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + + synchronized void close() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + + synchronized int getBalance() throws BankAccountActionInvalidException { + checkIfClosed(); + return balance; + } + + synchronized void deposit(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + + synchronized void withdraw(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +For more information, check the [Synchronized methods approach][approach-synchronized-methods]. + +## Approach: Synchronized statements + +```java +class BankAccount { + private final Object lock = new Object(); + private int balance = 0; + private boolean isClosed = true; + + void open() throws BankAccountActionInvalidException { + synchronized(lock) { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + } + + void close() throws BankAccountActionInvalidException { + synchronized(lock) { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + } + + int getBalance() throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + return balance; + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +For more information, check the [Synchronized statements approach][approach-synchronized-statements]. + +## Approach: Reentrant lock + +```java +import java.util.concurrent.locks.ReentrantLock; + +class BankAccount { + + private final ReentrantLock lock = new ReentrantLock(); + + private boolean isOpen = false; + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.unlock(); + } + } + +} +``` + +For more information, check the [Reentrant lock approach][approach-reentrant-lock]. + +## Approach: Read write lock + +```java +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class BankAccount { + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private boolean isOpen = false; + + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.writeLock().unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.writeLock().unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.readLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.readLock().unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.writeLock().unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.writeLock().unlock(); + } + } +} +``` + +For more information, check the [Read write lock approach][approach-read-write-lock]. + +## Which approach to use? + +- The synchronized methods is the simplest, requiring no extra objects to be created. +- Synchronized statements provide greater control over which code statements are performed with a lock and which object is to be used as the lock. +- The read write lock allows greater concurrency by letting multiple read operations, such as `getBalance`, run in parallel. + However, it requires the lock to be explicitly released. + +[approach-read-write-lock]: https://exercism.org/tracks/java/exercises/bank-account/approaches/readwrite-lock +[approach-reentrant-lock]: https://exercism.org/tracks/java/exercises/bank-acconuunt/approaches/reentrant-lock +[approach-synchronized-methods]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronized-methods +[approach-synchronized-statements]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronzied-statements diff --git a/exercises/practice/bank-account/.approaches/readwrite-lock/content.md b/exercises/practice/bank-account/.approaches/readwrite-lock/content.md new file mode 100644 index 000000000..5db33cc0a --- /dev/null +++ b/exercises/practice/bank-account/.approaches/readwrite-lock/content.md @@ -0,0 +1,108 @@ +# Readwrite Lock + +```java +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class BankAccount { + + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private boolean isOpen = false; + + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.writeLock().unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.writeLock().unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.readLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.readLock().unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.writeLock().unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.writeLock().lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.writeLock().unlock(); + } + } +} +``` + +A [ReadWriteLock][docs-readwritelock] provides two types of locks - one for reading, the other for writing. +[ReentrantReadWriteLock][docs-reentrantreadwritelock] is an implementation of a [ReadWriteLock][docs-readwritelock]. + +Read locks are intended for read-only type operations, such as `getBalance`, and are acquired by calling `readLock().lock()` on the [ReadWriteLock][docs-readwritelock]. +Multiple threads are allowed to acquire read locks at the same time because they are expected to only read data. +This means multiple threads can run `getBalance` at the same time. + +Write locks are for write operations, such as `withdraw` and `deposit`. +It is also used in `open` and `close` as they change the state of the `BankAccount` (they "write" to the `isOpen` field). +Write locks are acquired by calling `writeLock().lock()` on the [ReadWriteLock][docs-readwritelock]. + +Only one thread can hold a write lock at a time. +Therefore, `withdraw`, `deposit`, `open` and `close` can not run at the same time. +Additionally, a thread must _also_ wait for _all_ read locks to be released to obtain a write lock. +Similarly, threads must wait for write locks to be released before they are granted a read lock. +This means `getBalance` also can not run at the same time as any of the `withdraw`, `deposit`, `open` and `close` methods. + +The locks are released in the `finally` block to ensure they are released, even when an exception is thrown. + +[docs-readwritelock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReadWriteLock.html +[docs-reentrantreadwritelock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html diff --git a/exercises/practice/bank-account/.approaches/readwrite-lock/snippet.txt b/exercises/practice/bank-account/.approaches/readwrite-lock/snippet.txt new file mode 100644 index 000000000..45fcdf98d --- /dev/null +++ b/exercises/practice/bank-account/.approaches/readwrite-lock/snippet.txt @@ -0,0 +1,7 @@ +private final ReadWriteLock lock = new ReentrantReadWriteLock(); +lock.readLock().lock(); +try { + balance += amount; +} finally { + lock.readLock().unlock(); +} \ No newline at end of file diff --git a/exercises/practice/bank-account/.approaches/reentrant-lock/content.md b/exercises/practice/bank-account/.approaches/reentrant-lock/content.md new file mode 100644 index 000000000..efa9f9512 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/reentrant-lock/content.md @@ -0,0 +1,96 @@ +# Reentrant Lock + +```java +import java.util.concurrent.locks.ReentrantLock; + +class BankAccount { + + private final ReentrantLock lock = new ReentrantLock(); + + private boolean isOpen = false; + private int balance = 0; + + void open() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (isOpen) { + throw new BankAccountActionInvalidException("Account already open"); + } + isOpen = true; + balance = 0; + } finally { + lock.unlock(); + } + } + + void close() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account not open"); + } + isOpen = false; + } finally { + lock.unlock(); + } + } + + int getBalance() throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + return balance; + } finally { + lock.unlock(); + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance += amount; + } finally { + lock.unlock(); + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + lock.lock(); + try { + if (!isOpen) { + throw new BankAccountActionInvalidException("Account closed"); + } + if (amount > balance) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + balance -= amount; + } finally { + lock.unlock(); + } + } + +} +``` + +A [ReentrantLock][docs-reentrantlock] object represents a lock that threads must acquire to perform certain operations. +It is used here by the operation methods to ensure they are not trying to update the bank account at the same time. + +The lock is requested by calling [lock][docs-reentrantlock-lock]. +The lock is released at the end of the operation by calling [unlock][docs-reentrantlock-unlock] in a `finally` block. +This is important to ensure that the lock is released when it is no longer needed, especially if an exception is thrown. +The re-entrant nature of the lock means a thread will be granted a lock again if it already has the lock. + +[docs-reentrantlock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html +[docs-reentrantlock-lock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html#lock() +[docs-reentrantlock-unlock]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/locks/ReentrantLock.html#unlock() diff --git a/exercises/practice/bank-account/.approaches/reentrant-lock/snippet.txt b/exercises/practice/bank-account/.approaches/reentrant-lock/snippet.txt new file mode 100644 index 000000000..2014610f8 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/reentrant-lock/snippet.txt @@ -0,0 +1,7 @@ +private final ReentrantLock lock = new ReentrantLock(); +lock.lock(); +try { + balance += amount; +} finally { + lock.unlock(); +} \ No newline at end of file diff --git a/exercises/practice/bank-account/.approaches/synchronized-methods/content.md b/exercises/practice/bank-account/.approaches/synchronized-methods/content.md new file mode 100644 index 000000000..7c1730694 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-methods/content.md @@ -0,0 +1,77 @@ +# Synchronized methods + +```java +class BankAccount { + private int balance = 0; + private boolean isClosed = true; + + synchronized void open() throws BankAccountActionInvalidException { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + + synchronized void close() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + + synchronized int getBalance() throws BankAccountActionInvalidException { + checkIfClosed(); + return balance; + } + + synchronized void deposit(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + + synchronized void withdraw(int amount) throws BankAccountActionInvalidException { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +Each operation method is marked `synchronized`. +This tells the thread to acquire a lock on the `BankAccount` object _before_ executing the method. +If any other thread holds a lock on the `BankAccount` object, it must wait for the other thread to release the lock. + +~~~~exercism/note +In Java, the is one other way to acquire a lock on the `BankAccount` object - [synchronized statements][approach-synchronized-statements]. +Since synchronized methods use a lock on the `BankAccount` object, it will also have to wait for locks on the `BankAccount` that are used by [synchronized statements][approach-synchronized-statements] to be reused. + +[approach-synchronized-statements]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronzied-statements +~~~~ + +The lock is automatically released when the method finishes. diff --git a/exercises/practice/bank-account/.approaches/synchronized-methods/snippet.txt b/exercises/practice/bank-account/.approaches/synchronized-methods/snippet.txt new file mode 100644 index 000000000..61ca7d4ec --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-methods/snippet.txt @@ -0,0 +1,3 @@ +synchronized void deposit(int amount) throws BankAccountActionInvalidException { + balance += amount; +} \ No newline at end of file diff --git a/exercises/practice/bank-account/.approaches/synchronized-statements/content.md b/exercises/practice/bank-account/.approaches/synchronized-statements/content.md new file mode 100644 index 000000000..8d17806b1 --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-statements/content.md @@ -0,0 +1,123 @@ +# Synchronized statements + +```java +class BankAccount { + private final Object lock = new Object(); + private int balance = 0; + private boolean isClosed = true; + + void open() throws BankAccountActionInvalidException { + synchronized(lock) { + if (!isClosed) { + throw new BankAccountActionInvalidException("Account already open"); + } + isClosed = false; + balance = 0; + } + } + + void close() throws BankAccountActionInvalidException { + synchronized(lock) { + if (isClosed) { + throw new BankAccountActionInvalidException("Account not open"); + } + isClosed = true; + } + } + + int getBalance() throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + return balance; + } + } + + void deposit(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + + balance += amount; + } + } + + void withdraw(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + checkIfClosed(); + checkIfValidAmount(amount); + checkIfEnoughMoneyInAccount(amount); + + balance -= amount; + } + } + + private void checkIfValidAmount(int amount) throws BankAccountActionInvalidException { + if (amount < 0) { + throw new BankAccountActionInvalidException("Cannot deposit or withdraw negative amount"); + } + } + + private void checkIfEnoughMoneyInAccount(int amount) throws BankAccountActionInvalidException { + if (balance == 0) { + throw new BankAccountActionInvalidException("Cannot withdraw money from an empty account"); + } + if (balance - amount < 0) { + throw new BankAccountActionInvalidException("Cannot withdraw more money than is currently in the account"); + } + } + + private void checkIfClosed() throws BankAccountActionInvalidException { + if (isClosed) { + throw new BankAccountActionInvalidException("Account closed"); + } + } +} +``` + +In this approach, the operation methods, such as `open`, `close`, `deposit` and `withdraw`, perform their operations in a `synchronized` code block. +A lock is acquired on the synchronized object (`lock`) before the statements inside the block are executed. +If another thread has a lock on the object, it must wait for it to be released. +The lock is released after the block is executed. + +## Using `this` as the synchronized object + +Any object can be used as the lock, including `this`. +For example: + +```java +int getBalance() throws BankAccountActionInvalidException { + synchronized(this) { + checkIfClosed(); + return balance; + } +} +``` + +This is the same as using a [synchronized method][approach-synchronized-methods], which requires a lock on the same `this` object to run the method. +For example: + +```java +synchronized int getBalance() throws BankAccountActionInvalidException { + checkIfClosed(); + return balance; +} +``` + +When using [synchronized methods][approach-synchronized-methods] and `synchronized(this)`, it is important to keep in mind that it may be trying to acquire a lock on the same instance. +For example: + +```java +BankAccount account = new BankAccount(); + +Thread thread1 = new Thread(() -> { + account.withdraw(5); +}); + +Thread thread2 = new Thread(() -> { + synchronized (account) { + // Code in here can not run at same time as account.withdraw in thread1. + } +}); +``` + +[approach-synchronized-methods]: https://exercism.org/tracks/java/exercises/bank-account/approaches/synchronized-methods diff --git a/exercises/practice/bank-account/.approaches/synchronized-statements/snippet.txt b/exercises/practice/bank-account/.approaches/synchronized-statements/snippet.txt new file mode 100644 index 000000000..6b3a368fb --- /dev/null +++ b/exercises/practice/bank-account/.approaches/synchronized-statements/snippet.txt @@ -0,0 +1,7 @@ +private final Object lock = new Object(); + +void deposit(int amount) throws BankAccountActionInvalidException { + synchronized(lock) { + balance += amount; + } +} \ No newline at end of file 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/bank-account/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/bank-account/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/bank-account/gradlew b/exercises/practice/bank-account/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/bank-account/gradlew +++ b/exercises/practice/bank-account/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/bank-account/gradlew.bat b/exercises/practice/bank-account/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/bank-account/gradlew.bat +++ b/exercises/practice/bank-account/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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/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-tree/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/binary-search-tree/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/binary-search-tree/gradlew b/exercises/practice/binary-search-tree/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/binary-search-tree/gradlew +++ b/exercises/practice/binary-search-tree/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/binary-search-tree/gradlew.bat b/exercises/practice/binary-search-tree/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/binary-search-tree/gradlew.bat +++ b/exercises/practice/binary-search-tree/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/binary-search/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/binary-search/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/binary-search/gradlew b/exercises/practice/binary-search/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/binary-search/gradlew +++ b/exercises/practice/binary-search/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/binary-search/gradlew.bat b/exercises/practice/binary-search/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/binary-search/gradlew.bat +++ b/exercises/practice/binary-search/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/binary-search/src/test/java/BinarySearchTest.java b/exercises/practice/binary-search/src/test/java/BinarySearchTest.java index 5a1474e96..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,7 +103,8 @@ public void aValueSmallerThanTheArraysSmallestValueIsNotFound() { @Disabled("Remove to run test") @Test - public void aValueLargerThanTheArraysSmallestValueIsNotFound() throws ValueNotFoundException { + @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); BinarySearch search = new BinarySearch(sortedList); @@ -106,6 +116,7 @@ public void aValueLargerThanTheArraysSmallestValueIsNotFound() throws ValueNotFo @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/.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 diff --git a/exercises/practice/bob/.meta/config.json b/exercises/practice/bob/.meta/config.json index 22fda09a3..a86461574 100644 --- a/exercises/practice/bob/.meta/config.json +++ b/exercises/practice/bob/.meta/config.json @@ -7,6 +7,7 @@ "austinlyons", "c-thornton", "FridaTveit", + "jagdish-15", "jmrunkle", "jtigger", "kytrinyx", diff --git a/exercises/practice/bob/.meta/tests.toml b/exercises/practice/bob/.meta/tests.toml index 630485579..5299e2895 100644 --- a/exercises/practice/bob/.meta/tests.toml +++ b/exercises/practice/bob/.meta/tests.toml @@ -1,6 +1,13 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [e162fead-606f-437a-a166-d051915cea8e] description = "stating something" @@ -64,6 +71,7 @@ description = "alternate silence" [66953780-165b-4e7e-8ce3-4bcb80b6385a] description = "multiple line question" +include = false [5371ef75-d9ea-4103-bcfa-2da973ddec1b] description = "starting with whitespace" @@ -76,3 +84,7 @@ description = "other whitespace" [12983553-8601-46a8-92fa-fcaa3bc4a2a0] description = "non-question ending with whitespace" + +[2c7278ac-f955-4eb4-bf8f-e33eb4116a15] +description = "multiple line question" +reimplements = "66953780-165b-4e7e-8ce3-4bcb80b6385a" 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/bob/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/bob/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/bob/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/bob/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/bob/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/bob/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/bob/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/bob/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/bob/gradlew b/exercises/practice/bob/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/bob/gradlew +++ b/exercises/practice/bob/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/bob/gradlew.bat b/exercises/practice/bob/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/bob/gradlew.bat +++ b/exercises/practice/bob/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/bob/src/test/java/BobTest.java b/exercises/practice/bob/src/test/java/BobTest.java index c7a671206..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,13 +175,7 @@ public void alternateSilence() { @Disabled("Remove to run test") @Test - public void multipleLineQuestion() { - assertThat(bob.hey("\nDoes this cryogenic chamber make me look fat?\nNo.")) - .isEqualTo("Whatever."); - } - - @Disabled("Remove to run test") - @Test + @DisplayName("starting with whitespace") public void startingWithWhitespace() { assertThat(bob.hey(" hmmmmmmm...")) .isEqualTo("Whatever."); @@ -168,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."); @@ -175,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!"); @@ -182,9 +199,18 @@ 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."); } + @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/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/book-store/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/book-store/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/book-store/gradlew b/exercises/practice/book-store/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/book-store/gradlew +++ b/exercises/practice/book-store/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/book-store/gradlew.bat b/exercises/practice/book-store/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/book-store/gradlew.bat +++ b/exercises/practice/book-store/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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)) 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/bottle-song/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/bottle-song/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/bottle-song/gradlew b/exercises/practice/bottle-song/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/bottle-song/gradlew +++ b/exercises/practice/bottle-song/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/bottle-song/gradlew.bat b/exercises/practice/bottle-song/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/bottle-song/gradlew.bat +++ b/exercises/practice/bottle-song/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/bowling/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/bowling/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/bowling/gradlew b/exercises/practice/bowling/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/bowling/gradlew +++ b/exercises/practice/bowling/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/bowling/gradlew.bat b/exercises/practice/bowling/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/bowling/gradlew.bat +++ b/exercises/practice/bowling/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.approaches/config.json b/exercises/practice/change/.approaches/config.json new file mode 100644 index 000000000..c24280ab1 --- /dev/null +++ b/exercises/practice/change/.approaches/config.json @@ -0,0 +1,42 @@ +{ + "introduction": { + "authors": [ + "jagdish-15" + ], + "contributors": [ + "kahgoh" + ] + }, + "approaches": [ + { + "uuid": "d0b615ca-3a02-4d66-ad10-e0c513062189", + "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-top-down/content.md b/exercises/practice/change/.approaches/dynamic-programming-top-down/content.md new file mode 100644 index 000000000..06dbf9ba8 --- /dev/null +++ b/exercises/practice/change/.approaches/dynamic-programming-top-down/content.md @@ -0,0 +1,68 @@ +# Dynamic Programming - Top Down + +```java +import java.util.List; +import java.util.ArrayList; + +class ChangeCalculator { + private final List currencyCoins; + + ChangeCalculator(List currencyCoins) { + this.currencyCoins = currencyCoins; + } + + List computeMostEfficientChange(int grandTotal) { + if (grandTotal < 0) + throw new IllegalArgumentException("Negative totals are not allowed."); + + List> coinsUsed = new ArrayList<>(grandTotal + 1); + coinsUsed.add(new ArrayList()); + + for (int i = 1; i <= grandTotal; i++) { + List bestCombination = null; + for (int coin: currencyCoins) { + if (coin <= i && coinsUsed.get(i - coin) != null) { + List currentCombination = new ArrayList<>(coinsUsed.get(i - coin)); + currentCombination.add(0, coin); + if (bestCombination == null || currentCombination.size() < bestCombination.size()) + bestCombination = currentCombination; + } + } + coinsUsed.add(bestCombination); + } + + if (coinsUsed.get(grandTotal) == null) + throw new IllegalArgumentException("The total " + grandTotal + " cannot be represented in the given currency."); + + return coinsUsed.get(grandTotal); + } +} +``` + +The **Dynamic Programming (DP)** approach is an efficient way to solve the problem of making change for a given total using a list of available coin denominations. +It minimizes the number of coins needed by breaking down the problem into smaller subproblems and solving them progressively. + +## Explanation + +### Initialize Coins Usage Tracker + +- If the `grandTotal` is negative, an exception is thrown immediately. +- We create a list `coinsUsed`, where each index `i` stores the most efficient combination of coins that sum up to the value `i`. +- The list is initialized with an empty list at index `0`, as no coins are needed to achieve a total of zero. + +### Iterative Dynamic Programming + +- For each value `i` from 1 to `grandTotal`, we explore all available coin denominations to find the best combination that can achieve the total `i`. +- For each coin, we check if it can be part of the solution (i.e., if `coin <= i` and `coinsUsed[i - coin]` is a valid combination). +- If so, we generate a new combination by adding the current coin to the solution for `i - coin`. We then compare the size of this new combination with the existing best combination and keep the one with fewer coins. + +### Result + +- After processing all values up to `grandTotal`, the combination at `coinsUsed[grandTotal]` will represent the most efficient solution. +- If no valid combination exists for `grandTotal`, an exception is thrown. + +## 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/introduction.md b/exercises/practice/change/.approaches/introduction.md new file mode 100644 index 000000000..de92106d8 --- /dev/null +++ b/exercises/practice/change/.approaches/introduction.md @@ -0,0 +1,162 @@ +# Introduction + +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: 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.")); + + } +} +``` + +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 +import java.util.List; +import java.util.ArrayList; + +class ChangeCalculator { + private final List currencyCoins; + + ChangeCalculator(List currencyCoins) { + this.currencyCoins = currencyCoins; + } + + List computeMostEfficientChange(int grandTotal) { + if (grandTotal < 0) + throw new IllegalArgumentException("Negative totals are not allowed."); + + List> coinsUsed = new ArrayList<>(grandTotal + 1); + coinsUsed.add(new ArrayList()); + + for (int i = 1; i <= grandTotal; i++) { + List bestCombination = null; + for (int coin: currencyCoins) { + if (coin <= i && coinsUsed.get(i - coin) != null) { + List currentCombination = new ArrayList<>(coinsUsed.get(i - coin)); + currentCombination.add(0, coin); + if (bestCombination == null || currentCombination.size() < bestCombination.size()) + bestCombination = currentCombination; + } + } + coinsUsed.add(bestCombination); + } + + if (coinsUsed.get(grandTotal) == null) + throw new IllegalArgumentException("The total " + grandTotal + " cannot be represented in the given currency."); + + return coinsUsed.get(grandTotal); + } +} +``` + +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-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/change/.docs/instructions.md b/exercises/practice/change/.docs/instructions.md index 30fa56775..5887f4cb6 100644 --- a/exercises/practice/change/.docs/instructions.md +++ b/exercises/practice/change/.docs/instructions.md @@ -1,14 +1,8 @@ # Instructions -Correctly determine the fewest number of coins to be given to a customer such that the sum of the coins' value would equal the correct amount of change. +Determine the fewest number of coins to give a customer so that the sum of their values equals the correct amount of change. -## For example +## Examples -- An input of 15 with [1, 5, 10, 25, 100] should return one nickel (5) and one dime (10) or [5, 10] -- An input of 40 with [1, 5, 10, 25, 100] should return one nickel (5) and one dime (10) and one quarter (25) or [5, 10, 25] - -## Edge cases - -- Does your algorithm work for any given set of coins? -- Can you ask for negative change? -- Can you ask for a change value smaller than the smallest coin value? +- An amount of 15 with available coin values [1, 5, 10, 25, 100] should return one coin of value 5 and one coin of value 10, or [5, 10]. +- An amount of 40 with available coin values [1, 5, 10, 25, 100] should return one coin of value 5, one coin of value 10, and one coin of value 25, or [5, 10, 25]. diff --git a/exercises/practice/change/.docs/introduction.md b/exercises/practice/change/.docs/introduction.md new file mode 100644 index 000000000..b4f8308a1 --- /dev/null +++ b/exercises/practice/change/.docs/introduction.md @@ -0,0 +1,26 @@ +# Introduction + +In the mystical village of Coinholt, you stand behind the counter of your bakery, arranging a fresh batch of pastries. +The door creaks open, and in walks Denara, a skilled merchant with a keen eye for quality goods. +After a quick meal, she slides a shimmering coin across the counter, representing a value of 100 units. + +You smile, taking the coin, and glance at the total cost of the meal: 88 units. +That means you need to return 12 units in change. + +Denara holds out her hand expectantly. +"Just give me the fewest coins," she says with a smile. +"My pouch is already full, and I don't want to risk losing them on the road." + +You know you have a few options. +"We have Lumis (worth 10 units), Viras (worth 5 units), and Zenth (worth 2 units) available for change." + +You quickly calculate the possibilities in your head: + +- one Lumis (1 × 10 units) + one Zenth (1 × 2 units) = 2 coins total +- two Viras (2 × 5 units) + one Zenth (1 × 2 units) = 3 coins total +- six Zenth (6 × 2 units) = 6 coins total + +"The best choice is two coins: one Lumis and one Zenth," you say, handing her the change. + +Denara smiles, clearly impressed. +"As always, you've got it right." 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/change/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/change/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/change/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/change/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/change/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/change/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/change/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/change/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/change/gradlew b/exercises/practice/change/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/change/gradlew +++ b/exercises/practice/change/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/change/gradlew.bat b/exercises/practice/change/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/change/gradlew.bat +++ b/exercises/practice/change/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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)); 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/circular-buffer/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/circular-buffer/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/circular-buffer/gradlew b/exercises/practice/circular-buffer/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/circular-buffer/gradlew +++ b/exercises/practice/circular-buffer/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/circular-buffer/gradlew.bat b/exercises/practice/circular-buffer/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/circular-buffer/gradlew.bat +++ b/exercises/practice/circular-buffer/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/clock/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/clock/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/clock/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/clock/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/clock/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/clock/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/clock/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/clock/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/clock/gradlew b/exercises/practice/clock/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/clock/gradlew +++ b/exercises/practice/clock/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/clock/gradlew.bat b/exercises/practice/clock/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/clock/gradlew.bat +++ b/exercises/practice/clock/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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. diff --git a/exercises/practice/collatz-conjecture/.docs/instructions.md b/exercises/practice/collatz-conjecture/.docs/instructions.md index ba060483e..af332a810 100644 --- a/exercises/practice/collatz-conjecture/.docs/instructions.md +++ b/exercises/practice/collatz-conjecture/.docs/instructions.md @@ -1,29 +1,3 @@ # Instructions -The Collatz Conjecture or 3x+1 problem can be summarized as follows: - -Take any positive integer n. -If n is even, divide n by 2 to get n / 2. -If n is odd, multiply n by 3 and add 1 to get 3n + 1. -Repeat the process indefinitely. -The conjecture states that no matter which number you start with, you will always reach 1 eventually. - -Given a number n, return the number of steps required to reach 1. - -## Examples - -Starting with n = 12, the steps would be as follows: - -0. 12 -1. 6 -2. 3 -3. 10 -4. 5 -5. 16 -6. 8 -7. 4 -8. 2 -9. 1 - -Resulting in 9 steps. -So for input n = 12, the return value would be 9. +Given a positive integer, return the number of steps it takes to reach 1 according to the rules of the Collatz Conjecture. diff --git a/exercises/practice/collatz-conjecture/.docs/introduction.md b/exercises/practice/collatz-conjecture/.docs/introduction.md new file mode 100644 index 000000000..c35bdeb67 --- /dev/null +++ b/exercises/practice/collatz-conjecture/.docs/introduction.md @@ -0,0 +1,28 @@ +# Introduction + +One evening, you stumbled upon an old notebook filled with cryptic scribbles, as though someone had been obsessively chasing an idea. +On one page, a single question stood out: **Can every number find its way to 1?** +It was tied to something called the **Collatz Conjecture**, a puzzle that has baffled thinkers for decades. + +The rules were deceptively simple. +Pick any positive integer. + +- If it's even, divide it by 2. +- If it's odd, multiply it by 3 and add 1. + +Then, repeat these steps with the result, continuing indefinitely. + +Curious, you picked number 12 to test and began the journey: + +12 ➜ 6 ➜ 3 ➜ 10 ➜ 5 ➜ 16 ➜ 8 ➜ 4 ➜ 2 ➜ 1 + +Counting from the second number (6), it took 9 steps to reach 1, and each time the rules repeated, the number kept changing. +At first, the sequence seemed unpredictable — jumping up, down, and all over. +Yet, the conjecture claims that no matter the starting number, we'll always end at 1. + +It was fascinating, but also puzzling. +Why does this always seem to work? +Could there be a number where the process breaks down, looping forever or escaping into infinity? +The notebook suggested solving this could reveal something profound — and with it, fame, [fortune][collatz-prize], and a place in history awaits whoever could unlock its secrets. + +[collatz-prize]: https://mathprize.net/posts/collatz-conjecture/ diff --git a/exercises/practice/collatz-conjecture/.meta/config.json b/exercises/practice/collatz-conjecture/.meta/config.json index 3f3853f57..7a8ed70d2 100644 --- a/exercises/practice/collatz-conjecture/.meta/config.json +++ b/exercises/practice/collatz-conjecture/.meta/config.json @@ -35,6 +35,6 @@ ] }, "blurb": "Calculate the number of steps to reach 1 using the Collatz conjecture.", - "source": "An unsolved problem in mathematics named after mathematician Lothar Collatz", - "source_url": "https://en.wikipedia.org/wiki/3x_%2B_1_problem" + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Collatz_conjecture" } 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/collatz-conjecture/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/collatz-conjecture/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/collatz-conjecture/gradlew b/exercises/practice/collatz-conjecture/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/collatz-conjecture/gradlew +++ b/exercises/practice/collatz-conjecture/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/collatz-conjecture/gradlew.bat b/exercises/practice/collatz-conjecture/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/collatz-conjecture/gradlew.bat +++ b/exercises/practice/collatz-conjecture/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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)) diff --git a/exercises/practice/complex-numbers/.docs/instructions.md b/exercises/practice/complex-numbers/.docs/instructions.md index 50b19aedf..2b8a7a49d 100644 --- a/exercises/practice/complex-numbers/.docs/instructions.md +++ b/exercises/practice/complex-numbers/.docs/instructions.md @@ -1,29 +1,100 @@ # Instructions -A complex number is a number in the form `a + b * i` where `a` and `b` are real and `i` satisfies `i^2 = -1`. +A **complex number** is expressed in the form `z = a + b * i`, where: -`a` is called the real part and `b` is called the imaginary part of `z`. -The conjugate of the number `a + b * i` is the number `a - b * i`. -The absolute value of a complex number `z = a + b * i` is a real number `|z| = sqrt(a^2 + b^2)`. The square of the absolute value `|z|^2` is the result of multiplication of `z` by its complex conjugate. +- `a` is the **real part** (a real number), -The sum/difference of two complex numbers involves adding/subtracting their real and imaginary parts separately: -`(a + i * b) + (c + i * d) = (a + c) + (b + d) * i`, -`(a + i * b) - (c + i * d) = (a - c) + (b - d) * i`. +- `b` is the **imaginary part** (also a real number), and -Multiplication result is by definition -`(a + i * b) * (c + i * d) = (a * c - b * d) + (b * c + a * d) * i`. +- `i` is the **imaginary unit** satisfying `i^2 = -1`. -The reciprocal of a non-zero complex number is -`1 / (a + i * b) = a/(a^2 + b^2) - b/(a^2 + b^2) * i`. +## Operations on Complex Numbers -Dividing a complex number `a + i * b` by another `c + i * d` gives: -`(a + i * b) / (c + i * d) = (a * c + b * d)/(c^2 + d^2) + (b * c - a * d)/(c^2 + d^2) * i`. +### Conjugate -Raising e to a complex exponent can be expressed as `e^(a + i * b) = e^a * e^(i * b)`, the last term of which is given by Euler's formula `e^(i * b) = cos(b) + i * sin(b)`. +The conjugate of the complex number `z = a + b * i` is given by: -Implement the following operations: +```text +zc = a - b * i +``` -- addition, subtraction, multiplication and division of two complex numbers, -- conjugate, absolute value, exponent of a given complex number. +### Absolute Value -Assume the programming language you are using does not have an implementation of complex numbers. +The absolute value (or modulus) of `z` is defined as: + +```text +|z| = sqrt(a^2 + b^2) +``` + +The square of the absolute value is computed as the product of `z` and its conjugate `zc`: + +```text +|z|^2 = z * zc = a^2 + b^2 +``` + +### Addition + +The sum of two complex numbers `z1 = a + b * i` and `z2 = c + d * i` is computed by adding their real and imaginary parts separately: + +```text +z1 + z2 = (a + b * i) + (c + d * i) + = (a + c) + (b + d) * i +``` + +### Subtraction + +The difference of two complex numbers is obtained by subtracting their respective parts: + +```text +z1 - z2 = (a + b * i) - (c + d * i) + = (a - c) + (b - d) * i +``` + +### Multiplication + +The product of two complex numbers is defined as: + +```text +z1 * z2 = (a + b * i) * (c + d * i) + = (a * c - b * d) + (b * c + a * d) * i +``` + +### Reciprocal + +The reciprocal of a non-zero complex number is given by: + +```text +1 / z = 1 / (a + b * i) + = a / (a^2 + b^2) - b / (a^2 + b^2) * i +``` + +### Division + +The division of one complex number by another is given by: + +```text +z1 / z2 = z1 * (1 / z2) + = (a + b * i) / (c + d * i) + = (a * c + b * d) / (c^2 + d^2) + (b * c - a * d) / (c^2 + d^2) * i +``` + +### Exponentiation + +Raising _e_ (the base of the natural logarithm) to a complex exponent can be expressed using Euler's formula: + +```text +e^(a + b * i) = e^a * e^(b * i) + = e^a * (cos(b) + i * sin(b)) +``` + +## Implementation Requirements + +Given that you should not use built-in support for complex numbers, implement the following operations: + +- **addition** of two complex numbers +- **subtraction** of two complex numbers +- **multiplication** of two complex numbers +- **division** of two complex numbers +- **conjugate** of a complex number +- **absolute value** of a complex number +- **exponentiation** of _e_ (the base of the natural logarithm) to a complex number diff --git a/exercises/practice/complex-numbers/.meta/config.json b/exercises/practice/complex-numbers/.meta/config.json index 6909ed748..bee3cc59d 100644 --- a/exercises/practice/complex-numbers/.meta/config.json +++ b/exercises/practice/complex-numbers/.meta/config.json @@ -6,6 +6,7 @@ "aadityakulkarni", "FridaTveit", "jackattack24", + "jagdish-15", "jmrunkle", "kytrinyx", "lemoncurry", diff --git a/exercises/practice/complex-numbers/.meta/src/reference/java/ComplexNumber.java b/exercises/practice/complex-numbers/.meta/src/reference/java/ComplexNumber.java index 3bf393ba0..66ef89160 100644 --- a/exercises/practice/complex-numbers/.meta/src/reference/java/ComplexNumber.java +++ b/exercises/practice/complex-numbers/.meta/src/reference/java/ComplexNumber.java @@ -1,7 +1,6 @@ final class ComplexNumber { private final double real; - private final double imaginary; ComplexNumber(double real, double imaginary) { @@ -32,7 +31,10 @@ ComplexNumber subtract(ComplexNumber other) { } ComplexNumber divide(ComplexNumber other) { - return this.multiply(other.conjugate()).divide(Math.pow(other.abs(), 2)); + double divisor = Math.pow(other.real, 2) + Math.pow(other.imaginary, 2); + return new ComplexNumber( + (real * other.real + imaginary * other.imaginary) / divisor, + (imaginary * other.real - real * other.imaginary) / divisor); } double abs() { @@ -44,14 +46,7 @@ ComplexNumber conjugate() { } ComplexNumber exponentialOf() { - return new ComplexNumber(Math.cos(imaginary), Math.sin(imaginary)).multiply(Math.exp(real)); + return new ComplexNumber(Math.exp(real) * Math.cos(imaginary), Math.exp(real) * Math.sin(imaginary)); } - private ComplexNumber divide(double factor) { - return new ComplexNumber(real / factor, imaginary / factor); - } - - private ComplexNumber multiply(double factor) { - return new ComplexNumber(factor * real, factor * imaginary); - } } 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/complex-numbers/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/complex-numbers/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/complex-numbers/gradlew b/exercises/practice/complex-numbers/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/complex-numbers/gradlew +++ b/exercises/practice/complex-numbers/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/complex-numbers/gradlew.bat b/exercises/practice/complex-numbers/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/complex-numbers/gradlew.bat +++ b/exercises/practice/complex-numbers/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/complex-numbers/src/main/java/ComplexNumber.java b/exercises/practice/complex-numbers/src/main/java/ComplexNumber.java index 6a47ada17..0eb95f779 100644 --- a/exercises/practice/complex-numbers/src/main/java/ComplexNumber.java +++ b/exercises/practice/complex-numbers/src/main/java/ComplexNumber.java @@ -28,18 +28,10 @@ ComplexNumber multiply(ComplexNumber other) { throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } - ComplexNumber multiply(double factor) { - throw new UnsupportedOperationException("Delete this statement and write your own implementation."); - } - ComplexNumber divide(ComplexNumber other) { throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } - ComplexNumber divide(double divisor) { - throw new UnsupportedOperationException("Delete this statement and write your own implementation."); - } - ComplexNumber conjugate() { throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } @@ -47,5 +39,4 @@ ComplexNumber conjugate() { ComplexNumber exponentialOf() { throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } - } \ No newline at end of file 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/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/connect/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/connect/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/connect/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/connect/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/connect/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/connect/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/connect/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/connect/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/connect/gradlew b/exercises/practice/connect/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/connect/gradlew +++ b/exercises/practice/connect/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/connect/gradlew.bat b/exercises/practice/connect/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/connect/gradlew.bat +++ b/exercises/practice/connect/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.meta/config.json b/exercises/practice/crypto-square/.meta/config.json index 83572a7b4..5f8c77cc7 100644 --- a/exercises/practice/crypto-square/.meta/config.json +++ b/exercises/practice/crypto-square/.meta/config.json @@ -21,6 +21,7 @@ "sshine", "stkent", "vdemeester", + "Xinri", "Zaldrick" ], "files": { diff --git a/exercises/practice/crypto-square/.meta/tests.toml b/exercises/practice/crypto-square/.meta/tests.toml index 085d142ea..94ef0819f 100644 --- a/exercises/practice/crypto-square/.meta/tests.toml +++ b/exercises/practice/crypto-square/.meta/tests.toml @@ -32,3 +32,8 @@ description = "8 character plaintext results in 3 chunks, the last one with a tr [fbcb0c6d-4c39-4a31-83f6-c473baa6af80] description = "54 character plaintext results in 7 chunks, the last two with trailing spaces" +include = false + +[33fd914e-fa44-445b-8f38-ff8fbc9fe6e6] +description = "54 character plaintext results in 8 chunks, the last two with trailing spaces" +reimplements = "fbcb0c6d-4c39-4a31-83f6-c473baa6af80" 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/crypto-square/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/crypto-square/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/crypto-square/gradlew b/exercises/practice/crypto-square/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/crypto-square/gradlew +++ b/exercises/practice/crypto-square/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/crypto-square/gradlew.bat b/exercises/practice/crypto-square/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/crypto-square/gradlew.bat +++ b/exercises/practice/crypto-square/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/crypto-square/src/test/java/CryptoSquareTest.java b/exercises/practice/crypto-square/src/test/java/CryptoSquareTest.java index 59eb4fc70..9b9225487 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,7 +77,8 @@ public void eightCharacterPlaintextResultsInThreeChunksWithATrailingSpace() { @Disabled("Remove to run test") @Test - public void fiftyFourCharacterPlaintextResultsInSevenChunksWithTrailingSpaces() { + @DisplayName("54 character plaintext results in 8 chunks, the last two with trailing spaces") + public void fiftyFourCharacterPlaintextResultsInEightChunksWithTrailingSpaces() { CryptoSquare cryptoSquare = new CryptoSquare("If man was meant to stay on the ground, god would have " + "given us roots."); String expectedOutput = "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "; 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/custom-set/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/custom-set/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/custom-set/gradlew b/exercises/practice/custom-set/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/custom-set/gradlew +++ b/exercises/practice/custom-set/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/custom-set/gradlew.bat b/exercises/practice/custom-set/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/custom-set/gradlew.bat +++ b/exercises/practice/custom-set/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md index b68d0ada6..8aa7f2591 100644 --- a/exercises/practice/darts/.approaches/introduction.md +++ b/exercises/practice/darts/.approaches/introduction.md @@ -39,19 +39,19 @@ import java.util.function.DoublePredicate; class Darts { - final private static double innerRing = 1.0; - final private static double middleRing = 5.0; - final private static double outerRing = 10.0; + private static final double INNER_RING = 1.0; + private static final double MIDDLE_RING = 5.0; + private static final double OUTER_RING = 10.0; int score(double x, double y) { var pointRadius = (Math.sqrt((x * x) + (y * y))); DoublePredicate thrownOutside = ring -> pointRadius > ring; - if (thrownOutside.test(outerRing)) + if (thrownOutside.test(OUTER_RING)) return 0; - if (thrownOutside.test(middleRing)) + if (thrownOutside.test(MIDDLE_RING)) return 1; - if (thrownOutside.test(innerRing)) + if (thrownOutside.test(INNER_RING)) return 5; return 10; } 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/darts/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/darts/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/darts/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/darts/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/darts/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/darts/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/darts/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/darts/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/darts/gradlew b/exercises/practice/darts/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/darts/gradlew +++ b/exercises/practice/darts/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/darts/gradlew.bat b/exercises/practice/darts/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/darts/gradlew.bat +++ b/exercises/practice/darts/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/diamond/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/diamond/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/diamond/gradlew b/exercises/practice/diamond/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/diamond/gradlew +++ b/exercises/practice/diamond/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/diamond/gradlew.bat b/exercises/practice/diamond/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/diamond/gradlew.bat +++ b/exercises/practice/diamond/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/difference-of-squares/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/difference-of-squares/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/difference-of-squares/gradlew b/exercises/practice/difference-of-squares/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/difference-of-squares/gradlew +++ b/exercises/practice/difference-of-squares/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/difference-of-squares/gradlew.bat b/exercises/practice/difference-of-squares/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/difference-of-squares/gradlew.bat +++ b/exercises/practice/difference-of-squares/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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); 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/dnd-character/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/dnd-character/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/dnd-character/gradlew b/exercises/practice/dnd-character/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/dnd-character/gradlew +++ b/exercises/practice/dnd-character/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/dnd-character/gradlew.bat b/exercises/practice/dnd-character/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/dnd-character/gradlew.bat +++ b/exercises/practice/dnd-character/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/dnd-character/src/test/java/DnDCharacterTest.java b/exercises/practice/dnd-character/src/test/java/DnDCharacterTest.java index 1c9afc921..c54ad52ac 100644 --- a/exercises/practice/dnd-character/src/test/java/DnDCharacterTest.java +++ b/exercises/practice/dnd-character/src/test/java/DnDCharacterTest.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; @@ -10,150 +11,175 @@ public class DnDCharacterTest { private DnDCharacter dndCharacter = new DnDCharacter(); @Test + @DisplayName("ability modifier for score 3 is -4") public void testAbilityModifierForScore3IsNegative4() { assertThat(dndCharacter.modifier(3)).isEqualTo(-4); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 4 is -3") public void testAbilityModifierForScore4IsNegative3() { assertThat(dndCharacter.modifier(4)).isEqualTo(-3); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 5 is -3") public void testAbilityModifierForScore5IsNegative3() { assertThat(dndCharacter.modifier(5)).isEqualTo(-3); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 6 is -2") public void testAbilityModifierForScore6IsNegative2() { assertThat(dndCharacter.modifier(6)).isEqualTo(-2); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 7 is -2") public void testAbilityModifierForScore7IsNegative2() { assertThat(dndCharacter.modifier(7)).isEqualTo(-2); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 8 is -1") public void testAbilityModifierForScore8IsNegative1() { assertThat(dndCharacter.modifier(8)).isEqualTo(-1); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 9 is -1") public void testAbilityModifierForScore9IsNegative1() { assertThat(dndCharacter.modifier(9)).isEqualTo(-1); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 10 is 0") public void testAbilityModifierForScore10Is0() { assertThat(dndCharacter.modifier(10)).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 11 is 0") public void testAbilityModifierForScore11Is0() { assertThat(dndCharacter.modifier(11)).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 12 is +1") public void testAbilityModifierForScore12Is1() { assertThat(dndCharacter.modifier(12)).isEqualTo(1); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 13 is +1") public void testAbilityModifierForScore13Is1() { assertThat(dndCharacter.modifier(13)).isEqualTo(1); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 14 is +2") public void testAbilityModifierForScore14Is2() { assertThat(dndCharacter.modifier(14)).isEqualTo(2); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 15 is +2") public void testAbilityModifierForScore15Is2() { assertThat(dndCharacter.modifier(15)).isEqualTo(2); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 16 is +3") public void testAbilityModifierForScore16Is3() { assertThat(dndCharacter.modifier(16)).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 17 is +3") public void testAbilityModifierForScore17Is3() { assertThat(dndCharacter.modifier(17)).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("ability modifier for score 18 is +4") public void testAbilityModifierForScore18Is4() { assertThat(dndCharacter.modifier(18)).isEqualTo(4); } @Disabled("Remove to run test") @Test + @DisplayName("Rolling uses 4 dice") public void test4DiceWereUsedForRollingScores() { assertThat(dndCharacter.rollDice().size()).isEqualTo(4); } @Disabled("Remove to run test") @Test + @DisplayName("Dice values are between 1 and 6 inclusive") public void testDiceValuesBetween1And6() { assertThat(dndCharacter.rollDice()).allMatch(d -> d >= 1 && d <= 6); } @Disabled("Remove to run test") @Test + @DisplayName("Ability uses 3 largest numbers from scores in descending order") public void testAbilityCalculationsUses3LargestNumbersFromScoresInDescendingOrder() { assertThat(dndCharacter.ability(List.of(4, 3, 2, 1))).isEqualTo(9); } @Disabled("Remove to run test") @Test + @DisplayName("Ability uses 3 largest numbers from scores in ascending order") public void testAbilityCalculationsUses3LargestNumbersFromFromScoresInAscendingOrder() { assertThat(dndCharacter.ability(List.of(1, 2, 3, 4))).isEqualTo(9); } @Disabled("Remove to run test") @Test + @DisplayName("Ability uses 3 largest numbers from scores in random order") public void testAbilityCalculationsUses3LargestNumbersFromScoresInRandomOrder() { assertThat(dndCharacter.ability(List.of(2, 4, 3, 1))).isEqualTo(9); } @Disabled("Remove to run test") @Test + @DisplayName("Ability with all lowest equal numbers yields 3") public void testAbilityCalculationsWithLowestEqualNumbers() { assertThat(dndCharacter.ability(List.of(1, 1, 1, 1))).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("Ability with all highest equal numbers yields 18") public void testAbilityCalculationsWithHighestEqualNumbers() { assertThat(dndCharacter.ability(List.of(6, 6, 6, 6))).isEqualTo(18); } @Disabled("Remove to run test") @Test + @DisplayName("Ability calculation with two lowest numbers") public void testAbilityCalculationsWithTwoLowestNumbers() { assertThat(dndCharacter.ability(List.of(3, 5, 3, 4))).isEqualTo(12); } @Disabled("Remove to run test") @Test + @DisplayName("Ability calculation does not mutate input scores") public void testAbilityCalculationDoesNotChangeInputScores() { List scores = List.of(1, 2, 3, 4); dndCharacter.ability(scores); @@ -164,6 +190,7 @@ public void testAbilityCalculationDoesNotChangeInputScores() { @Disabled("Remove to run test") @Test + @DisplayName("random character is valid") public void testRandomCharacterIsValid() { for (int i = 0; i < 1000; i++) { DnDCharacter character = new DnDCharacter(); @@ -179,6 +206,7 @@ public void testRandomCharacterIsValid() { @Disabled("Remove to run test") @Test + @DisplayName("each ability is only calculated once") public void testEachAbilityIsOnlyCalculatedOnce() { assertThat(dndCharacter.getStrength()).isEqualTo(dndCharacter.getStrength()); assertThat(dndCharacter.getDexterity()).isEqualTo(dndCharacter.getDexterity()); @@ -190,6 +218,7 @@ public void testEachAbilityIsOnlyCalculatedOnce() { @Disabled("Remove to run test") @Test + @DisplayName("Each randomly created character should be unique in attributes") public void testUniqueCharacterIsCreated() { DnDCharacter uniqueDnDCharacter = new DnDCharacter(); for (int i = 0; i < 1000; i++) { diff --git a/exercises/practice/dominoes/.docs/instructions.md b/exercises/practice/dominoes/.docs/instructions.md index 1ced9f644..75055b9e8 100644 --- a/exercises/practice/dominoes/.docs/instructions.md +++ b/exercises/practice/dominoes/.docs/instructions.md @@ -2,7 +2,9 @@ Make a chain of dominoes. -Compute a way to order a given set of dominoes in such a way that they form a correct domino chain (the dots on one half of a stone match the dots on the neighboring half of an adjacent stone) and that dots on the halves of the stones which don't have a neighbor (the first and last stone) match each other. +Compute a way to order a given set of domino stones so that they form a correct domino chain. +In the chain, the dots on one half of a stone must match the dots on the neighboring half of an adjacent stone. +Additionally, the dots on the halves of the stones without neighbors (the first and last stone) must match each other. For example given the stones `[2|1]`, `[2|3]` and `[1|3]` you should compute something like `[1|2] [2|3] [3|1]` or `[3|2] [2|1] [1|3]` or `[1|3] [3|2] [2|1]` etc, where the first and last numbers are the same. diff --git a/exercises/practice/dominoes/.docs/introduction.md b/exercises/practice/dominoes/.docs/introduction.md new file mode 100644 index 000000000..df248c211 --- /dev/null +++ b/exercises/practice/dominoes/.docs/introduction.md @@ -0,0 +1,13 @@ +# Introduction + +In Toyland, the trains are always busy delivering treasures across the city, from shiny marbles to rare building blocks. +The tracks they run on are made of colorful domino-shaped pieces, each marked with two numbers. +For the trains to move, the dominoes must form a perfect chain where the numbers match. + +Today, an urgent delivery of rare toys is on hold. +You've been handed a set of track pieces to inspect. +If they can form a continuous chain, the train will be on its way, bringing smiles across Toyland. +If not, the set will be discarded, and another will be tried. + +The toys are counting on you to solve this puzzle. +Will the dominoes connect the tracks and send the train rolling, or will the set be left behind? 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/dominoes/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/dominoes/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/dominoes/gradlew b/exercises/practice/dominoes/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/dominoes/gradlew +++ b/exercises/practice/dominoes/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/dominoes/gradlew.bat b/exercises/practice/dominoes/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/dominoes/gradlew.bat +++ b/exercises/practice/dominoes/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/dot-dsl/.docs/instructions.append.md b/exercises/practice/dot-dsl/.docs/instructions.append.md new file mode 100644 index 000000000..e110bf76b --- /dev/null +++ b/exercises/practice/dot-dsl/.docs/instructions.append.md @@ -0,0 +1,6 @@ +# Instructions append + +The graph is represented in the DSL by the `Graph` class. +The implementation for the nodes and edges (represented by the `Node` and `Edge` classes respectively) are provided. + +For more details on the DSL's expected design, take a look at the test cases in `GraphTest.java`. diff --git a/exercises/practice/dot-dsl/.docs/instructions.md b/exercises/practice/dot-dsl/.docs/instructions.md new file mode 100644 index 000000000..5e65ebef9 --- /dev/null +++ b/exercises/practice/dot-dsl/.docs/instructions.md @@ -0,0 +1,30 @@ +# Instructions + +A [Domain Specific Language (DSL)][dsl] is a small language optimized for a specific domain. +Since a DSL is targeted, it can greatly impact productivity/understanding by allowing the writer to declare _what_ they want rather than _how_. + +One problem area where they are applied are complex customizations/configurations. + +For example the [DOT language][dot-language] allows you to write a textual description of a graph which is then transformed into a picture by one of the [Graphviz][graphviz] tools (such as `dot`). +A simple graph looks like this: + + graph { + graph [bgcolor="yellow"] + a [color="red"] + b [color="blue"] + a -- b [color="green"] + } + +Putting this in a file `example.dot` and running `dot example.dot -T png -o example.png` creates an image `example.png` with red and blue circle connected by a green line on a yellow background. + +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. + +[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) +[graphviz]: https://graphviz.org/ +[fowler-dsl]: https://martinfowler.com/bliki/DomainSpecificLanguage.html diff --git a/exercises/practice/dot-dsl/.meta/config.json b/exercises/practice/dot-dsl/.meta/config.json new file mode 100644 index 000000000..d1c57ef6e --- /dev/null +++ b/exercises/practice/dot-dsl/.meta/config.json @@ -0,0 +1,27 @@ +{ + "authors": [ + "kahgoh", + "manumafe98" + ], + "files": { + "solution": [ + "src/main/java/Graph.java" + ], + "test": [ + "src/test/java/GraphTest.java" + ], + "example": [ + ".meta/src/reference/java/Graph.java" + ], + "editor": [ + "src/main/java/Node.java", + "src/main/java/Edge.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Write a Domain Specific Language similar to the Graphviz dot language.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/DOT_(graph_description_language)" +} diff --git a/exercises/practice/dot-dsl/.meta/src/reference/java/Edge.java b/exercises/practice/dot-dsl/.meta/src/reference/java/Edge.java new file mode 100644 index 000000000..7168bfa1d --- /dev/null +++ b/exercises/practice/dot-dsl/.meta/src/reference/java/Edge.java @@ -0,0 +1,48 @@ +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Represents an edge on the graph. + * + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public final class Edge { + private final String start; + + private final String end; + + private final Map attributes; + + public Edge(String start, String end) { + this.start = start; + this.end = end; + this.attributes = Collections.emptyMap(); + } + + public Edge(String start, String end, Map attributes) { + this.start = start; + this.end = end; + this.attributes = Map.copyOf(attributes); + } + + @Override + public int hashCode() { + return Objects.hash(start, end, attributes); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Edge other) { + return Objects.equals(other.start, start) + && Objects.equals(other.end, end) + && Objects.equals(other.attributes, attributes); + } + return false; + } + + @Override + public String toString() { + return "Edge [start=" + start + ", end=" + end + ", attributes=" + attributes + "]"; + } +} diff --git a/exercises/practice/dot-dsl/.meta/src/reference/java/Graph.java b/exercises/practice/dot-dsl/.meta/src/reference/java/Graph.java new file mode 100644 index 000000000..91a8f176b --- /dev/null +++ b/exercises/practice/dot-dsl/.meta/src/reference/java/Graph.java @@ -0,0 +1,54 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class Graph { + + private final List nodes = new ArrayList<>(); + + private final List edges = new ArrayList<>(); + + private final Map attributes; + + public Graph() { + this.attributes = Collections.emptyMap(); + } + + public Graph(Map attributes) { + this.attributes = Map.copyOf(attributes); + } + + public Collection getNodes() { + return Collections.unmodifiableList(nodes); + } + + public Collection getEdges() { + return Collections.unmodifiableList(edges); + } + + public Graph node(String name) { + nodes.add(new Node(name)); + return this; + } + + public Graph node(String name, Map attributes) { + nodes.add(new Node(name, attributes)); + return this; + } + + public Graph edge(String start, String end) { + edges.add(new Edge(start, end)); + return this; + } + + public Graph edge(String start, String end, Map attributes) { + edges.add(new Edge(start, end, attributes)); + return this; + } + + public Map getAttributes() { + return attributes; + } +} diff --git a/exercises/practice/dot-dsl/.meta/src/reference/java/Node.java b/exercises/practice/dot-dsl/.meta/src/reference/java/Node.java new file mode 100644 index 000000000..0aa76e58d --- /dev/null +++ b/exercises/practice/dot-dsl/.meta/src/reference/java/Node.java @@ -0,0 +1,43 @@ +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Represents a node on the graph. + * + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public final class Node { + + private final String name; + + private final Map attributes; + + public Node(String name) { + this.name = name; + this.attributes = Collections.emptyMap(); + } + + public Node(String name, Map attributes) { + this.name = name; + this.attributes = Map.copyOf(attributes); + } + + @Override + public int hashCode() { + return Objects.hash(name, attributes); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Node other) { + return Objects.equals(other.name, name) && Objects.equals(other.attributes, attributes); + } + return false; + } + + @Override + public String toString() { + return "Node [name=" + name + ", attributes=" + attributes + "]"; + } +} diff --git a/exercises/practice/dot-dsl/build.gradle b/exercises/practice/dot-dsl/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/practice/dot-dsl/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/dot-dsl/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/dot-dsl/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/dot-dsl/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/dot-dsl/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/dot-dsl/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/dot-dsl/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/dot-dsl/gradlew b/exercises/practice/dot-dsl/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/dot-dsl/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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/dot-dsl/gradlew.bat b/exercises/practice/dot-dsl/gradlew.bat new file mode 100644 index 000000000..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/dot-dsl/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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/dot-dsl/src/main/java/Edge.java b/exercises/practice/dot-dsl/src/main/java/Edge.java new file mode 100644 index 000000000..7168bfa1d --- /dev/null +++ b/exercises/practice/dot-dsl/src/main/java/Edge.java @@ -0,0 +1,48 @@ +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Represents an edge on the graph. + * + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public final class Edge { + private final String start; + + private final String end; + + private final Map attributes; + + public Edge(String start, String end) { + this.start = start; + this.end = end; + this.attributes = Collections.emptyMap(); + } + + public Edge(String start, String end, Map attributes) { + this.start = start; + this.end = end; + this.attributes = Map.copyOf(attributes); + } + + @Override + public int hashCode() { + return Objects.hash(start, end, attributes); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Edge other) { + return Objects.equals(other.start, start) + && Objects.equals(other.end, end) + && Objects.equals(other.attributes, attributes); + } + return false; + } + + @Override + public String toString() { + return "Edge [start=" + start + ", end=" + end + ", attributes=" + attributes + "]"; + } +} diff --git a/exercises/practice/dot-dsl/src/main/java/Graph.java b/exercises/practice/dot-dsl/src/main/java/Graph.java new file mode 100644 index 000000000..01861a708 --- /dev/null +++ b/exercises/practice/dot-dsl/src/main/java/Graph.java @@ -0,0 +1,38 @@ +import java.util.Collection; +import java.util.Map; + +public class Graph { + public Graph() { + } + + public Graph(Map attributes) { + } + + public Collection getNodes() { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public Collection getEdges() { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public Graph node(String name) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public Graph node(String name, Map attributes) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public Graph edge(String start, String end) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public Graph edge(String start, String end, Map attributes) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public Map getAttributes() { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } +} diff --git a/exercises/practice/dot-dsl/src/main/java/Node.java b/exercises/practice/dot-dsl/src/main/java/Node.java new file mode 100644 index 000000000..0aa76e58d --- /dev/null +++ b/exercises/practice/dot-dsl/src/main/java/Node.java @@ -0,0 +1,43 @@ +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + +/** + * Represents a node on the graph. + * + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public final class Node { + + private final String name; + + private final Map attributes; + + public Node(String name) { + this.name = name; + this.attributes = Collections.emptyMap(); + } + + public Node(String name, Map attributes) { + this.name = name; + this.attributes = Map.copyOf(attributes); + } + + @Override + public int hashCode() { + return Objects.hash(name, attributes); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Node other) { + return Objects.equals(other.name, name) && Objects.equals(other.attributes, attributes); + } + return false; + } + + @Override + public String toString() { + return "Node [name=" + name + ", attributes=" + attributes + "]"; + } +} diff --git a/exercises/practice/dot-dsl/src/test/java/GraphTest.java b/exercises/practice/dot-dsl/src/test/java/GraphTest.java new file mode 100644 index 000000000..34c9c0a56 --- /dev/null +++ b/exercises/practice/dot-dsl/src/test/java/GraphTest.java @@ -0,0 +1,105 @@ +import static java.util.Collections.emptyMap; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class GraphTest { + + @Test + @DisplayName("empty graph") + public void testEmptyGraph() { + Graph graph = new Graph(); + + assertThat(graph.getNodes()).isEmpty(); + assertThat(graph.getEdges()).isEmpty(); + assertThat(graph.getAttributes()).isEmpty(); + } + + @Test + @Disabled + @DisplayName("graph with one node") + public void testGraphWithOneNode() { + Graph graph = new Graph().node("a"); + + assertThat(graph.getNodes()).containsExactly(new Node("a", emptyMap())); + assertThat(graph.getEdges()).isEmpty(); + assertThat(graph.getAttributes()).isEmpty(); + } + + @Test + @Disabled + @DisplayName("graph with one node with keywords") + public void testGraphWithOneNodeWithKeywords() { + Graph graph = new Graph().node("a", Map.of("color", "green")); + + assertThat(graph.getNodes()) + .containsExactly(new Node("a", Map.of("color", "green"))); + assertThat(graph.getEdges()).isEmpty(); + assertThat(graph.getAttributes()).isEmpty(); + } + + @Test + @Disabled + @DisplayName("graph with one edge") + public void testGraphWithOneEdge() { + Graph graph = new Graph().edge("a", "b"); + + assertThat(graph.getNodes()).isEmpty(); + assertThat(graph.getEdges()) + .containsExactly(new Edge("a", "b")); + assertThat(graph.getAttributes()).isEmpty(); + } + + @Test + @Disabled + @DisplayName("graph with one edge with keywords") + public void testGraphWithOneEdgeWithKeywords() { + Graph graph = new Graph().edge("a", "b", Map.of("color", "blue")); + + assertThat(graph.getNodes()).isEmpty(); + assertThat(graph.getEdges()) + .containsExactly(new Edge("a", "b", Map.of("color", "blue"))); + assertThat(graph.getAttributes()).isEmpty(); + } + + @Test + @Disabled + @DisplayName("graph with one attribute") + public void testGraphWithOneAttribute() { + Graph graph = new Graph(Map.of("foo", "1")); + + assertThat(graph.getNodes()).isEmpty(); + assertThat(graph.getEdges()).isEmpty(); + assertThat(graph.getAttributes()).containsEntry("foo", "1"); + } + + @Test + @Disabled + @DisplayName("graph with attributes") + public void testGraphWithAttributes() { + Graph graph = new Graph(Map.of("foo", "1", "title", "Testing Attrs", "bar", "true")) + .node("a", Map.of("color", "green")) + .node("c") + .node("b", Map.of("label", "Beta!")) + .edge("b", "c") + .edge("a", "b", Map.of("color", "blue")); + + assertThat(graph.getNodes()) + .containsExactlyInAnyOrder( + new Node("a", Map.of("color", "green")), + new Node("c"), + new Node("b", Map.of("label", "Beta!"))); + + assertThat(graph.getEdges()) + .containsExactlyInAnyOrder( + new Edge("a", "b", Map.of("color", "blue")), + new Edge("b", "c")); + + assertThat(graph.getAttributes()) + .containsExactlyInAnyOrderEntriesOf( + Map.of("foo", "1", "title", "Testing Attrs", "bar", "true")); + } +} diff --git a/exercises/practice/eliuds-eggs/.docs/introduction.md b/exercises/practice/eliuds-eggs/.docs/introduction.md index 49eaffd8b..2b2e5c43d 100644 --- a/exercises/practice/eliuds-eggs/.docs/introduction.md +++ b/exercises/practice/eliuds-eggs/.docs/introduction.md @@ -12,36 +12,54 @@ The position information encoding is calculated as follows: 2. Convert the number from binary to decimal. 3. Show the result on the display. -Example 1: +## Example 1 + +![Seven individual nest boxes arranged in a row whose first, third, fourth and seventh nests each have a single egg.](https://assets.exercism.org/images/exercises/eliuds-eggs/example-1-coop.svg) ```text -Chicken Coop: _ _ _ _ _ _ _ |E| |E|E| | |E| +``` + +### Resulting Binary + +![1011001](https://assets.exercism.org/images/exercises/eliuds-eggs/example-1-binary.svg) + +```text + _ _ _ _ _ _ _ +|1|0|1|1|0|0|1| +``` -Resulting Binary: - 1 0 1 1 0 0 1 +### Decimal number on the display -Decimal number on the display: 89 -Actual eggs in the coop: +### Actual eggs in the coop + 4 + +## Example 2 + +![Seven individual nest boxes arranged in a row where only the fourth nest has an egg.](https://assets.exercism.org/images/exercises/eliuds-eggs/example-2-coop.svg) + +```text + _ _ _ _ _ _ _ +| | | |E| | | | ``` -Example 2: +### Resulting Binary + +![0001000](https://assets.exercism.org/images/exercises/eliuds-eggs/example-2-binary.svg) ```text -Chicken Coop: - _ _ _ _ _ _ _ _ -| | | |E| | | | | + _ _ _ _ _ _ _ +|0|0|0|1|0|0|0| +``` -Resulting Binary: - 0 0 0 1 0 0 0 0 +### Decimal number on the display -Decimal number on the display: -16 +8 + +### Actual eggs in the coop -Actual eggs in the coop: 1 -``` 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/eliuds-eggs/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/eliuds-eggs/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/eliuds-eggs/gradlew b/exercises/practice/eliuds-eggs/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/eliuds-eggs/gradlew +++ b/exercises/practice/eliuds-eggs/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/eliuds-eggs/gradlew.bat b/exercises/practice/eliuds-eggs/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/eliuds-eggs/gradlew.bat +++ b/exercises/practice/eliuds-eggs/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/error-handling/.docs/instructions.append.md b/exercises/practice/error-handling/.docs/instructions.append.md index 10a61fdba..6f1650d3a 100644 --- a/exercises/practice/error-handling/.docs/instructions.append.md +++ b/exercises/practice/error-handling/.docs/instructions.append.md @@ -22,7 +22,7 @@ This is because checked exceptions are meant to be handled at runtime, i.e. they They're often used when a method can't return any valid result, for example a search method which hasn't found the item it was searching for. -It's an alternative to returning null or a error code. A checked exception is better than those alternatives because it forces the user of the method to consider the error case. +It's an alternative to returning null or an error code. A checked exception is better than those alternatives because it forces the user of the method to consider the error case. ## Unchecked exceptions @@ -34,4 +34,4 @@ You don't have to declare them in the [method signature](https://docs.oracle.com ### Examples of where they are used -Unchecked exceptions are mean to be used for any error than can't be handled at runtime, e.g. running out of memory. +Unchecked exceptions are typically caused by defects in the program and can be prevented by proper coding. Example: ArrayIndexOutOfBoundsException can be prevented by ensuring that the array indices are between 0 and the array's length. 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/error-handling/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/error-handling/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/error-handling/gradlew b/exercises/practice/error-handling/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/error-handling/gradlew +++ b/exercises/practice/error-handling/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/error-handling/gradlew.bat b/exercises/practice/error-handling/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/error-handling/gradlew.bat +++ b/exercises/practice/error-handling/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/error-handling/src/test/java/ErrorHandlingTest.java b/exercises/practice/error-handling/src/test/java/ErrorHandlingTest.java index c7864c7ad..7459b687f 100644 --- a/exercises/practice/error-handling/src/test/java/ErrorHandlingTest.java +++ b/exercises/practice/error-handling/src/test/java/ErrorHandlingTest.java @@ -1,16 +1,18 @@ -import static org.assertj.core.api.Assertions.assertThat; -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.Optional; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + public class ErrorHandlingTest { private ErrorHandling errorHandling = new ErrorHandling(); @Test + @DisplayName("Throws IllegalArgumentException") public void testThrowIllegalArgumentException() { assertThatExceptionOfType(Exception.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingIllegalArgumentException()); @@ -18,6 +20,7 @@ public void testThrowIllegalArgumentException() { @Disabled("Remove to run test") @Test + @DisplayName("Throws IllegalArgumentException with provided detail message") public void testThrowIllegalArgumentExceptionWithDetailMessage() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingIllegalArgumentExceptionWithDetailMessage( @@ -27,6 +30,7 @@ public void testThrowIllegalArgumentExceptionWithDetailMessage() { @Disabled("Remove to run test") @Test + @DisplayName("Throws any checked exception") public void testThrowAnyCheckedException() { assertThatExceptionOfType(Exception.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingAnyCheckedException()) @@ -35,6 +39,7 @@ public void testThrowAnyCheckedException() { @Disabled("Remove to run test") @Test + @DisplayName("Throws any checked exception with provided detail message") public void testThrowAnyCheckedExceptionWithDetailMessage() { assertThatExceptionOfType(Exception.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingAnyCheckedExceptionWithDetailMessage( @@ -45,6 +50,7 @@ public void testThrowAnyCheckedExceptionWithDetailMessage() { @Disabled("Remove to run test") @Test + @DisplayName("Throws any unchecked exception") public void testThrowAnyUncheckedException() { assertThatExceptionOfType(RuntimeException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingAnyUncheckedException()); @@ -52,6 +58,7 @@ public void testThrowAnyUncheckedException() { @Disabled("Remove to run test") @Test + @DisplayName("Throws any unchecked exception with provided detail message") public void testThrowAnyUncheckedExceptionWithDetailMessage() { assertThatExceptionOfType(RuntimeException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingAnyUncheckedExceptionWithDetailMessage( @@ -61,6 +68,7 @@ public void testThrowAnyUncheckedExceptionWithDetailMessage() { @Disabled("Remove to run test") @Test + @DisplayName("Throws custom checked exception") public void testThrowCustomCheckedException() { assertThatExceptionOfType(CustomCheckedException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingCustomCheckedException()); @@ -68,6 +76,7 @@ public void testThrowCustomCheckedException() { @Disabled("Remove to run test") @Test + @DisplayName("Throws custom checked exception with provided detail message") public void testThrowCustomCheckedExceptionWithDetailMessage() { assertThatExceptionOfType(CustomCheckedException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingCustomCheckedExceptionWithDetailMessage( @@ -77,6 +86,7 @@ public void testThrowCustomCheckedExceptionWithDetailMessage() { @Disabled("Remove to run test") @Test + @DisplayName("Throws custom unchecked exception") public void testThrowCustomUncheckedException() { assertThatExceptionOfType(CustomUncheckedException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingCustomUncheckedException()); @@ -84,6 +94,7 @@ public void testThrowCustomUncheckedException() { @Disabled("Remove to run test") @Test + @DisplayName("Throws custom unchecked exception with provided detail message") public void testThrowCustomUncheckedExceptionWithDetailMessage() { assertThatExceptionOfType(CustomUncheckedException.class) .isThrownBy(() -> errorHandling.handleErrorByThrowingCustomUncheckedExceptionWithDetailMessage( @@ -93,6 +104,7 @@ public void testThrowCustomUncheckedExceptionWithDetailMessage() { @Disabled("Remove to run test") @Test + @DisplayName("Handles error by throwing Optional instance") public void testReturnOptionalInstance() { Optional successfulResult = errorHandling.handleErrorByReturningOptionalInstance("1"); assertThat(successfulResult).isPresent().hasValue(1); 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/etl/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/etl/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/etl/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/etl/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/etl/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/etl/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/etl/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/etl/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/etl/gradlew b/exercises/practice/etl/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/etl/gradlew +++ b/exercises/practice/etl/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/etl/gradlew.bat b/exercises/practice/etl/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/etl/gradlew.bat +++ b/exercises/practice/etl/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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/config.json b/exercises/practice/flatten-array/.meta/config.json index 938167ee9..8b521fdd6 100644 --- a/exercises/practice/flatten-array/.meta/config.json +++ b/exercises/practice/flatten-array/.meta/config.json @@ -6,6 +6,7 @@ "aadityakulkarni", "FridaTveit", "jackattack24", + "jagdish-15", "jmrunkle", "jtigger", "kytrinyx", diff --git a/exercises/practice/flatten-array/.meta/src/reference/java/Flattener.java b/exercises/practice/flatten-array/.meta/src/reference/java/Flattener.java index 0c3e5e7a3..5cff9efd1 100644 --- a/exercises/practice/flatten-array/.meta/src/reference/java/Flattener.java +++ b/exercises/practice/flatten-array/.meta/src/reference/java/Flattener.java @@ -1,28 +1,17 @@ import java.util.ArrayList; -import java.util.Collections; import java.util.List; -final class Flattener { +class Flattener { - List flatten(final List nestedList) { - if (nestedList.isEmpty()) { - return new ArrayList<>(); - } else { - final List result = new ArrayList(); - - final Object head = nestedList.get(0); - final List tail = nestedList.subList(1, nestedList.size()); - - if (head instanceof List) { - result.addAll(flatten((List) head)); - } else { - result.add(head); + List flatten(List list) { + List flattenedList = new ArrayList<>(); + for (Object element: list) { + if (element instanceof List listAsElement) { + flattenedList.addAll(flatten(listAsElement)); + } else if (element != null) { + flattenedList.add(element); } - - result.addAll(flatten(tail)); - result.removeAll(Collections.singleton(null)); - return result; } + return flattenedList; } - } 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" 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/flatten-array/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/flatten-array/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/flatten-array/gradlew b/exercises/practice/flatten-array/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/flatten-array/gradlew +++ b/exercises/practice/flatten-array/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/flatten-array/gradlew.bat b/exercises/practice/flatten-array/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/flatten-array/gradlew.bat +++ b/exercises/practice/flatten-array/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/flatten-array/src/main/java/Flattener.java b/exercises/practice/flatten-array/src/main/java/Flattener.java index c2759577f..69db9b0b4 100644 --- a/exercises/practice/flatten-array/src/main/java/Flattener.java +++ b/exercises/practice/flatten-array/src/main/java/Flattener.java @@ -2,7 +2,7 @@ class Flattener { - List flatten(List list) { + List flatten(List list) { throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } 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, 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..965ba8fd4 --- /dev/null +++ b/exercises/practice/flower-field/.meta/tests.toml @@ -0,0 +1,49 @@ +# 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" + +[6e4ac13a-3e43-4728-a2e3-3551d4b1a996] +description = "multiple adjacent flowers" 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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/flower-field/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/flower-field/gradlew b/exercises/practice/flower-field/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/flower-field/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/flower-field/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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..58f995293 --- /dev/null +++ b/exercises/practice/flower-field/src/test/java/FlowerFieldBoardTest.java @@ -0,0 +1,269 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FlowerFieldBoardTest { + + @Test + @DisplayName("no rows") + 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 + @DisplayName("no columns") + public void testInputBoardWithOneRowAndNoColumns() { + List inputBoard = List.of(""); + List expectedNumberedBoard = List.of(""); + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("no flowers") + public void testInputBoardWithNoFlowers() { + List inputBoard = List.of( + " ", + " ", + " " + ); + + List expectedNumberedBoard = List.of( + " ", + " ", + " " + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("garden full of flowers") + public void testInputBoardWithOnlyFlowers() { + List inputBoard = List.of( + "***", + "***", + "***" + ); + + List expectedNumberedBoard = List.of( + "***", + "***", + "***" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("flower surrounded by spaces") + public void testInputBoardWithSingleFlowerAtCenter() { + List inputBoard = List.of( + " ", + " * ", + " " + ); + + List expectedNumberedBoard = List.of( + "111", + "1*1", + "111" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("space surrounded by flowers") + public void testInputBoardWithFlowersAroundPerimeter() { + List inputBoard = List.of( + "***", + "* *", + "***" + ); + + List expectedNumberedBoard = List.of( + "***", + "*8*", + "***" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("horizontal line") + public void testInputBoardWithSingleRowAndTwoFlowers() { + List inputBoard = List.of( + " * * " + ); + + List expectedNumberedBoard = List.of( + "1*2*1" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("horizontal line, flowers at edges") + public void testInputBoardWithSingleRowAndTwoFlowersAtEdges() { + List inputBoard = List.of( + "* *" + ); + + List expectedNumberedBoard = List.of( + "*1 1*" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("vertical line") + public void testInputBoardWithSingleColumnAndTwoFlowers() { + List inputBoard = List.of( + " ", + "*", + " ", + "*", + " " + ); + + List expectedNumberedBoard = List.of( + "1", + "*", + "2", + "*", + "1" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("vertical line, flowers at edges") + public void testInputBoardWithSingleColumnAndTwoFlowersAtEdges() { + List inputBoard = List.of( + "*", + " ", + " ", + " ", + "*" + ); + + List expectedNumberedBoard = List.of( + "*", + "1", + " ", + "1", + "*" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("cross") + public void testInputBoardWithFlowersInCross() { + List inputBoard = List.of( + " * ", + " * ", + "*****", + " * ", + " * " + ); + + List expectedNumberedBoard = List.of( + " 2*2 ", + "25*52", + "*****", + "25*52", + " 2*2 " + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("large garden") + public void testLargeInputBoard() { + List inputBoard = List.of( + " * * ", + " * ", + " * ", + " * *", + " * * ", + " " + ); + + List expectedNumberedBoard = List.of( + "1*22*1", + "12*322", + " 123*2", + "112*4*", + "1*22*2", + "111111" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("multiple adjacent flowers") + public void testMultipleAdjacentFlowers() { + List inputBoard = List.of( + " ** " + ); + + List expectedNumberedBoard = List.of( + "1**1" + ); + + List actualNumberedBoard = new FlowerFieldBoard(inputBoard).withNumbers(); + + assertThat(actualNumberedBoard).isEqualTo(expectedNumberedBoard); + } +} 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/food-chain/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/food-chain/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/food-chain/gradlew b/exercises/practice/food-chain/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/food-chain/gradlew +++ b/exercises/practice/food-chain/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/food-chain/gradlew.bat b/exercises/practice/food-chain/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/food-chain/gradlew.bat +++ b/exercises/practice/food-chain/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.meta/config.json b/exercises/practice/forth/.meta/config.json index 85016dbdc..65de8e284 100644 --- a/exercises/practice/forth/.meta/config.json +++ b/exercises/practice/forth/.meta/config.json @@ -6,6 +6,7 @@ "aadityakulkarni", "FridaTveit", "hgvanpariya", + "jagdish-15", "jmrunkle", "kytrinyx", "lemoncurry", diff --git a/exercises/practice/forth/.meta/tests.toml b/exercises/practice/forth/.meta/tests.toml index c9c1d6377..d1e146a1e 100644 --- a/exercises/practice/forth/.meta/tests.toml +++ b/exercises/practice/forth/.meta/tests.toml @@ -24,6 +24,9 @@ description = "addition -> errors if there is nothing on the stack" [06efb9a4-817a-435e-b509-06166993c1b8] description = "addition -> errors if there is only one value on the stack" +[1e07a098-c5fa-4c66-97b2-3c81205dbc2f] +description = "addition -> more than two values on the stack" + [09687c99-7bbc-44af-8526-e402f997ccbf] description = "subtraction -> can subtract two numbers" @@ -33,6 +36,9 @@ description = "subtraction -> errors if there is nothing on the stack" [b3cee1b2-9159-418a-b00d-a1bb3765c23b] description = "subtraction -> errors if there is only one value on the stack" +[2c8cc5ed-da97-4cb1-8b98-fa7b526644f4] +description = "subtraction -> more than two values on the stack" + [5df0ceb5-922e-401f-974d-8287427dbf21] description = "multiplication -> can multiply two numbers" @@ -42,6 +48,9 @@ description = "multiplication -> errors if there is nothing on the stack" [8ba4b432-9f94-41e0-8fae-3b3712bd51b3] description = "multiplication -> errors if there is only one value on the stack" +[5cd085b5-deb1-43cc-9c17-6b1c38bc9970] +description = "multiplication -> more than two values on the stack" + [e74c2204-b057-4cff-9aa9-31c7c97a93f5] description = "division -> can divide two numbers" @@ -57,12 +66,21 @@ description = "division -> errors if there is nothing on the stack" [d5547f43-c2ff-4d5c-9cb0-2a4f6684c20d] description = "division -> errors if there is only one value on the stack" +[f224f3e0-b6b6-4864-81de-9769ecefa03f] +description = "division -> more than two values on the stack" + [ee28d729-6692-4a30-b9be-0d830c52a68c] description = "combined arithmetic -> addition and subtraction" [40b197da-fa4b-4aca-a50b-f000d19422c1] description = "combined arithmetic -> multiplication and division" +[f749b540-53aa-458e-87ec-a70797eddbcb] +description = "combined arithmetic -> multiplication and addition" + +[c8e5a4c2-f9bf-4805-9a35-3c3314e4989a] +description = "combined arithmetic -> addition and multiplication" + [c5758235-6eef-4bf6-ab62-c878e50b9957] description = "dup -> copies a value on the stack" 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/forth/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/forth/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/forth/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/forth/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/forth/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/forth/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/forth/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/forth/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/forth/gradlew b/exercises/practice/forth/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/forth/gradlew +++ b/exercises/practice/forth/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/forth/gradlew.bat b/exercises/practice/forth/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/forth/gradlew.bat +++ b/exercises/practice/forth/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/forth/src/test/java/ForthEvaluatorTest.java b/exercises/practice/forth/src/test/java/ForthEvaluatorTest.java index 9a43e11bf..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,15 @@ 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); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("can subtract two numbers") public void testTwoNumbersCanBeSubtracted() { assertThat(forthEvaluator.evaluateProgram(Collections.singletonList("3 4 -"))) .containsExactly(-1); @@ -57,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("-"))) @@ -66,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 -"))) @@ -74,12 +91,22 @@ 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); + } + + @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("*"))) @@ -88,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 *"))) @@ -96,18 +124,29 @@ 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); + } + + @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 /"))) @@ -116,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("/"))) @@ -124,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 /"))) @@ -132,30 +173,57 @@ 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); + } + + @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"))) @@ -164,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"))) @@ -184,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); @@ -197,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"))) @@ -205,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"))) @@ -213,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); @@ -220,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); @@ -227,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"))) @@ -235,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"))) @@ -243,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); @@ -250,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); @@ -257,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); @@ -264,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); @@ -271,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); @@ -278,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); @@ -285,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); @@ -292,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 ;"))) @@ -300,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 ;"))) @@ -308,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"))) @@ -316,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); @@ -323,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); @@ -330,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); @@ -337,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); @@ -344,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); @@ -351,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); @@ -358,6 +453,7 @@ public void testDefinitionsAreCaseInsensitive() { @Disabled @Test + @DisplayName("only defines locally") public void testDefinitionsAreOnlyDefinedLocally() { ForthEvaluator firstInstance = new ForthEvaluator(); ForthEvaluator secondInstance = new ForthEvaluator(); 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/game-of-life/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/game-of-life/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/game-of-life/gradlew b/exercises/practice/game-of-life/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/game-of-life/gradlew +++ b/exercises/practice/game-of-life/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/game-of-life/gradlew.bat b/exercises/practice/game-of-life/gradlew.bat index 93e3f59f1..c4bdd3ab8 100644 --- a/exercises/practice/game-of-life/gradlew.bat +++ b/exercises/practice/game-of-life/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ 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. +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 @@ -57,22 +59,21 @@ 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. +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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/gigasecond/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/gigasecond/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/gigasecond/gradlew b/exercises/practice/gigasecond/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/gigasecond/gradlew +++ b/exercises/practice/gigasecond/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/gigasecond/gradlew.bat b/exercises/practice/gigasecond/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/gigasecond/gradlew.bat +++ b/exercises/practice/gigasecond/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/go-counting/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/go-counting/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/go-counting/gradlew b/exercises/practice/go-counting/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/go-counting/gradlew +++ b/exercises/practice/go-counting/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/go-counting/gradlew.bat b/exercises/practice/go-counting/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/go-counting/gradlew.bat +++ b/exercises/practice/go-counting/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.docs/instructions.md b/exercises/practice/grade-school/.docs/instructions.md index 9a63e398d..3cb1b5d5f 100644 --- a/exercises/practice/grade-school/.docs/instructions.md +++ b/exercises/practice/grade-school/.docs/instructions.md @@ -1,21 +1,21 @@ # Instructions -Given students' names along with the grade that they are in, create a roster for the school. +Given students' names along with the grade they are in, create a roster for the school. In the end, you should be able to: -- Add a student's name to the roster for a grade +- Add a student's name to the roster for a grade: - "Add Jim to grade 2." - "OK." -- Get a list of all students enrolled in a grade +- Get a list of all students enrolled in a grade: - "Which students are in grade 2?" - - "We've only got Jim just now." + - "We've only got Jim right now." - Get a sorted list of all students in all grades. - Grades should sort as 1, 2, 3, etc., and students within a grade should be sorted alphabetically by name. - - "Who all is enrolled in school right now?" + Grades should be sorted as 1, 2, 3, etc., and students within a grade should be sorted alphabetically by name. + - "Who is enrolled in school right now?" - "Let me think. - We have Anna, Barb, and Charlie in grade 1, Alex, Peter, and Zoe in grade 2 and Jim in grade 5. - So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe and Jim" + We have Anna, Barb, and Charlie in grade 1, Alex, Peter, and Zoe in grade 2, and Jim in grade 5. + So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe, and Jim." -Note that all our students only have one name (It's a small town, what do you want?) and each student cannot be added more than once to a grade or the roster. -In fact, when a test attempts to add the same student more than once, your implementation should indicate that this is incorrect. +Note that all our students only have one name (it's a small town, what do you want?), and each student cannot be added more than once to a grade or the roster. +If a test attempts to add the same student more than once, your implementation should indicate that this is incorrect. 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/grade-school/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/grade-school/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/grade-school/gradlew b/exercises/practice/grade-school/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/grade-school/gradlew +++ b/exercises/practice/grade-school/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/grade-school/gradlew.bat b/exercises/practice/grade-school/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/grade-school/gradlew.bat +++ b/exercises/practice/grade-school/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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/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/grains/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/grains/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/grains/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/grains/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/grains/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/grains/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/grains/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/grains/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/grains/gradlew b/exercises/practice/grains/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/grains/gradlew +++ b/exercises/practice/grains/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/grains/gradlew.bat b/exercises/practice/grains/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/grains/gradlew.bat +++ b/exercises/practice/grains/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/grep/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/grep/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/grep/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/grep/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/grep/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/grep/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/grep/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/grep/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/grep/gradlew b/exercises/practice/grep/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/grep/gradlew +++ b/exercises/practice/grep/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/grep/gradlew.bat b/exercises/practice/grep/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/grep/gradlew.bat +++ b/exercises/practice/grep/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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" diff --git a/exercises/practice/hamming/.docs/instructions.md b/exercises/practice/hamming/.docs/instructions.md index 020fdd02d..8f47a179e 100644 --- a/exercises/practice/hamming/.docs/instructions.md +++ b/exercises/practice/hamming/.docs/instructions.md @@ -1,26 +1,15 @@ # Instructions -Calculate the Hamming Distance between two DNA strands. +Calculate the Hamming distance between two DNA strands. -Your body is made up of cells that contain DNA. -Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. -In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! - -When cells divide, their DNA replicates too. -Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. -If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. -This is known as the "Hamming Distance". - -We read DNA using the letters C,A,G and T. +We read DNA using the letters C, A, G and T. Two strands might look like this: GAGCCTACTAACGGGAT CATCGTAATGACGGCCT ^ ^ ^ ^ ^ ^^ -They have 7 differences, and therefore the Hamming Distance is 7. - -The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with :) +They have 7 differences, and therefore the Hamming distance is 7. ## Implementation notes diff --git a/exercises/practice/hamming/.docs/introduction.md b/exercises/practice/hamming/.docs/introduction.md new file mode 100644 index 000000000..8419bf479 --- /dev/null +++ b/exercises/practice/hamming/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +Your body is made up of cells that contain DNA. +Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. +In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! + +When cells divide, their DNA replicates too. +Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. +If we compare two strands of DNA and count the differences between them, we can see how many mistakes occurred. +This is known as the "Hamming distance". + +The Hamming distance is useful in many areas of science, not just biology, so it's a nice phrase to be familiar with :) diff --git a/exercises/practice/hamming/.meta/config.json b/exercises/practice/hamming/.meta/config.json index 5e2dba3ae..acf067955 100644 --- a/exercises/practice/hamming/.meta/config.json +++ b/exercises/practice/hamming/.meta/config.json @@ -43,7 +43,7 @@ "build.gradle" ] }, - "blurb": "Calculate the Hamming difference between two DNA strands.", + "blurb": "Calculate the Hamming distance between two DNA strands.", "source": "The Calculating Point Mutations problem at Rosalind", "source_url": "https://rosalind.info/problems/hamm/" } 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/hamming/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/hamming/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/hamming/gradlew b/exercises/practice/hamming/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/hamming/gradlew +++ b/exercises/practice/hamming/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/hamming/gradlew.bat b/exercises/practice/hamming/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/hamming/gradlew.bat +++ b/exercises/practice/hamming/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/hangman/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/hangman/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/hangman/gradlew b/exercises/practice/hangman/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/hangman/gradlew +++ b/exercises/practice/hangman/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/hangman/gradlew.bat b/exercises/practice/hangman/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/hangman/gradlew.bat +++ b/exercises/practice/hangman/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/hangman/src/test/java/HangmanTest.java b/exercises/practice/hangman/src/test/java/HangmanTest.java index df60dbee9..d4bbe13da 100644 --- a/exercises/practice/hangman/src/test/java/HangmanTest.java +++ b/exercises/practice/hangman/src/test/java/HangmanTest.java @@ -1,8 +1,10 @@ import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.disposables.Disposable; + 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.ArrayList; @@ -22,6 +24,7 @@ public void init() { } @Test + @DisplayName("Initial game state is set correctly") public void initialization() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -39,6 +42,7 @@ public void initialization() { @Disabled("Remove to run test") @Test + @DisplayName("First correct guess updates discovered and guess lists") public void firstGuess() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -54,6 +58,7 @@ public void firstGuess() { @Disabled("Remove to run test") @Test + @DisplayName("First incorrect guess registers a miss and adds a part") public void firstMiss() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -69,6 +74,7 @@ public void firstMiss() { @Disabled("Remove to run test") @Test + @DisplayName("Game in progress accumulates guesses, misses and parts correctly") public void gameInProgress() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -84,6 +90,7 @@ public void gameInProgress() { @Disabled("Remove to run test") @Test + @DisplayName("Winning the game results in WIN status") public void wonGame() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -97,6 +104,7 @@ public void wonGame() { @Disabled("Remove to run test") @Test + @DisplayName("Losing the game results in LOSS status") public void lostGame() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -118,6 +126,7 @@ public void lostGame() { @Disabled("Remove to run test") @Test + @DisplayName("Handles consecutive games correctly with ordered emissions") public void consecutiveGames() { // This test setup is more complex because we have to order the emission of values in the // different observers. @@ -189,6 +198,7 @@ Observable createLetterObservable(ObservableEmitter[] emitters, Runnable emit) { @Disabled("Remove to run test") @Test + @DisplayName("Cannot play the same guess twice") public void cannotPlayAGuessTwice() { Observable result = hangman.play( Observable.fromArray("secret"), @@ -201,6 +211,7 @@ public void cannotPlayAGuessTwice() { @Disabled("Remove to run test") @Test + @DisplayName("Cannot play the same miss twice") public void cannotPlayAMissTwice() { Observable result = hangman.play( Observable.fromArray("secret"), 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/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/hello-world/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/hello-world/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/hello-world/gradlew b/exercises/practice/hello-world/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/hello-world/gradlew +++ b/exercises/practice/hello-world/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/hello-world/gradlew.bat b/exercises/practice/hello-world/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/hello-world/gradlew.bat +++ b/exercises/practice/hello-world/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/high-scores/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/high-scores/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/high-scores/gradlew b/exercises/practice/high-scores/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/high-scores/gradlew +++ b/exercises/practice/high-scores/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/high-scores/gradlew.bat b/exercises/practice/high-scores/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/high-scores/gradlew.bat +++ b/exercises/practice/high-scores/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/house/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/house/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/house/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/house/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/house/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/house/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/house/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/house/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/house/gradlew b/exercises/practice/house/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/house/gradlew +++ b/exercises/practice/house/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/house/gradlew.bat b/exercises/practice/house/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/house/gradlew.bat +++ b/exercises/practice/house/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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" + 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..f0bc8ae9d --- /dev/null +++ b/exercises/practice/intergalactic-transmission/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "jagdish-15" + ], + "files": { + "solution": [ + "src/main/java/IntergalacticTransmission.java" + ], + "test": [ + "src/test/java/IntergalacticTransmissionTest.java" + ], + "example": [ + ".meta/src/reference/java/IntergalacticTransmission.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/intergalactic-transmission/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/intergalactic-transmission/gradlew b/exercises/practice/intergalactic-transmission/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/intergalactic-transmission/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/intergalactic-transmission/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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..9c26e13e5 --- /dev/null +++ b/exercises/practice/intergalactic-transmission/src/test/java/IntergalacticTransmissionTest.java @@ -0,0 +1,277 @@ +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class IntergalacticTransmissionTest { + + @Test + @DisplayName("calculate transmit sequences") + public void calculateTransmitSequencesEmptyMessage() { + List input = List.of(); + List expected = List.of(); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.getTransmitSequence(input)) + .isEqualTo(expected); + } + + @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, + 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 + @DisplayName("empty message") + public void decodeReceivedMessagesEmptyMessage() { + List input = List.of(); + List expected = List.of(); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("zero message") + 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 + @DisplayName("0x0300 is decoded to 0x02") + 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 + @DisplayName("0x0581 is decoded to 0x05") + 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 + @DisplayName("0x2881 is decoded to 0x29") + 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 + @DisplayName("first byte has wrong parity") + public void decodeFirstByteWrongParity() { + List input = List.of(0x07, 0x00); + assertThrows(IllegalArgumentException.class, () + -> IntergalacticTransmission.decodeSequence(input)); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("second byte has wrong parity") + public void decodeSecondByteWrongParity() { + List input = List.of(0x03, 0x68); + assertThrows(IllegalArgumentException.class, () + -> IntergalacticTransmission.decodeSequence(input)); + } + + @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); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @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); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @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, () + -> IntergalacticTransmission.decodeSequence(input)); + } + + @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); + assertThat(IntergalacticTransmission.decodeSequence(input)) + .isEqualTo(expected); + } + + @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, + 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 + @DisplayName("wrong parity on 16th byte") + 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/practice/isbn-verifier/.meta/tests.toml b/exercises/practice/isbn-verifier/.meta/tests.toml index 6d5a84599..17e18d47a 100644 --- a/exercises/practice/isbn-verifier/.meta/tests.toml +++ b/exercises/practice/isbn-verifier/.meta/tests.toml @@ -30,6 +30,12 @@ description = "invalid character in isbn is not treated as zero" [28025280-2c39-4092-9719-f3234b89c627] description = "X is only valid as a check digit" +[8005b57f-f194-44ee-88d2-a77ac4142591] +description = "only one check digit is allowed" + +[fdb14c99-4cf8-43c5-b06d-eb1638eff343] +description = "X is not substituted by the value 10" + [f6294e61-7e79-46b3-977b-f48789a4945b] description = "valid isbn without separating dashes" 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/isbn-verifier/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/isbn-verifier/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/isbn-verifier/gradlew b/exercises/practice/isbn-verifier/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/isbn-verifier/gradlew +++ b/exercises/practice/isbn-verifier/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/isbn-verifier/gradlew.bat b/exercises/practice/isbn-verifier/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/isbn-verifier/gradlew.bat +++ b/exercises/practice/isbn-verifier/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/isbn-verifier/src/test/java/IsbnVerifierTest.java b/exercises/practice/isbn-verifier/src/test/java/IsbnVerifierTest.java index 65483a772..2848b63c5 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,154 @@ 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("only one check digit is allowed") + public void onlyOneCheckDigitIsAllowed() { + assertThat(isbnVerifier.isValid("3-598-21508-96")).isFalse(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("X is not substituted by the value 10") + public void xIsNotSubstitutedByTheValue10() { + assertThat(isbnVerifier.isValid("3-598-2X507-5")).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/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/isogram/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/isogram/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/isogram/gradlew b/exercises/practice/isogram/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/isogram/gradlew +++ b/exercises/practice/isogram/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/isogram/gradlew.bat b/exercises/practice/isogram/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/isogram/gradlew.bat +++ b/exercises/practice/isogram/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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(); } diff --git a/exercises/practice/killer-sudoku-helper/.docs/instructions.md b/exercises/practice/killer-sudoku-helper/.docs/instructions.md index fdafdca8f..9f5cb1368 100644 --- a/exercises/practice/killer-sudoku-helper/.docs/instructions.md +++ b/exercises/practice/killer-sudoku-helper/.docs/instructions.md @@ -74,7 +74,7 @@ You can also find Killer Sudokus in varying difficulty in numerous newspapers, a ## Credit -The screenshots above have been generated using [F-Puzzles.com](https://www.f-puzzles.com/), a Puzzle Setting Tool by Eric Fox. +The screenshots above have been generated using F-Puzzles.com, a Puzzle Setting Tool by Eric Fox. [sudoku-rules]: https://masteringsudoku.com/sudoku-rules-beginners/ [killer-guide]: https://masteringsudoku.com/killer-sudoku/ 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/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/killer-sudoku-helper/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/killer-sudoku-helper/gradlew b/exercises/practice/killer-sudoku-helper/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/killer-sudoku-helper/gradlew +++ b/exercises/practice/killer-sudoku-helper/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/killer-sudoku-helper/gradlew.bat b/exercises/practice/killer-sudoku-helper/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/killer-sudoku-helper/gradlew.bat +++ b/exercises/practice/killer-sudoku-helper/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/kindergarten-garden/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/kindergarten-garden/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/kindergarten-garden/gradlew b/exercises/practice/kindergarten-garden/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/kindergarten-garden/gradlew +++ b/exercises/practice/kindergarten-garden/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/kindergarten-garden/gradlew.bat b/exercises/practice/kindergarten-garden/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/kindergarten-garden/gradlew.bat +++ b/exercises/practice/kindergarten-garden/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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 diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md index 3411db988..0ebf7914c 100644 --- a/exercises/practice/knapsack/.docs/instructions.md +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -1,11 +1,11 @@ # Instructions -Your task is to determine which items to take so that the total value of his selection is maximized, taking into account the knapsack's carrying capacity. +Your task is to determine which items to take so that the total value of her selection is maximized, taking into account the knapsack's carrying capacity. Items will be represented as a list of items. Each item will have a weight and value. All values given will be strictly positive. -Bob can take only one of each item. +Lhakpa can take only one of each item. For example: @@ -21,5 +21,5 @@ Knapsack Maximum Weight: 10 ``` For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on. -In this example, Bob should take the second and fourth item to maximize his value, which, in this case, is 90. -He cannot get more than 90 as his knapsack has a weight limit of 10. +In this example, Lhakpa should take the second and fourth item to maximize her value, which, in this case, is 90. +She cannot get more than 90 as her knapsack has a weight limit of 10. diff --git a/exercises/practice/knapsack/.docs/introduction.md b/exercises/practice/knapsack/.docs/introduction.md index 9b2bed8b4..9ac9df596 100644 --- a/exercises/practice/knapsack/.docs/introduction.md +++ b/exercises/practice/knapsack/.docs/introduction.md @@ -1,8 +1,10 @@ # Introduction -Bob is a thief. -After months of careful planning, he finally manages to crack the security systems of a fancy store. +Lhakpa is a [Sherpa][sherpa] mountain guide and porter. +After months of careful planning, the expedition Lhakpa works for is about to leave. +She will be paid the value she carried to the base camp. -In front of him are many items, each with a value and weight. -Bob would gladly take all of the items, but his knapsack can only hold so much weight. -Bob has to carefully consider which items to take so that the total value of his selection is maximized. +In front of her are many items, each with a value and weight. +Lhakpa would gladly take all of the items, but her knapsack can only hold so much weight. + +[sherpa]: https://en.wikipedia.org/wiki/Sherpa_people#Mountaineering 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/knapsack/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/knapsack/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/knapsack/gradlew b/exercises/practice/knapsack/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/knapsack/gradlew +++ b/exercises/practice/knapsack/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/knapsack/gradlew.bat b/exercises/practice/knapsack/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/knapsack/gradlew.bat +++ b/exercises/practice/knapsack/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.meta/config.json b/exercises/practice/largest-series-product/.meta/config.json index b6a57ee6c..c3ba23a98 100644 --- a/exercises/practice/largest-series-product/.meta/config.json +++ b/exercises/practice/largest-series-product/.meta/config.json @@ -22,6 +22,7 @@ "Smarticles101", "sonapraneeth-a", "sshine", + "Xinri", "Zaldrick" ], "files": { diff --git a/exercises/practice/largest-series-product/.meta/tests.toml b/exercises/practice/largest-series-product/.meta/tests.toml index 883169259..982f517cc 100644 --- a/exercises/practice/largest-series-product/.meta/tests.toml +++ b/exercises/practice/largest-series-product/.meta/tests.toml @@ -38,6 +38,11 @@ description = "reports zero if all spans include zero" [5d81aaf7-4f67-4125-bf33-11493cc7eab7] description = "rejects span longer than string length" +include = false + +[0ae1ce53-d9ba-41bb-827f-2fceb64f058b] +description = "rejects span longer than string length" +reimplements = "5d81aaf7-4f67-4125-bf33-11493cc7eab7" [06bc8b90-0c51-4c54-ac22-3ec3893a079e] description = "reports 1 for empty string and empty product (0 span)" @@ -49,6 +54,11 @@ include = false [6d96c691-4374-4404-80ee-2ea8f3613dd4] description = "rejects empty string and nonzero span" +include = false + +[6cf66098-a6af-4223-aab1-26aeeefc7402] +description = "rejects empty string and nonzero span" +reimplements = "6d96c691-4374-4404-80ee-2ea8f3613dd4" [7a38f2d6-3c35-45f6-8d6f-12e6e32d4d74] description = "rejects invalid character in digits" 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/largest-series-product/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/largest-series-product/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/largest-series-product/gradlew b/exercises/practice/largest-series-product/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/largest-series-product/gradlew +++ b/exercises/practice/largest-series-product/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/largest-series-product/gradlew.bat b/exercises/practice/largest-series-product/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/largest-series-product/gradlew.bat +++ b/exercises/practice/largest-series-product/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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..417878d1e 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,7 +129,8 @@ public void testSeriesLengthLongerThanLengthOfStringToTestIsRejected() { @Disabled("Remove to run test") @Test - public void testEmptyStringToSearchAndSeriesOfNonZeroLengthIsRejected() { + @DisplayName("rejects empty string and nonzero span") + public void testEmptyStringAndNonZeroSpanIsRejected() { LargestSeriesProductCalculator calculator = new LargestSeriesProductCalculator(""); assertThatExceptionOfType(IllegalArgumentException.class) @@ -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/.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/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 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/leap/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/leap/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/leap/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/leap/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/leap/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/leap/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/leap/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/leap/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/leap/gradlew b/exercises/practice/leap/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/leap/gradlew +++ b/exercises/practice/leap/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/leap/gradlew.bat b/exercises/practice/leap/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/leap/gradlew.bat +++ b/exercises/practice/leap/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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(); } 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/ledger/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/ledger/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/ledger/gradlew b/exercises/practice/ledger/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/ledger/gradlew +++ b/exercises/practice/ledger/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/ledger/gradlew.bat b/exercises/practice/ledger/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/ledger/gradlew.bat +++ b/exercises/practice/ledger/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/ledger/src/test/java/LedgerTest.java b/exercises/practice/ledger/src/test/java/LedgerTest.java index eb7cbfddb..15c022ee4 100644 --- a/exercises/practice/ledger/src/test/java/LedgerTest.java +++ b/exercises/practice/ledger/src/test/java/LedgerTest.java @@ -1,7 +1,8 @@ -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -23,6 +24,7 @@ public void setUp() throws Exception { @Disabled("Remove to run test") @Test + @DisplayName("empty ledger") public void emptyLedgerUS() { var entries = new Ledger.LedgerEntry[] {}; @@ -34,6 +36,7 @@ public void emptyLedgerUS() { @Disabled("Remove to run test") @Test + @DisplayName("one entry") public void oneEntry() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-01-01", "Buy present", -1000) @@ -48,6 +51,7 @@ public void oneEntry() { @Disabled("Remove to run test") @Test + @DisplayName("credit and debit") public void creditAndDebit() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-01-02", "Get present", 1000), @@ -65,6 +69,7 @@ public void creditAndDebit() { @Disabled("Remove to run test") @Test + @DisplayName("multiple entries on same date ordered by description") public void multipleEntriesOnSameDateOrderedByDescription() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-01-01", "Get present", 1000), @@ -82,6 +87,7 @@ public void multipleEntriesOnSameDateOrderedByDescription() { @Disabled("Remove to run test") @Test + @DisplayName("final order tie breaker is change") public void finalOrderTieBreakerIsChange() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-01-01", "Something", 0), @@ -101,6 +107,7 @@ public void finalOrderTieBreakerIsChange() { @Disabled("Remove to run test") @Test + @DisplayName("overlong description is truncated") public void overlongDescriptions() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-01-01", "Freude schoner Gotterfunken", -123456) @@ -116,6 +123,7 @@ public void overlongDescriptions() { @Disabled("Remove to run test") @Test + @DisplayName("euros") public void euros() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-01-01", "Buy present", -1000) @@ -131,6 +139,7 @@ public void euros() { @Disabled("Remove to run test") @Test + @DisplayName("Dutch locale") public void dutchLocale() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-03-12", "Buy present", 123456) @@ -146,6 +155,7 @@ public void dutchLocale() { @Disabled("Remove to run test") @Test + @DisplayName("Dutch locale and euros") public void dutchLocaleAndEuros() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-03-12", "Buy present", 123456) @@ -161,6 +171,7 @@ public void dutchLocaleAndEuros() { @Disabled("Remove to run test") @Test + @DisplayName("Dutch negative number with 3 digits before decimal point") public void dutchNegativeNumberWith3DigitsBeforeDecimalPoint() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-03-12", "Buy present", -12345) @@ -176,6 +187,7 @@ public void dutchNegativeNumberWith3DigitsBeforeDecimalPoint() { @Disabled("Remove to run test") @Test + @DisplayName("American negative number with 3 digits before decimal point") public void americanNegativeNumberWith3DigitsBeforeDecimalPoint() { var entries = new Ledger.LedgerEntry[] { ledger.createLedgerEntry("2015-03-12", "Buy present", -12345) 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/linked-list/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/linked-list/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/linked-list/gradlew b/exercises/practice/linked-list/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/linked-list/gradlew +++ b/exercises/practice/linked-list/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/linked-list/gradlew.bat b/exercises/practice/linked-list/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/linked-list/gradlew.bat +++ b/exercises/practice/linked-list/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/linked-list/src/test/java/DoublyLinkedListTest.java b/exercises/practice/linked-list/src/test/java/DoublyLinkedListTest.java index b36e5b988..365d92088 100644 --- a/exercises/practice/linked-list/src/test/java/DoublyLinkedListTest.java +++ b/exercises/practice/linked-list/src/test/java/DoublyLinkedListTest.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 DoublyLinkedListTest { @Test + @DisplayName("pop gets element from the list") public void popGetsElementFromTheList() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -16,6 +18,7 @@ public void popGetsElementFromTheList() { @Disabled("Remove to run test") @Test + @DisplayName("push/pop respectively add/remove at the end of the list") public void pushAndPopRespectivelyAddsAndRemovesAtEndOfList() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -28,6 +31,7 @@ public void pushAndPopRespectivelyAddsAndRemovesAtEndOfList() { @Disabled("Remove to run test") @Test + @DisplayName("shift gets an element from the list") public void shiftGetsAnElementFromTheList() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -38,6 +42,7 @@ public void shiftGetsAnElementFromTheList() { @Disabled("Remove to run test") @Test + @DisplayName("shift gets first element from the list") public void shiftGetsFirstElementFromTheList() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -50,6 +55,7 @@ public void shiftGetsFirstElementFromTheList() { @Disabled("Remove to run test") @Test + @DisplayName("unshift adds element at start of the list") public void unshiftAddsElementAtStartOfTheList() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -62,6 +68,7 @@ public void unshiftAddsElementAtStartOfTheList() { @Disabled("Remove to run test") @Test + @DisplayName("pop push shift unshift can be used in any order") public void popPushShiftUnshiftCanBeUsedInAnyOrder() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -84,6 +91,7 @@ public void popPushShiftUnshiftCanBeUsedInAnyOrder() { @Disabled("Remove to run test") @Test + @DisplayName("popping to empty doesn't break the list") public void poppingToEmptyDoesNotBreakTheList() { DoublyLinkedList list = new DoublyLinkedList<>(); @@ -98,6 +106,7 @@ public void poppingToEmptyDoesNotBreakTheList() { @Disabled("Remove to run test") @Test + @DisplayName("shifting to empty doesn't break the list") public void shiftingToEmptyDoesNotBreakTheList() { DoublyLinkedList list = new DoublyLinkedList<>(); 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/list-ops/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/list-ops/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/list-ops/gradlew b/exercises/practice/list-ops/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/list-ops/gradlew +++ b/exercises/practice/list-ops/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/list-ops/gradlew.bat b/exercises/practice/list-ops/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/list-ops/gradlew.bat +++ b/exercises/practice/list-ops/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/list-ops/src/test/java/ListOpsTest.java b/exercises/practice/list-ops/src/test/java/ListOpsTest.java index f1eb87051..dc44c61da 100644 --- a/exercises/practice/list-ops/src/test/java/ListOpsTest.java +++ b/exercises/practice/list-ops/src/test/java/ListOpsTest.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; @@ -8,12 +9,14 @@ public class ListOpsTest { @Test + @DisplayName("empty lists") public void testAppendingEmptyLists() { assertThat(ListOps.append(List.of(), List.of())).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("list to empty list") public void testAppendingListToEmptyList() { assertThat(ListOps.append(List.of(), List.of('1', '2', '3', '4'))) .containsExactly('1', '2', '3', '4'); @@ -21,6 +24,7 @@ public void testAppendingListToEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("empty list to list") public void testAppendingEmptyListToList() { assertThat(ListOps.append(List.of('1', '2', '3', '4'), List.of())) .containsExactly('1', '2', '3', '4'); @@ -28,6 +32,7 @@ public void testAppendingEmptyListToList() { @Disabled("Remove to run test") @Test + @DisplayName("non-empty lists") public void testAppendingNonEmptyLists() { assertThat(ListOps.append(List.of("1", "2"), List.of("2", "3", "4", "5"))) .containsExactly("1", "2", "2", "3", "4", "5"); @@ -35,12 +40,14 @@ public void testAppendingNonEmptyLists() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testConcatEmptyList() { assertThat(ListOps.concat(List.of())).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("list of lists") public void testConcatListOfLists() { List> listOfLists = List.of( List.of('1', '2'), @@ -54,6 +61,7 @@ public void testConcatListOfLists() { @Disabled("Remove to run test") @Test + @DisplayName("list of nested lists") public void testConcatListOfNestedLists() { List>> listOfNestedLists = List.of( List.of( @@ -82,6 +90,7 @@ public void testConcatListOfNestedLists() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testFilteringEmptyList() { assertThat(ListOps.filter(List.of(), integer -> integer % 2 == 1)) .isEmpty(); @@ -89,6 +98,7 @@ public void testFilteringEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("non-empty list") public void testFilteringNonEmptyList() { assertThat(ListOps.filter(List.of(1, 2, 3, 5), integer -> integer % 2 == 1)) .containsExactly(1, 3, 5); @@ -96,24 +106,28 @@ public void testFilteringNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testSizeOfEmptyList() { assertThat(ListOps.size(List.of())).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName("non-empty list") public void testSizeOfNonEmptyList() { assertThat(ListOps.size(List.of("one", "two", "three", "four"))).isEqualTo(4); } @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testTransformingEmptyList() { assertThat(ListOps.map(List.of(), integer -> integer + 1)).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("non-empty list") public void testTransformingNonEmptyList() { assertThat(ListOps.map(List.of(1, 3, 5, 7), integer -> integer + 1)) .containsExactly(2, 4, 6, 8); @@ -121,6 +135,7 @@ public void testTransformingNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testFoldLeftEmptyList() { assertThat( ListOps.foldLeft( @@ -132,6 +147,7 @@ public void testFoldLeftEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("direction independent function applied to non-empty list") public void testFoldLeftDirectionIndependentFunctionAppliedToNonEmptyList() { assertThat( ListOps.foldLeft( @@ -143,6 +159,7 @@ public void testFoldLeftDirectionIndependentFunctionAppliedToNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("direction dependent function applied to non-empty list") public void testFoldLeftDirectionDependentFunctionAppliedToNonEmptyList() { assertThat( ListOps.foldLeft( @@ -154,6 +171,7 @@ public void testFoldLeftDirectionDependentFunctionAppliedToNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testFoldRightEmptyList() { assertThat( ListOps.foldRight( @@ -165,6 +183,7 @@ public void testFoldRightEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("direction independent function applied to non-empty list") public void testFoldRightDirectionIndependentFunctionAppliedToNonEmptyList() { assertThat( ListOps.foldRight( @@ -176,6 +195,7 @@ public void testFoldRightDirectionIndependentFunctionAppliedToNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("direction dependent function applied to non-empty list") public void testFoldRightDirectionDependentFunctionAppliedToNonEmptyList() { assertThat( ListOps.foldRight( @@ -187,12 +207,14 @@ public void testFoldRightDirectionDependentFunctionAppliedToNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("empty list") public void testReversingEmptyList() { assertThat(ListOps.reverse(List.of())).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("non-empty list") public void testReversingNonEmptyList() { assertThat(ListOps.reverse(List.of('1', '3', '5', '7'))) .containsExactly('7', '5', '3', '1'); @@ -200,6 +222,7 @@ public void testReversingNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("list of lists is not flattened") public void testReversingListOfListIsNotFlattened() { List> listOfLists = List.of( List.of('1', '2'), diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md index 8cbe791fc..7702c6bbb 100644 --- a/exercises/practice/luhn/.docs/instructions.md +++ b/exercises/practice/luhn/.docs/instructions.md @@ -1,64 +1,68 @@ # Instructions -Given a number determine whether or not it is valid per the Luhn formula. +Determine whether a number is valid according to the [Luhn formula][luhn]. -The [Luhn algorithm][luhn] is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers and Canadian Social Insurance Numbers. +The number will be provided as a string. -The task is to check if a given string is valid. - -## Validating a Number +## Validating a number 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 -4_3_ 3_9_ 0_4_ 6_6_ +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 478`. -### 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 new file mode 100644 index 000000000..dee48006e --- /dev/null +++ b/exercises/practice/luhn/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +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 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 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/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/luhn/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/luhn/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/luhn/gradlew b/exercises/practice/luhn/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/luhn/gradlew +++ b/exercises/practice/luhn/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/luhn/gradlew.bat b/exercises/practice/luhn/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/luhn/gradlew.bat +++ b/exercises/practice/luhn/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/luhn/src/test/java/LuhnValidatorTest.java b/exercises/practice/luhn/src/test/java/LuhnValidatorTest.java index 12d3e5f3f..bc0c6d7dc 100644 --- a/exercises/practice/luhn/src/test/java/LuhnValidatorTest.java +++ b/exercises/practice/luhn/src/test/java/LuhnValidatorTest.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,132 +14,154 @@ public void setUp() { } @Test + @DisplayName("single digit strings can not be valid") public void testSingleDigitStringInvalid() { assertThat(luhnValidator.isValid("1")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("a single zero is invalid") public void testSingleZeroIsInvalid() { assertThat(luhnValidator.isValid("0")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("a simple valid SIN that remains valid if reversed") public void testSimpleValidSINReversedRemainsValid() { assertThat(luhnValidator.isValid("059")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("a simple valid SIN that becomes invalid if reversed") public void testSimpleValidSINReversedBecomesInvalid() { assertThat(luhnValidator.isValid("59")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("a valid Canadian SIN") public void testValidCanadianSINValid() { assertThat(luhnValidator.isValid("055 444 285")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid Canadian SIN") public void testInvalidCanadianSINInvalid() { assertThat(luhnValidator.isValid("055 444 286")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid credit card") public void testInvalidCreditCardInvalid() { assertThat(luhnValidator.isValid("8273 1232 7352 0569")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid long number with an even remainder") public void testInvalidLongNumberWithAnEvenRemainder() { assertThat(luhnValidator.isValid("1 2345 6789 1234 5678 9012")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("invalid long number with a remainder divisible by 5") public void testInvalidLongNumberWithARemainderDivisibleBy5() { assertThat(luhnValidator.isValid("1 2345 6789 1234 5678 9013")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("valid number with an even number of digits") public void testValidNumberWithAnEvenNumberOfDigits() { assertThat(luhnValidator.isValid("095 245 88")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("valid number with an odd number of spaces") public void testValidNumberWithAnOddNumberOfSpaces() { assertThat(luhnValidator.isValid("234 567 891 234")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("valid strings with a non-digit added at the end become invalid") public void testValidStringsWithANonDigitAtEndInvalid() { assertThat(luhnValidator.isValid("059a")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("valid strings with punctuation included become invalid") public void testStringContainingPunctuationInvalid() { assertThat(luhnValidator.isValid("055-444-285")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("valid strings with symbols included become invalid") public void testStringContainingSymbolsInvalid() { assertThat(luhnValidator.isValid("055# 444$ 285")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("single zero with space is invalid") public void testSingleSpaceWithZeroInvalid() { assertThat(luhnValidator.isValid(" 0")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("more than a single zero is valid") public void testMoreThanSingleZeroValid() { assertThat(luhnValidator.isValid("0000 0")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("input digit 9 is correctly converted to output digit 9") public void testDigitNineConvertedToOutputNine() { assertThat(luhnValidator.isValid("091")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("very long input is valid") public void testVeryLongInputIsValid() { assertThat(luhnValidator.isValid("9999999999 9999999999 9999999999 9999999999")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("valid luhn with an odd number of digits and non zero first digit") public void testValidLuhnWithOddNumberOfDigitsAndNonZeroFirstDigit() { assertThat(luhnValidator.isValid("109")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("using ascii value for non-doubled non-digit isn't allowed") public void testUsingASCIIValueForNonDoubledNonDigitNotAllowed() { assertThat(luhnValidator.isValid("055b 444 285")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("using ascii value for doubled non-digit isn't allowed") public void testUsingASCIIValueForDoubledNonDigitNotAllowed() { assertThat(luhnValidator.isValid(":9")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed") public void testNonNumericNonSpaceCharInMiddleWithSumDivisibleBy10IsNotAllowed() { assertThat(luhnValidator.isValid("59%59")).isFalse(); } @@ -150,7 +173,8 @@ public void testNonNumericNonSpaceCharInMiddleWithSumDivisibleBy10IsNotAllowed() */ @Disabled("Remove to run test") @Test - public void testStringContainingSymbolsInvalidJavaTrackSpecific() { + @DisplayName("string containing symbols is invalid (Java track specific)") + public void testStringContainingSymbolsIsInvalidJavaTrackSpecific() { assertThat(luhnValidator.isValid("85&")).isFalse(); } } 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/markdown/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/markdown/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/markdown/gradlew b/exercises/practice/markdown/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/markdown/gradlew +++ b/exercises/practice/markdown/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/markdown/gradlew.bat b/exercises/practice/markdown/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/markdown/gradlew.bat +++ b/exercises/practice/markdown/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/markdown/src/test/java/MarkdownTest.java b/exercises/practice/markdown/src/test/java/MarkdownTest.java index 10f0823f2..ec0f31d94 100644 --- a/exercises/practice/markdown/src/test/java/MarkdownTest.java +++ b/exercises/practice/markdown/src/test/java/MarkdownTest.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("parses normal text as a paragraph") public void normalTextAsAParagraph() { String input = "This will be a paragraph"; String expected = "

This will be a paragraph

"; @@ -23,6 +25,7 @@ public void normalTextAsAParagraph() { @Disabled("Remove to run test") @Test + @DisplayName("parsing italics") public void italics() { String input = "_This will be italic_"; String expected = "

This will be italic

"; @@ -32,6 +35,7 @@ public void italics() { @Disabled("Remove to run test") @Test + @DisplayName("parsing bold text") public void boldText() { String input = "__This will be bold__"; String expected = "

This will be bold

"; @@ -41,6 +45,7 @@ public void boldText() { @Disabled("Remove to run test") @Test + @DisplayName("mixed normal, italics and bold text") public void normalItalicsAndBoldText() { String input = "This will _be_ __mixed__"; String expected = "

This will be mixed

"; @@ -50,6 +55,7 @@ public void normalItalicsAndBoldText() { @Disabled("Remove to run test") @Test + @DisplayName("with h1 header level") public void withH1HeaderLevel() { String input = "# This will be an h1"; String expected = "

This will be an h1

"; @@ -59,6 +65,7 @@ public void withH1HeaderLevel() { @Disabled("Remove to run test") @Test + @DisplayName("with h2 header level") public void withH2HeaderLevel() { String input = "## This will be an h2"; String expected = "

This will be an h2

"; @@ -68,6 +75,7 @@ public void withH2HeaderLevel() { @Disabled("Remove to run test") @Test + @DisplayName("with h3 header level") public void withH3HeaderLevel() { String input = "### This will be an h3"; String expected = "

This will be an h3

"; @@ -77,6 +85,7 @@ public void withH3HeaderLevel() { @Disabled("Remove to run test") @Test + @DisplayName("with h4 header level") public void withH4HeaderLevel() { String input = "#### This will be an h4"; String expected = "

This will be an h4

"; @@ -86,6 +95,7 @@ public void withH4HeaderLevel() { @Disabled("Remove to run test") @Test + @DisplayName("with h5 header level") public void withH5HeaderLevel() { String input = "##### This will be an h5"; String expected = "
This will be an h5
"; @@ -95,6 +105,7 @@ public void withH5HeaderLevel() { @Disabled("Remove to run test") @Test + @DisplayName("with h6 header level") public void withH6HeaderLevel() { String input = "###### This will be an h6"; String expected = "
This will be an h6
"; @@ -104,6 +115,7 @@ public void withH6HeaderLevel() { @Disabled("Remove to run test") @Test + @DisplayName("h7 header level is a paragraph") public void h7HeaderLevelIsAParagraph() { String input = "####### This will not be an h7"; String expected = "

####### This will not be an h7

"; @@ -113,6 +125,7 @@ public void h7HeaderLevelIsAParagraph() { @Disabled("Remove to run test") @Test + @DisplayName("unordered lists") public void unorderedLists() { String input = "* Item 1\n* Item 2"; String expected = "
  • Item 1
  • Item 2
"; @@ -122,6 +135,7 @@ public void unorderedLists() { @Disabled("Remove to run test") @Test + @DisplayName("With a little bit of everything") public void aLittleBitOfEverything() { String input = "# Header!\n* __Bold Item__\n* _Italic Item_"; String expected = "

Header!

  • Bold Item
  • Italic Item
"; @@ -131,6 +145,7 @@ public void aLittleBitOfEverything() { @Disabled("Remove to run test") @Test + @DisplayName("with markdown symbols in the header text that should not be interpreted") public void markdownSymbolsInTheHeaderShouldNotBeInterpreted() { String input = "# This is a header with # and * in the text"; String expected = "

This is a header with # and * in the text

"; @@ -140,6 +155,7 @@ public void markdownSymbolsInTheHeaderShouldNotBeInterpreted() { @Disabled("Remove to run test") @Test + @DisplayName("with markdown symbols in the list item text that should not be interpreted") public void markdownSymbolsInTheListItemTextShouldNotBeInterpreted() { String input = "* Item 1 with a # in the text\n* Item 2 with * in the text"; String expected = "
  • Item 1 with a # in the text
  • Item 2 with * in the text
"; @@ -149,6 +165,7 @@ public void markdownSymbolsInTheListItemTextShouldNotBeInterpreted() { @Disabled("Remove to run test") @Test + @DisplayName("with markdown symbols in the paragraph text that should not be interpreted") public void markdownSymbolsInTheParagraphTextShouldNotBeInterpreted() { String input = "This is a paragraph with # and * in the text"; String expected = "

This is a paragraph with # and * in the text

"; @@ -158,6 +175,7 @@ public void markdownSymbolsInTheParagraphTextShouldNotBeInterpreted() { @Disabled("Remove to run test") @Test + @DisplayName("unordered lists close properly with preceding and following lines") public void markdownUnorderedListsCloseProperlyWithPrecedingAndFollowingLines() { String input = "# Start a list\n* Item 1\n* Item 2\nEnd a list"; String expected = "

Start a list

  • Item 1
  • Item 2

End a list

"; 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/matching-brackets/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/matching-brackets/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/matching-brackets/gradlew b/exercises/practice/matching-brackets/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/matching-brackets/gradlew +++ b/exercises/practice/matching-brackets/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/matching-brackets/gradlew.bat b/exercises/practice/matching-brackets/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/matching-brackets/gradlew.bat +++ b/exercises/practice/matching-brackets/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/matching-brackets/src/test/java/BracketCheckerTest.java b/exercises/practice/matching-brackets/src/test/java/BracketCheckerTest.java index f1a5b5e7e..9051b388c 100644 --- a/exercises/practice/matching-brackets/src/test/java/BracketCheckerTest.java +++ b/exercises/practice/matching-brackets/src/test/java/BracketCheckerTest.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 BracketCheckerTest { @Test + @DisplayName("paired square brackets") public void testPairedSquareBrackets() { BracketChecker bracketChecker = new BracketChecker("[]"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -13,6 +15,7 @@ public void testPairedSquareBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("empty string") public void testEmptyString() { BracketChecker bracketChecker = new BracketChecker(""); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -20,6 +23,7 @@ public void testEmptyString() { @Disabled("Remove to run test") @Test + @DisplayName("unpaired brackets") public void testUnpairedBrackets() { BracketChecker bracketChecker = new BracketChecker("[["); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -27,6 +31,7 @@ public void testUnpairedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("wrong ordered brackets") public void testWrongOrderedBrackets() { BracketChecker bracketChecker = new BracketChecker("}{"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -34,6 +39,7 @@ public void testWrongOrderedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("wrong closing bracket") public void testWrongClosingBracket() { BracketChecker bracketChecker = new BracketChecker("{]"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -41,6 +47,7 @@ public void testWrongClosingBracket() { @Disabled("Remove to run test") @Test + @DisplayName("paired with whitespace") public void testPairedWithWhitespace() { BracketChecker bracketChecker = new BracketChecker("{ }"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -48,6 +55,7 @@ public void testPairedWithWhitespace() { @Disabled("Remove to run test") @Test + @DisplayName("partially paired brackets") public void testPartiallyPairedBrackets() { BracketChecker bracketChecker = new BracketChecker("{[])"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -55,6 +63,7 @@ public void testPartiallyPairedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("simple nested brackets") public void testSimpleNestedBrackets() { BracketChecker bracketChecker = new BracketChecker("{[]}"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -62,6 +71,7 @@ public void testSimpleNestedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("several paired brackets") public void testSeveralPairedBrackets() { BracketChecker bracketChecker = new BracketChecker("{}[]"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -69,6 +79,7 @@ public void testSeveralPairedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("paired and nested brackets") public void testPairedAndNestedBrackets() { BracketChecker bracketChecker = new BracketChecker("([{}({}[])])"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -76,6 +87,7 @@ public void testPairedAndNestedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("unopened closing brackets") public void testUnopenedClosingBracket() { BracketChecker bracketChecker = new BracketChecker("{[)][]}"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -83,6 +95,7 @@ public void testUnopenedClosingBracket() { @Disabled("Remove to run test") @Test + @DisplayName("unpaired and nested brackets") public void testUnpairedAndNestedBrackets() { BracketChecker bracketChecker = new BracketChecker("([{])"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -90,6 +103,7 @@ public void testUnpairedAndNestedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("paired and wrong nested brackets") public void testPairedAndWrongNestedBrackets() { BracketChecker bracketChecker = new BracketChecker("[({]})"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -97,6 +111,7 @@ public void testPairedAndWrongNestedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("paired and wrong nested brackets but innermost are correct") public void testPairedAndWrongNestedBracketsButInnermostAreCorrect() { BracketChecker bracketChecker = new BracketChecker("[({}])"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -104,6 +119,7 @@ public void testPairedAndWrongNestedBracketsButInnermostAreCorrect() { @Disabled("Remove to run test") @Test + @DisplayName("paired and incomplete brackets") public void testPairedAndIncompleteBrackets() { BracketChecker bracketChecker = new BracketChecker("{}["); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -111,6 +127,7 @@ public void testPairedAndIncompleteBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("too many closing brackets") public void testTooManyClosingBrackets() { BracketChecker bracketChecker = new BracketChecker("[]]"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -118,13 +135,15 @@ public void testTooManyClosingBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("early unexpected brackets") public void testEarlyUnexpectedBrackets() { BracketChecker bracketChecker = new BracketChecker(")()"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); } - + @Disabled("Remove to run test") @Test + @DisplayName("early mismatched brackets") public void testEarlyMismatchedBrackets() { BracketChecker bracketChecker = new BracketChecker("{)()"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isFalse(); @@ -132,6 +151,7 @@ public void testEarlyMismatchedBrackets() { @Disabled("Remove to run test") @Test + @DisplayName("math expression") public void testMathExpression() { BracketChecker bracketChecker = new BracketChecker("(((185 + 223.85) * 15) - 543)/2"); assertThat(bracketChecker.areBracketsMatchedAndNestedCorrectly()).isTrue(); @@ -139,6 +159,7 @@ public void testMathExpression() { @Disabled("Remove to run test") @Test + @DisplayName("complex latex expression") public void testComplexLatexExpression() { BracketChecker bracketChecker = new BracketChecker( "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)"); 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/matrix/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/matrix/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/matrix/gradlew b/exercises/practice/matrix/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/matrix/gradlew +++ b/exercises/practice/matrix/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/matrix/gradlew.bat b/exercises/practice/matrix/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/matrix/gradlew.bat +++ b/exercises/practice/matrix/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/matrix/src/test/java/MatrixTest.java b/exercises/practice/matrix/src/test/java/MatrixTest.java index 3406ea8d7..5eb9ccfb4 100644 --- a/exercises/practice/matrix/src/test/java/MatrixTest.java +++ b/exercises/practice/matrix/src/test/java/MatrixTest.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 MatrixTest { @Test + @DisplayName("extract row from one number matrix") public void extractRowFromOneNumberMatrixTest() { String matrixAsString = "1"; int rowIndex = 1; @@ -18,6 +20,7 @@ public void extractRowFromOneNumberMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("can extract row") public void extractRowFromMatrixTest() { String matrixAsString = "1 2\n3 4"; int rowIndex = 2; @@ -30,6 +33,7 @@ public void extractRowFromMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("extract row where numbers have different widths") public void extractRowFromDiffWidthsMatrixTest() { String matrixAsString = "1 2\n10 20"; int rowIndex = 2; @@ -42,6 +46,7 @@ public void extractRowFromDiffWidthsMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("can extract row from non-square matrix with no corresponding column") public void extractRowFromNonSquareMatrixTest() { String matrixAsString = "1 2 3\n4 5 6\n7 8 9\n8 7 6"; int rowIndex = 4; @@ -54,6 +59,7 @@ public void extractRowFromNonSquareMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("extract column from one number matrix") public void extractColumnFromOneNumberMatrixTest() { String matrixAsString = "1"; int columnIndex = 1; @@ -66,6 +72,7 @@ public void extractColumnFromOneNumberMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("can extract column") public void extractColumnMatrixTest() { String matrixAsString = "1 2 3\n4 5 6\n7 8 9"; int columnIndex = 3; @@ -78,6 +85,7 @@ public void extractColumnMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("can extract column from non-square matrix with no corresponding row") public void extractColumnFromNonSquareMatrixTest() { String matrixAsString = "1 2 3 4\n5 6 7 8\n9 8 7 6"; int columnIndex = 4; @@ -90,6 +98,7 @@ public void extractColumnFromNonSquareMatrixTest() { @Disabled("Remove to run test") @Test + @DisplayName("extract column where numbers have different widths") public void extractColumnFromDiffWidthsMatrixTest() { String matrixAsString = "89 1903 3\n18 3 1\n9 4 800"; int columnIndex = 2; 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/mazy-mice/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/mazy-mice/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/mazy-mice/gradlew b/exercises/practice/mazy-mice/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/mazy-mice/gradlew +++ b/exercises/practice/mazy-mice/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/mazy-mice/gradlew.bat b/exercises/practice/mazy-mice/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/mazy-mice/gradlew.bat +++ b/exercises/practice/mazy-mice/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/mazy-mice/src/test/java/MazeGeneratorTest.java b/exercises/practice/mazy-mice/src/test/java/MazeGeneratorTest.java index 24e5de663..8b3635f2a 100644 --- a/exercises/practice/mazy-mice/src/test/java/MazeGeneratorTest.java +++ b/exercises/practice/mazy-mice/src/test/java/MazeGeneratorTest.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.Set; @@ -37,6 +38,7 @@ public void setup() { } @Test + @DisplayName("Maze has correct overall dimensions") public void theDimensionsOfTheMazeAreCorrect() { var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); var expectedWidth = RECTANGLE_COLUMNS * 2 + 1; @@ -49,6 +51,7 @@ public void theDimensionsOfTheMazeAreCorrect() { @Disabled("Remove to run test") @Test + @DisplayName("Maze contains only valid characters") public void theMazeContainsOnlyValidCharacters() { var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); @@ -63,7 +66,8 @@ public void theMazeContainsOnlyValidCharacters() { @Disabled("Remove to run test") @Test - public void theMazeHasOnlyOneEntranceOnTheLeftSide() { + @DisplayName("Maze has a single entrance on the left side") + public void theMazeHasSingleEntranceOnTheLeftSide() { var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); int entranceCount = countEntrances(maze); @@ -74,7 +78,8 @@ public void theMazeHasOnlyOneEntranceOnTheLeftSide() { @Disabled("Remove to run test") @Test - public void theMazeHasSingleExitOnTheRightSideOfTheMaze() { + @DisplayName("Maze has a single exit on the right side") + public void theMazeHasSingleExitOnTheRightSide() { var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); int exitCount = countExits(maze); @@ -85,6 +90,7 @@ public void theMazeHasSingleExitOnTheRightSideOfTheMaze() { @Disabled("Remove to run test") @Test + @DisplayName("Maze is different each time it is generated") public void aMazeIsDifferentEachTimeItIsGenerated() { var maze1 = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); var maze2 = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); @@ -96,6 +102,27 @@ public void aMazeIsDifferentEachTimeItIsGenerated() { @Disabled("Remove to run test") @Test + @DisplayName("Maze is generated perfectly (single path, no isolated cells)") + public void theMazeIsPerfect() { + var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); + + assertThatMazeHasSinglePath(maze); + assertThatMazeHasNoIsolatedSections(maze); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Maze with a seed is generated perfectly (single path, no isolated cells)") + public void theMazeIsPerfectWithSeed() { + var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS, SEED_ONE); + + assertThatMazeHasSinglePath(maze); + assertThatMazeHasNoIsolatedSections(maze); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Mazes generated with same seed are identical") public void twoMazesWithSameSeedShouldBeEqual() { var maze1 = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS, SEED_ONE); var maze2 = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS, SEED_ONE); @@ -107,6 +134,7 @@ public void twoMazesWithSameSeedShouldBeEqual() { @Disabled("Remove to run test") @Test + @DisplayName("Mazes generated with different seeds are different") public void twoMazesWithDifferentSeedsShouldNotBeEqual() { var maze1 = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS, SEED_ONE); var maze2 = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS, SEED_TWO); @@ -118,24 +146,7 @@ public void twoMazesWithDifferentSeedsShouldNotBeEqual() { @Disabled("Remove to run test") @Test - public void theMazeIsPerfect() { - var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS); - - assertThatMazeHasSinglePath(maze); - assertThatMazeHasNoIsolatedSections(maze); - } - - @Disabled("Remove to run test") - @Test - public void theMazeIsPerfectWithSeed() { - var maze = sut.generatePerfectMaze(RECTANGLE_ROWS, RECTANGLE_COLUMNS, SEED_ONE); - - assertThatMazeHasSinglePath(maze); - assertThatMazeHasNoIsolatedSections(maze); - } - - @Disabled("Remove to run test") - @Test + @DisplayName("Throws when rows are less than five") public void shouldThrowExceptionWhenRowsIsLessThanFive() { assertThatIllegalArgumentException() .isThrownBy(() -> sut.generatePerfectMaze(0, RECTANGLE_COLUMNS)); @@ -143,6 +154,7 @@ public void shouldThrowExceptionWhenRowsIsLessThanFive() { @Disabled("Remove to run test") @Test + @DisplayName("Throws when columns are less than five") public void shouldThrowExceptionWhenColumnsIsLessThanFive() { assertThatIllegalArgumentException() .isThrownBy(() -> sut.generatePerfectMaze(RECTANGLE_ROWS, 0)); @@ -150,6 +162,7 @@ public void shouldThrowExceptionWhenColumnsIsLessThanFive() { @Disabled("Remove to run test") @Test + @DisplayName("Throws when rows exceed hundred") public void shouldThrowExceptionWhenRowsIsMoreThenHundred() { assertThatIllegalArgumentException() .isThrownBy(() -> sut.generatePerfectMaze(101, RECTANGLE_COLUMNS)); @@ -157,6 +170,7 @@ public void shouldThrowExceptionWhenRowsIsMoreThenHundred() { @Disabled("Remove to run test") @Test + @DisplayName("Throws when columns exceed hundred") public void shouldThrowExceptionWhenColumnsIsMoreThenHundred() { assertThatIllegalArgumentException() .isThrownBy(() -> sut.generatePerfectMaze(RECTANGLE_ROWS, 101)); 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/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/meetup/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/meetup/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/meetup/gradlew b/exercises/practice/meetup/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/meetup/gradlew +++ b/exercises/practice/meetup/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/meetup/gradlew.bat b/exercises/practice/meetup/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/meetup/gradlew.bat +++ b/exercises/practice/meetup/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/meetup/src/test/java/MeetupTest.java b/exercises/practice/meetup/src/test/java/MeetupTest.java index 47eab9ad7..193eaa9ea 100644 --- a/exercises/practice/meetup/src/test/java/MeetupTest.java +++ b/exercises/practice/meetup/src/test/java/MeetupTest.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.DayOfWeek; @@ -9,6 +10,7 @@ public class MeetupTest { @Test + @DisplayName("when teenth Monday is the 13th, the first day of the teenth week") public void testMonteenthOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 13); Meetup meetup = new Meetup(5, 2013); @@ -17,6 +19,7 @@ public void testMonteenthOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Monday is the 19th, the last day of the teenth week") public void testMonteenthOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 19); Meetup meetup = new Meetup(8, 2013); @@ -25,6 +28,7 @@ public void testMonteenthOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Monday is some day in the middle of the teenth week") public void testMonteenthOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 16); Meetup meetup = new Meetup(9, 2013); @@ -33,6 +37,7 @@ public void testMonteenthOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Tuesday is the 19th, the last day of the teenth week") public void testTuesteenthOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 19); Meetup meetup = new Meetup(3, 2013); @@ -41,6 +46,7 @@ public void testTuesteenthOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Tuesday is some day in the middle of the teenth week") public void testTuesteenthOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 16); Meetup meetup = new Meetup(4, 2013); @@ -49,6 +55,7 @@ public void testTuesteenthOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Tuesday is the 13th, the first day of the teenth week") public void testTuesteenthOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 13); Meetup meetup = new Meetup(8, 2013); @@ -57,6 +64,7 @@ public void testTuesteenthOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Wednesday is some day in the middle of the teenth week") public void testWednesteenthOfJanuary2013() { LocalDate expected = LocalDate.of(2013, 1, 16); Meetup meetup = new Meetup(1, 2013); @@ -65,6 +73,7 @@ public void testWednesteenthOfJanuary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Wednesday is the 13th, the first day of the teenth week") public void testWednesteenthOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 13); Meetup meetup = new Meetup(2, 2013); @@ -73,6 +82,7 @@ public void testWednesteenthOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Wednesday is the 19th, the last day of the teenth week") public void testWednesteenthOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 19); Meetup meetup = new Meetup(6, 2013); @@ -81,6 +91,7 @@ public void testWednesteenthOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Thursday is some day in the middle of the teenth week") public void testThursteenthOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 16); Meetup meetup = new Meetup(5, 2013); @@ -89,6 +100,7 @@ public void testThursteenthOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Thursday is the 13th, the first day of the teenth week") public void testThursteenthOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 13); Meetup meetup = new Meetup(6, 2013); @@ -97,6 +109,7 @@ public void testThursteenthOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Thursday is the 19th, the last day of the teenth week") public void testThursteenthOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 19); Meetup meetup = new Meetup(9, 2013); @@ -105,6 +118,7 @@ public void testThursteenthOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Friday is the 19th, the last day of the teenth week") public void testFriteenthOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 19); Meetup meetup = new Meetup(4, 2013); @@ -113,6 +127,7 @@ public void testFriteenthOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Friday is some day in the middle of the teenth week") public void testFriteenthOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 16); Meetup meetup = new Meetup(8, 2013); @@ -121,6 +136,7 @@ public void testFriteenthOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Friday is the 13th, the first day of the teenth week") public void testFriteenthOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 13); Meetup meetup = new Meetup(9, 2013); @@ -129,6 +145,7 @@ public void testFriteenthOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Saturday is some day in the middle of the teenth week") public void testSaturteenthOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 16); Meetup meetup = new Meetup(2, 2013); @@ -137,6 +154,7 @@ public void testSaturteenthOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Saturday is the 13th, the first day of the teenth week") public void testSaturteenthOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 13); Meetup meetup = new Meetup(4, 2013); @@ -145,6 +163,7 @@ public void testSaturteenthOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Saturday is the 19th, the last day of the teenth week") public void testSaturteenthOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 19); Meetup meetup = new Meetup(10, 2013); @@ -153,6 +172,7 @@ public void testSaturteenthOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Sunday is the 19th, the last day of the teenth week") public void testSunteenthOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 19); Meetup meetup = new Meetup(5, 2013); @@ -161,6 +181,7 @@ public void testSunteenthOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Sunday is some day in the middle of the teenth week") public void testSunteenthOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 16); Meetup meetup = new Meetup(6, 2013); @@ -169,6 +190,7 @@ public void testSunteenthOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when teenth Sunday is the 13th, the first day of the teenth week") public void testSunteenthOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 13); Meetup meetup = new Meetup(10, 2013); @@ -177,6 +199,7 @@ public void testSunteenthOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Monday is some day in the middle of the first week") public void testFirstMondayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 4); Meetup meetup = new Meetup(3, 2013); @@ -185,6 +208,7 @@ public void testFirstMondayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Monday is the 1st, the first day of the first week") public void testFirstMondayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 1); Meetup meetup = new Meetup(4, 2013); @@ -193,6 +217,7 @@ public void testFirstMondayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Tuesday is the 7th, the last day of the first week") public void testFirstTuesdayOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 7); Meetup meetup = new Meetup(5, 2013); @@ -201,6 +226,7 @@ public void testFirstTuesdayOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Tuesday is some day in the middle of the first week") public void testFirstTuesdayOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 4); Meetup meetup = new Meetup(6, 2013); @@ -209,6 +235,7 @@ public void testFirstTuesdayOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Wednesday is some day in the middle of the first week") public void testFirstWednesdayOfJuly2013() { LocalDate expected = LocalDate.of(2013, 7, 3); Meetup meetup = new Meetup(7, 2013); @@ -217,6 +244,7 @@ public void testFirstWednesdayOfJuly2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Wednesday is the 7th, the last day of the first week") public void testFirstWednesdayOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 7); Meetup meetup = new Meetup(8, 2013); @@ -225,6 +253,7 @@ public void testFirstWednesdayOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Thursday is some day in the middle of the first wee") public void testFirstThursdayOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 5); Meetup meetup = new Meetup(9, 2013); @@ -233,6 +262,7 @@ public void testFirstThursdayOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Thursday is another day in the middle of the first week") public void testFirstThursdayOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 3); Meetup meetup = new Meetup(10, 2013); @@ -241,6 +271,7 @@ public void testFirstThursdayOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Friday is the 1st, the first day of the first week") public void testFirstFridayOfNovember2013() { LocalDate expected = LocalDate.of(2013, 11, 1); Meetup meetup = new Meetup(11, 2013); @@ -249,6 +280,7 @@ public void testFirstFridayOfNovember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Friday is some day in the middle of the first week") public void testFirstFridayOfDecember2013() { LocalDate expected = LocalDate.of(2013, 12, 6); Meetup meetup = new Meetup(12, 2013); @@ -257,6 +289,7 @@ public void testFirstFridayOfDecember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Saturday is some day in the middle of the first week") public void testFirstSaturdayOfJanuary2013() { LocalDate expected = LocalDate.of(2013, 1, 5); Meetup meetup = new Meetup(1, 2013); @@ -265,6 +298,7 @@ public void testFirstSaturdayOfJanuary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Saturday is another day in the middle of the first week") public void testFirstSaturdayOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 2); Meetup meetup = new Meetup(2, 2013); @@ -273,6 +307,7 @@ public void testFirstSaturdayOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Sunday is some day in the middle of the first week") public void testFirstSundayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 3); Meetup meetup = new Meetup(3, 2013); @@ -281,6 +316,7 @@ public void testFirstSundayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when first Sunday is the 7th, the last day of the first week") public void testFirstSundayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 7); Meetup meetup = new Meetup(4, 2013); @@ -289,6 +325,7 @@ public void testFirstSundayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Monday is some day in the middle of the second week") public void testSecondMondayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 11); Meetup meetup = new Meetup(3, 2013); @@ -297,6 +334,7 @@ public void testSecondMondayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Monday is the 8th, the first day of the second week") public void testSecondMondayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 8); Meetup meetup = new Meetup(4, 2013); @@ -305,6 +343,7 @@ public void testSecondMondayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Tuesday is the 14th, the last day of the second week") public void testSecondTuesdayOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 14); Meetup meetup = new Meetup(5, 2013); @@ -313,6 +352,7 @@ public void testSecondTuesdayOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Tuesday is some day in the middle of the second week") public void testSecondTuesdayOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 11); Meetup meetup = new Meetup(6, 2013); @@ -321,6 +361,7 @@ public void testSecondTuesdayOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Wednesday is some day in the middle of the second week") public void testSecondWednesdayOfJuly2013() { LocalDate expected = LocalDate.of(2013, 7, 10); Meetup meetup = new Meetup(7, 2013); @@ -329,6 +370,7 @@ public void testSecondWednesdayOfJuly2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Wednesday is the 14th, the last day of the second week") public void testSecondWednesdayOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 14); Meetup meetup = new Meetup(8, 2013); @@ -337,6 +379,7 @@ public void testSecondWednesdayOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Thursday is some day in the middle of the second week") public void testSecondThursdayOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 12); Meetup meetup = new Meetup(9, 2013); @@ -345,6 +388,7 @@ public void testSecondThursdayOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Thursday is another day in the middle of the second week") public void testSecondThursdayOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 10); Meetup meetup = new Meetup(10, 2013); @@ -353,6 +397,7 @@ public void testSecondThursdayOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Friday is the 8th, the first day of the second week") public void testSecondFridayOfNovember2013() { LocalDate expected = LocalDate.of(2013, 11, 8); Meetup meetup = new Meetup(11, 2013); @@ -361,6 +406,7 @@ public void testSecondFridayOfNovember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Friday is some day in the middle of the second week") public void testSecondFridayOfDecember2013() { LocalDate expected = LocalDate.of(2013, 12, 13); Meetup meetup = new Meetup(12, 2013); @@ -369,6 +415,7 @@ public void testSecondFridayOfDecember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Saturday is some day in the middle of the second week") public void testSecondSaturdayOfJanuary2013() { LocalDate expected = LocalDate.of(2013, 1, 12); Meetup meetup = new Meetup(1, 2013); @@ -377,6 +424,7 @@ public void testSecondSaturdayOfJanuary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Saturday is another day in the middle of the second week") public void testSecondSaturdayOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 9); Meetup meetup = new Meetup(2, 2013); @@ -385,6 +433,7 @@ public void testSecondSaturdayOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Sunday is some day in the middle of the second week") public void testSecondSundayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 10); Meetup meetup = new Meetup(3, 2013); @@ -393,6 +442,7 @@ public void testSecondSundayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when second Sunday is the 14th, the last day of the second week") public void testSecondSundayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 14); Meetup meetup = new Meetup(4, 2013); @@ -401,6 +451,7 @@ public void testSecondSundayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Monday is some day in the middle of the third week") public void testThirdMondayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 18); Meetup meetup = new Meetup(3, 2013); @@ -409,6 +460,7 @@ public void testThirdMondayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Monday is the 15th, the first day of the third week") public void testThirdMondayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 15); Meetup meetup = new Meetup(4, 2013); @@ -417,6 +469,7 @@ public void testThirdMondayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Tuesday is the 21st, the last day of the third week") public void testThirdTuesdayOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 21); Meetup meetup = new Meetup(5, 2013); @@ -425,6 +478,7 @@ public void testThirdTuesdayOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Tuesday is some day in the middle of the third week") public void testThirdTuesdayOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 18); Meetup meetup = new Meetup(6, 2013); @@ -433,6 +487,7 @@ public void testThirdTuesdayOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Wednesday is some day in the middle of the third week") public void testThirdWednesdayOfJuly2013() { LocalDate expected = LocalDate.of(2013, 7, 17); Meetup meetup = new Meetup(7, 2013); @@ -441,6 +496,7 @@ public void testThirdWednesdayOfJuly2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Wednesday is the 21st, the last day of the third week") public void testThirdWednesdayOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 21); Meetup meetup = new Meetup(8, 2013); @@ -449,6 +505,7 @@ public void testThirdWednesdayOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Thursday is some day in the middle of the third week") public void testThirdThursdayOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 19); Meetup meetup = new Meetup(9, 2013); @@ -457,6 +514,7 @@ public void testThirdThursdayOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Thursday is another day in the middle of the third week") public void testThirdThursdayOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 17); Meetup meetup = new Meetup(10, 2013); @@ -465,6 +523,7 @@ public void testThirdThursdayOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Friday is the 15th, the first day of the third week") public void testThirdFridayOfNovember2013() { LocalDate expected = LocalDate.of(2013, 11, 15); Meetup meetup = new Meetup(11, 2013); @@ -473,6 +532,7 @@ public void testThirdFridayOfNovember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Friday is some day in the middle of the third week") public void testThirdFridayOfDecember2013() { LocalDate expected = LocalDate.of(2013, 12, 20); Meetup meetup = new Meetup(12, 2013); @@ -481,6 +541,7 @@ public void testThirdFridayOfDecember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Saturday is some day in the middle of the third week") public void testThirdSaturdayOfJanuary2013() { LocalDate expected = LocalDate.of(2013, 1, 19); Meetup meetup = new Meetup(1, 2013); @@ -489,6 +550,7 @@ public void testThirdSaturdayOfJanuary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Saturday is another day in the middle of the third week") public void testThirdSaturdayOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 16); Meetup meetup = new Meetup(2, 2013); @@ -497,6 +559,7 @@ public void testThirdSaturdayOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Sunday is some day in the middle of the third week") public void testThirdSundayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 17); Meetup meetup = new Meetup(3, 2013); @@ -505,6 +568,7 @@ public void testThirdSundayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when third Sunday is the 21st, the last day of the third week") public void testThirdSundayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 21); Meetup meetup = new Meetup(4, 2013); @@ -513,6 +577,7 @@ public void testThirdSundayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Monday is some day in the middle of the fourth week") public void testFourthMondayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 25); Meetup meetup = new Meetup(3, 2013); @@ -521,6 +586,7 @@ public void testFourthMondayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Monday is the 22nd, the first day of the fourth week") public void testFourthMondayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 22); Meetup meetup = new Meetup(4, 2013); @@ -529,6 +595,7 @@ public void testFourthMondayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Tuesday is the 28th, the last day of the fourth week") public void testFourthTuesdayOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 28); Meetup meetup = new Meetup(5, 2013); @@ -537,6 +604,7 @@ public void testFourthTuesdayOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Tuesday is some day in the middle of the fourth week") public void testFourthTuesdayOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 25); Meetup meetup = new Meetup(6, 2013); @@ -545,6 +613,7 @@ public void testFourthTuesdayOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Wednesday is some day in the middle of the fourth week") public void testFourthWednesdayOfJuly2013() { LocalDate expected = LocalDate.of(2013, 7, 24); Meetup meetup = new Meetup(7, 2013); @@ -553,6 +622,7 @@ public void testFourthWednesdayOfJuly2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Wednesday is the 28th, the last day of the fourth week") public void testFourthWednesdayOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 28); Meetup meetup = new Meetup(8, 2013); @@ -561,6 +631,7 @@ public void testFourthWednesdayOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Thursday is some day in the middle of the fourth week") public void testFourthThursdayOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 26); Meetup meetup = new Meetup(9, 2013); @@ -569,6 +640,7 @@ public void testFourthThursdayOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Thursday is another day in the middle of the fourth week") public void testFourthThursdayOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 24); Meetup meetup = new Meetup(10, 2013); @@ -577,6 +649,7 @@ public void testFourthThursdayOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Friday is the 22nd, the first day of the fourth week") public void testFourthFridayOfNovember2013() { LocalDate expected = LocalDate.of(2013, 11, 22); Meetup meetup = new Meetup(11, 2013); @@ -585,6 +658,7 @@ public void testFourthFridayOfNovember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Friday is some day in the middle of the fourth week") public void testFourthFridayOfDecember2013() { LocalDate expected = LocalDate.of(2013, 12, 27); Meetup meetup = new Meetup(12, 2013); @@ -593,6 +667,7 @@ public void testFourthFridayOfDecember2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Saturday is some day in the middle of the fourth week") public void testFourthSaturdayOfJanuary2013() { LocalDate expected = LocalDate.of(2013, 1, 26); Meetup meetup = new Meetup(1, 2013); @@ -601,6 +676,7 @@ public void testFourthSaturdayOfJanuary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Saturday is another day in the middle of the fourth week") public void testFourthSaturdayOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 23); Meetup meetup = new Meetup(2, 2013); @@ -609,6 +685,7 @@ public void testFourthSaturdayOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Sunday is some day in the middle of the fourth week") public void testFourthSundayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 24); Meetup meetup = new Meetup(3, 2013); @@ -617,6 +694,7 @@ public void testFourthSundayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("when fourth Sunday is the 28th, the last day of the fourth week") public void testFourthSundayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 28); Meetup meetup = new Meetup(4, 2013); @@ -625,6 +703,7 @@ public void testFourthSundayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Monday in a month with four Mondays") public void testLastMondayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 25); Meetup meetup = new Meetup(3, 2013); @@ -633,6 +712,7 @@ public void testLastMondayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Monday in a month with five Mondays") public void testLastMondayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 29); Meetup meetup = new Meetup(4, 2013); @@ -641,6 +721,7 @@ public void testLastMondayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Tuesday in a month with four Tuesdays") public void testLastTuesdayOfMay2013() { LocalDate expected = LocalDate.of(2013, 5, 28); Meetup meetup = new Meetup(5, 2013); @@ -649,6 +730,7 @@ public void testLastTuesdayOfMay2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Tuesday in another month with four Tuesdays") public void testLastTuesdayOfJune2013() { LocalDate expected = LocalDate.of(2013, 6, 25); Meetup meetup = new Meetup(6, 2013); @@ -657,6 +739,7 @@ public void testLastTuesdayOfJune2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Wednesday in a month with five Wednesdays") public void testLastWednesdayOfJuly2013() { LocalDate expected = LocalDate.of(2013, 7, 31); Meetup meetup = new Meetup(7, 2013); @@ -665,6 +748,7 @@ public void testLastWednesdayOfJuly2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Wednesday in a month with four Wednesdays") public void testLastWednesdayOfAugust2013() { LocalDate expected = LocalDate.of(2013, 8, 28); Meetup meetup = new Meetup(8, 2013); @@ -673,6 +757,7 @@ public void testLastWednesdayOfAugust2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Thursday in a month with four Thursdays") public void testLastThursdayOfSeptember2013() { LocalDate expected = LocalDate.of(2013, 9, 26); Meetup meetup = new Meetup(9, 2013); @@ -681,6 +766,7 @@ public void testLastThursdayOfSeptember2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Thursday in a month with five Thursdays") public void testLastThursdayOfOctober2013() { LocalDate expected = LocalDate.of(2013, 10, 31); Meetup meetup = new Meetup(10, 2013); @@ -689,6 +775,7 @@ public void testLastThursdayOfOctober2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Friday in a month with five Fridays") public void testLastFridayOfNovember2013() { LocalDate expected = LocalDate.of(2013, 11, 29); Meetup meetup = new Meetup(11, 2013); @@ -697,6 +784,7 @@ public void testLastFridayOfNovember2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Friday in a month with four Fridays") public void testLastFridayOfDecember2013() { LocalDate expected = LocalDate.of(2013, 12, 27); Meetup meetup = new Meetup(12, 2013); @@ -705,6 +793,7 @@ public void testLastFridayOfDecember2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Saturday in a month with four Saturdays") public void testLastSaturdayOfJanuary2013() { LocalDate expected = LocalDate.of(2013, 1, 26); Meetup meetup = new Meetup(1, 2013); @@ -713,6 +802,7 @@ public void testLastSaturdayOfJanuary2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Saturday in another month with four Saturdays") public void testLastSaturdayOfFebruary2013() { LocalDate expected = LocalDate.of(2013, 2, 23); Meetup meetup = new Meetup(2, 2013); @@ -721,6 +811,7 @@ public void testLastSaturdayOfFebruary2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Sunday in a month with five Sundays") public void testLastSundayOfMarch2013() { LocalDate expected = LocalDate.of(2013, 3, 31); Meetup meetup = new Meetup(3, 2013); @@ -729,6 +820,7 @@ public void testLastSundayOfMarch2013() { @Disabled("Remove to run test") @Test + @DisplayName("last Sunday in a month with four Sundays") public void testLastSundayOfApril2013() { LocalDate expected = LocalDate.of(2013, 4, 28); Meetup meetup = new Meetup(4, 2013); @@ -737,6 +829,7 @@ public void testLastSundayOfApril2013() { @Disabled("Remove to run test") @Test + @DisplayName("when last Wednesday in February in a leap year is the 29th") public void testLastWednesdayOfFebruary2012() { LocalDate expected = LocalDate.of(2012, 2, 29); Meetup meetup = new Meetup(2, 2012); @@ -745,6 +838,7 @@ public void testLastWednesdayOfFebruary2012() { @Disabled("Remove to run test") @Test + @DisplayName("last Wednesday in December that is also the last day of the year") public void testLastWednesdayOfDecember2014() { LocalDate expected = LocalDate.of(2014, 12, 31); Meetup meetup = new Meetup(12, 2014); @@ -753,6 +847,7 @@ public void testLastWednesdayOfDecember2014() { @Disabled("Remove to run test") @Test + @DisplayName("when last Sunday in February in a non-leap year is not the 29th") public void testLastSundayOfFebruary2015() { LocalDate expected = LocalDate.of(2015, 2, 22); Meetup meetup = new Meetup(2, 2015); @@ -761,6 +856,7 @@ public void testLastSundayOfFebruary2015() { @Disabled("Remove to run test") @Test + @DisplayName("when first Friday is the 7th, the last day of the first week") public void testFirstFridayOfDecember2012() { LocalDate expected = LocalDate.of(2012, 12, 7); Meetup meetup = new Meetup(12, 2012); 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/micro-blog/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/micro-blog/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/micro-blog/gradlew b/exercises/practice/micro-blog/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/micro-blog/gradlew +++ b/exercises/practice/micro-blog/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/micro-blog/gradlew.bat b/exercises/practice/micro-blog/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/micro-blog/gradlew.bat +++ b/exercises/practice/micro-blog/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/micro-blog/src/test/java/MicroBlogTest.java b/exercises/practice/micro-blog/src/test/java/MicroBlogTest.java index b22106041..1b44d5a78 100644 --- a/exercises/practice/micro-blog/src/test/java/MicroBlogTest.java +++ b/exercises/practice/micro-blog/src/test/java/MicroBlogTest.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 MicroBlogTest { private final MicroBlog microBlog = new MicroBlog(); @Test + @DisplayName("English language short") public void englishLanguageShort() { String expected = "Hi"; assertThat(microBlog.truncate("Hi")).isEqualTo(expected); @@ -15,6 +17,7 @@ public void englishLanguageShort() { @Disabled("Remove to run test") @Test + @DisplayName("English language long") public void englishLanguageLong() { String expected = "Hello"; assertThat(microBlog.truncate("Hello there")).isEqualTo(expected); @@ -22,34 +25,39 @@ public void englishLanguageLong() { @Disabled("Remove to run test") @Test - public void germanLanguageShort_broth() { + @DisplayName("German language short (broth)") + public void germanLanguageShortBroth() { String expected = "brühe"; assertThat(microBlog.truncate("brühe")).isEqualTo(expected); } @Disabled("Remove to run test") @Test - public void germanLanguageLong_bearCarpet_to_beards() { + @DisplayName("German language long (bear carpet → beards)") + public void germanLanguageLongBearCarpetToBeards() { String expected = "Bärte"; assertThat(microBlog.truncate("Bärteppich")).isEqualTo(expected); } @Disabled("Remove to run test") @Test - public void bulgarianLanguageShort_good() { + @DisplayName("Bulgarian language short (good)") + public void bulgarianLanguageShortGood() { String expected = "Добър"; assertThat(microBlog.truncate("Добър")).isEqualTo(expected); } @Disabled("Remove to run test") @Test - public void greekLanguageShort_health() { + @DisplayName("Greek language short (health)") + public void greekLanguageShortHealth() { String expected = "υγειά"; assertThat(microBlog.truncate("υγειά")).isEqualTo(expected); } @Disabled("Remove to run test") @Test + @DisplayName("Maths short") public void mathsShort() { String expected = "a=πr²"; assertThat(microBlog.truncate("a=πr²")).isEqualTo(expected); @@ -57,6 +65,7 @@ public void mathsShort() { @Disabled("Remove to run test") @Test + @DisplayName("Maths long") public void mathsLong() { String expected = "∅⊊ℕ⊊ℤ"; assertThat(microBlog.truncate("∅⊊ℕ⊊ℤ⊊ℚ⊊ℝ⊊ℂ")).isEqualTo(expected); @@ -64,6 +73,7 @@ public void mathsLong() { @Disabled("Remove to run test") @Test + @DisplayName("English and emoji short") public void englishAndEmojiShort() { String expected = "Fly 🛫"; assertThat(microBlog.truncate("Fly 🛫")).isEqualTo(expected); @@ -71,6 +81,7 @@ public void englishAndEmojiShort() { @Disabled("Remove to run test") @Test + @DisplayName("Emoji short") public void emojiShort() { String expected = "💇"; assertThat(microBlog.truncate("💇")).isEqualTo(expected); @@ -78,6 +89,7 @@ public void emojiShort() { @Disabled("Remove to run test") @Test + @DisplayName("Emoji long") public void emojiLong() { String expected = "❄🌡🤧🤒🏥"; assertThat(microBlog.truncate("❄🌡🤧🤒🏥🕰😀")).isEqualTo(expected); @@ -85,6 +97,7 @@ public void emojiLong() { @Disabled("Remove to run test") @Test + @DisplayName("Royal Flush?") public void royalFlush() { String expected = "🃎🂸🃅🃋🃍"; assertThat(microBlog.truncate("🃎🂸🃅🃋🃍🃁🃊")).isEqualTo(expected); 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/nth-prime/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/nth-prime/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/nth-prime/gradlew b/exercises/practice/nth-prime/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/nth-prime/gradlew +++ b/exercises/practice/nth-prime/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/nth-prime/gradlew.bat b/exercises/practice/nth-prime/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/nth-prime/gradlew.bat +++ b/exercises/practice/nth-prime/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/nth-prime/src/test/java/PrimeCalculatorTest.java b/exercises/practice/nth-prime/src/test/java/PrimeCalculatorTest.java index e13c7f23d..10d0bcf29 100644 --- a/exercises/practice/nth-prime/src/test/java/PrimeCalculatorTest.java +++ b/exercises/practice/nth-prime/src/test/java/PrimeCalculatorTest.java @@ -1,8 +1,8 @@ import static org.assertj.core.api.Assertions.assertThat; 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 PrimeCalculatorTest { @@ -10,30 +10,35 @@ public class PrimeCalculatorTest { private PrimeCalculator primeCalculator = new PrimeCalculator(); @Test + @DisplayName("first prime") public void testFirstPrime() { assertThat(primeCalculator.nth(1)).isEqualTo(2); } @Disabled("Remove to run test") @Test + @DisplayName("second prime") public void testSecondPrime() { assertThat(primeCalculator.nth(2)).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("sixth prime") public void testSixthPrime() { assertThat(primeCalculator.nth(6)).isEqualTo(13); } @Disabled("Remove to run test") @Test + @DisplayName("big prime") public void testBigPrime() { assertThat(primeCalculator.nth(10001)).isEqualTo(104743); } @Disabled("Remove to run test") @Test + @DisplayName("there is no zeroth prime") public void testUndefinedPrime() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> primeCalculator.nth(0)); 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/nucleotide-count/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/nucleotide-count/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/nucleotide-count/gradlew b/exercises/practice/nucleotide-count/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/nucleotide-count/gradlew +++ b/exercises/practice/nucleotide-count/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/nucleotide-count/gradlew.bat b/exercises/practice/nucleotide-count/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/nucleotide-count/gradlew.bat +++ b/exercises/practice/nucleotide-count/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/nucleotide-count/src/test/java/NucleotideCounterTest.java b/exercises/practice/nucleotide-count/src/test/java/NucleotideCounterTest.java index b47fb836e..d5938268a 100644 --- a/exercises/practice/nucleotide-count/src/test/java/NucleotideCounterTest.java +++ b/exercises/practice/nucleotide-count/src/test/java/NucleotideCounterTest.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.Map; @@ -9,6 +10,7 @@ public class NucleotideCounterTest { @Test + @DisplayName("empty strand") public void testEmptyDnaStringHasNoNucleotides() { NucleotideCounter nucleotideCounter = new NucleotideCounter(""); @@ -19,6 +21,7 @@ public void testEmptyDnaStringHasNoNucleotides() { @Disabled("Remove to run test") @Test + @DisplayName("can count one nucleotide in single-character input") public void testDnaStringHasOneNucleotide() { NucleotideCounter nucleotideCounter = new NucleotideCounter("G"); @@ -29,6 +32,7 @@ public void testDnaStringHasOneNucleotide() { @Disabled("Remove to run test") @Test + @DisplayName("strand with repeated nucleotide") public void testRepetitiveSequenceWithOnlyGuanine() { NucleotideCounter nucleotideCounter = new NucleotideCounter("GGGGGGG"); @@ -39,6 +43,7 @@ public void testRepetitiveSequenceWithOnlyGuanine() { @Disabled("Remove to run test") @Test + @DisplayName("strand with multiple nucleotides") public void testDnaStringHasMultipleNucleotide() { NucleotideCounter nucleotideCounter = new NucleotideCounter("AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC"); @@ -50,6 +55,7 @@ public void testDnaStringHasMultipleNucleotide() { @Disabled("Remove to run test") @Test + @DisplayName("strand with invalid nucleotides") public void testDnaStringHasInvalidNucleotides() { assertThatThrownBy(() -> new NucleotideCounter("AGXXACT")) .isInstanceOf(IllegalArgumentException.class); diff --git a/exercises/practice/ocr-numbers/.docs/instructions.md b/exercises/practice/ocr-numbers/.docs/instructions.md index 7beb25779..8a391ce4f 100644 --- a/exercises/practice/ocr-numbers/.docs/instructions.md +++ b/exercises/practice/ocr-numbers/.docs/instructions.md @@ -1,79 +1,47 @@ # Instructions -Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled. +Optical Character Recognition or OCR is software that converts images of text into machine-readable text. +Given a grid of characters representing some digits, convert the grid to a string of digits. +If the grid has multiple rows of cells, the rows should be separated in the output with a `","`. -## Step One +- The grid is made of one of more lines of cells. +- Each line of the grid is made of one or more cells. +- Each cell is three columns wide and four rows high (3x4) and represents one digit. +- Digits are drawn using pipes (`"|"`), underscores (`"_"`), and spaces (`" "`). -To begin with, convert a simple binary font to a string containing 0 or 1. +## Edge cases -The binary font uses pipes and underscores, four rows high and three columns wide. +- If the input is not a valid size, your program should indicate there is an error. +- If the input is the correct size, but a cell is not recognizable, your program should output a `"?"` for that character. -```text - _ # - | | # zero. - |_| # - # the fourth row is always blank -``` +## Examples -Is converted to "0" - -```text - # - | # one. - | # - # (blank fourth row) -``` - -Is converted to "1" - -If the input is the correct size, but not recognizable, your program should return '?' - -If the input is the incorrect size, your program should return an error. - -## Step Two - -Update your program to recognize multi-character binary strings, replacing garbled numbers with ? - -## Step Three - -Update your program to recognize all numbers 0 through 9, both individually and as part of a larger string. - -```text - _ - _| -|_ - -``` - -Is converted to "2" +The following input (without the comments) is converted to `"1234567890"`. ```text _ _ _ _ _ _ _ _ # - | _| _||_||_ |_ ||_||_|| | # decimal numbers. + | _| _||_||_ |_ ||_||_|| | # Decimal numbers. ||_ _| | _||_| ||_| _||_| # - # fourth line is always blank + # The fourth line is always blank, ``` -Is converted to "1234567890" - -## Step Four +The following input is converted to `"123,456,789"`. -Update your program to handle multiple numbers, one per line. -When converting several lines, join the lines with commas. + ```text - _ _ + _ _ | _| _| ||_ _| - - _ _ -|_||_ |_ + + _ _ +|_||_ |_ | _||_| - - _ _ _ + + _ _ _ ||_||_| ||_| _| - + ``` -Is converted to "123,456,789". + diff --git a/exercises/practice/ocr-numbers/.docs/introduction.md b/exercises/practice/ocr-numbers/.docs/introduction.md new file mode 100644 index 000000000..366d76062 --- /dev/null +++ b/exercises/practice/ocr-numbers/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +Your best friend Marta recently landed their dream job working with a local history museum's collections. +Knowing of your interests in programming, they confide in you about an issue at work for an upcoming exhibit on computing history. +A local university's math department had donated several boxes of historical printouts, but given the poor condition of the documents, the decision has been made to digitize the text. +However, the university's old printer had some quirks in how text was represented, and your friend could use your help to extract the data successfully. 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/ocr-numbers/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/ocr-numbers/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/ocr-numbers/gradlew b/exercises/practice/ocr-numbers/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/ocr-numbers/gradlew +++ b/exercises/practice/ocr-numbers/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/ocr-numbers/gradlew.bat b/exercises/practice/ocr-numbers/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/ocr-numbers/gradlew.bat +++ b/exercises/practice/ocr-numbers/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/ocr-numbers/src/test/java/OpticalCharacterReaderTest.java b/exercises/practice/ocr-numbers/src/test/java/OpticalCharacterReaderTest.java index fbe4c040c..6cc079c23 100644 --- a/exercises/practice/ocr-numbers/src/test/java/OpticalCharacterReaderTest.java +++ b/exercises/practice/ocr-numbers/src/test/java/OpticalCharacterReaderTest.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.Arrays; @@ -9,6 +10,7 @@ public class OpticalCharacterReaderTest { @Test + @DisplayName("Recognizes 0") public void testReaderRecognizesSingle0() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -23,6 +25,7 @@ public void testReaderRecognizesSingle0() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 1") public void testReaderRecognizesSingle1() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " ", @@ -36,6 +39,7 @@ public void testReaderRecognizesSingle1() { @Disabled("Remove to run test") @Test + @DisplayName("Unreadable but correctly sized inputs return ?") public void testReaderReturnsQuestionMarkForUnreadableButCorrectlySizedInput() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " ", @@ -49,6 +53,7 @@ public void testReaderReturnsQuestionMarkForUnreadableButCorrectlySizedInput() { @Disabled("Remove to run test") @Test + @DisplayName("Input with a number of lines that is not a multiple of four raises an error") public void testReaderThrowsExceptionWhenNumberOfInputLinesIsNotAMultipleOf4() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -63,6 +68,7 @@ public void testReaderThrowsExceptionWhenNumberOfInputLinesIsNotAMultipleOf4() { @Disabled("Remove to run test") @Test + @DisplayName("Input with a number of columns that is not a multiple of three raises an error") public void testReaderThrowsExceptionWhenNumberOfInputColumnsIsNotAMultipleOf3() { @@ -79,6 +85,7 @@ public void testReaderThrowsExceptionWhenNumberOfInputColumnsIsNotAMultipleOf3() @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 110101100") public void testReaderRecognizesBinarySequence110101100() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ _ _ _ ", @@ -92,6 +99,7 @@ public void testReaderRecognizesBinarySequence110101100() { @Disabled("Remove to run test") @Test + @DisplayName("Garbled numbers in a string are replaced with ?") public void testReaderReplacesUnreadableDigitsWithQuestionMarksWithinSequence() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ _ _ ", @@ -106,6 +114,7 @@ public void testReaderReplacesUnreadableDigitsWithQuestionMarksWithinSequence() @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 2") public void testReaderRecognizesSingle2() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -119,6 +128,7 @@ public void testReaderRecognizesSingle2() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 3") public void testReaderRecognizesSingle3() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -132,6 +142,7 @@ public void testReaderRecognizesSingle3() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 4") public void testReaderRecognizesSingle4() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " ", @@ -145,6 +156,7 @@ public void testReaderRecognizesSingle4() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 5") public void testReaderRecognizesSingle5() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -158,6 +170,7 @@ public void testReaderRecognizesSingle5() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 6") public void testReaderRecognizesSingle6() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -171,6 +184,7 @@ public void testReaderRecognizesSingle6() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 7") public void testReaderRecognizesSingle7() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -184,6 +198,7 @@ public void testReaderRecognizesSingle7() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 8") public void testReaderRecognizesSingle8() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -197,6 +212,7 @@ public void testReaderRecognizesSingle8() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes 9") public void testReaderRecognizesSingle9() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ ", @@ -210,6 +226,7 @@ public void testReaderRecognizesSingle9() { @Disabled("Remove to run test") @Test + @DisplayName("Recognizes string of decimal numbers") public void testReaderRecognizesSequence1234567890() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ _ _ _ _ _ _ _ ", @@ -223,6 +240,7 @@ public void testReaderRecognizesSequence1234567890() { @Disabled("Remove to run test") @Test + @DisplayName("Numbers separated by empty lines are recognized. Lines are joined by commas.") public void testReaderRecognizesAndCorrectlyFormatsMultiRowInput() { String parsedInput = new OpticalCharacterReader().parse(Arrays.asList( " _ _ ", diff --git a/exercises/practice/palindrome-products/.meta/config.json b/exercises/practice/palindrome-products/.meta/config.json index fcb0a2d63..5376b08cf 100644 --- a/exercises/practice/palindrome-products/.meta/config.json +++ b/exercises/practice/palindrome-products/.meta/config.json @@ -4,6 +4,7 @@ ], "contributors": [ "FridaTveit", + "jagdish-15", "jmrunkle", "jssander", "kytrinyx", diff --git a/exercises/practice/palindrome-products/.meta/tests.toml b/exercises/practice/palindrome-products/.meta/tests.toml index b34cb0d47..a3bc41750 100644 --- a/exercises/practice/palindrome-products/.meta/tests.toml +++ b/exercises/practice/palindrome-products/.meta/tests.toml @@ -1,12 +1,19 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [5cff78fe-cf02-459d-85c2-ce584679f887] -description = "finds the smallest palindrome from single digit factors" +description = "find the smallest palindrome from single digit factors" [0853f82c-5fc4-44ae-be38-fadb2cced92d] -description = "finds the largest palindrome from single digit factors" +description = "find the largest palindrome from single digit factors" [66c3b496-bdec-4103-9129-3fcb5a9063e1] description = "find the smallest palindrome from double digit factors" @@ -15,13 +22,13 @@ description = "find the smallest palindrome from double digit factors" description = "find the largest palindrome from double digit factors" [cecb5a35-46d1-4666-9719-fa2c3af7499d] -description = "find smallest palindrome from triple digit factors" +description = "find the smallest palindrome from triple digit factors" [edab43e1-c35f-4ea3-8c55-2f31dddd92e5] description = "find the largest palindrome from triple digit factors" [4f802b5a-9d74-4026-a70f-b53ff9234e4e] -description = "find smallest palindrome from four digit factors" +description = "find the smallest palindrome from four digit factors" [787525e0-a5f9-40f3-8cb2-23b52cf5d0be] description = "find the largest palindrome from four digit factors" @@ -37,3 +44,6 @@ description = "error result for smallest if min is more than max" [eeeb5bff-3f47-4b1e-892f-05829277bd74] description = "error result for largest if min is more than max" + +[16481711-26c4-42e0-9180-e2e4e8b29c23] +description = "smallest product does not use the smallest factor" 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/palindrome-products/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/palindrome-products/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/palindrome-products/gradlew b/exercises/practice/palindrome-products/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/palindrome-products/gradlew +++ b/exercises/practice/palindrome-products/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/palindrome-products/gradlew.bat b/exercises/practice/palindrome-products/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/palindrome-products/gradlew.bat +++ b/exercises/practice/palindrome-products/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/palindrome-products/src/test/java/PalindromeCalculatorTest.java b/exercises/practice/palindrome-products/src/test/java/PalindromeCalculatorTest.java index 8c8cb0cbb..211b4a9b8 100644 --- a/exercises/practice/palindrome-products/src/test/java/PalindromeCalculatorTest.java +++ b/exercises/practice/palindrome-products/src/test/java/PalindromeCalculatorTest.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.Arrays; @@ -15,6 +16,7 @@ public class PalindromeCalculatorTest { private PalindromeCalculator palindromeCalculator = new PalindromeCalculator(); @Test + @DisplayName("find the smallest palindrome from single digit factors") public void smallestPalindromeFromSingleDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -30,6 +32,7 @@ public void smallestPalindromeFromSingleDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the largest palindrome from single digit factors") public void largestPalindromeFromSingleDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -46,6 +49,7 @@ public void largestPalindromeFromSingleDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the smallest palindrome from double digit factors") public void largestPalindromeFromDoubleDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -62,6 +66,7 @@ public void largestPalindromeFromDoubleDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the largest palindrome from double digit factors") public void smallestPalindromeFromDoubleDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -78,6 +83,7 @@ public void smallestPalindromeFromDoubleDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the largest palindrome from triple digit factors") public void largestPalindromeFromTripleDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -94,6 +100,7 @@ public void largestPalindromeFromTripleDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the smallest palindrome from triple digit factors") public void smallestPalindromeFromTripleDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -110,6 +117,7 @@ public void smallestPalindromeFromTripleDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the smallest palindrome from four digit factors") public void smallestPalindromeFromQuadDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -126,6 +134,7 @@ public void smallestPalindromeFromQuadDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("find the largest palindrome from four digit factors") public void largestPalindromeFromQuadDigitFactors() { List> expected = Collections.unmodifiableList( Arrays.asList( @@ -142,6 +151,7 @@ public void largestPalindromeFromQuadDigitFactors() { @Disabled("Remove to run test") @Test + @DisplayName("empty result for smallest if no palindrome in the range") public void emtpyResultSmallestNoPalindromeInRange() { SortedMap>> palindromes = palindromeCalculator.getPalindromeProductsWithFactors(1002, @@ -151,6 +161,7 @@ public void emtpyResultSmallestNoPalindromeInRange() { @Disabled("Remove to run test") @Test + @DisplayName("empty result for largest if no palindrome in the range") public void emptyResultLargestNoPalindromeInRange() { SortedMap>> palindromes = palindromeCalculator.getPalindromeProductsWithFactors(15, @@ -160,6 +171,7 @@ public void emptyResultLargestNoPalindromeInRange() { @Disabled("Remove to run test") @Test + @DisplayName("error result for smallest if min is more than max") public void errorSmallestMinIsMoreThanMax() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -169,6 +181,7 @@ public void errorSmallestMinIsMoreThanMax() { @Disabled("Remove to run test") @Test + @DisplayName("error result for largest if min is more than max") public void errorLargestMinIsMoreThanMax() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -176,6 +189,23 @@ public void errorLargestMinIsMoreThanMax() { .withMessage("invalid input: min must be <= max"); } + @Disabled("Remove to run test") + @Test + @DisplayName("smallest product does not use the smallest factor") + public void smallestProductDoesNotUseTheSmallestFactor() { + List> expected = Collections.unmodifiableList( + Arrays.asList( + Arrays.asList(3297, 3333) + ) + ); + long expectedValue = 10988901L; + + SortedMap>> palindromes = palindromeCalculator.getPalindromeProductsWithFactors(3215, + 4000); + + checkPalindromeWithFactorsMatchesExpected(expected, expectedValue, palindromes, palindromes.firstKey()); + } + private void checkPalindromeWithFactorsMatchesExpected(List> expectedPalindromeFactors, long expectedValueOfPalindrome, 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/pangram/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/pangram/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/pangram/gradlew b/exercises/practice/pangram/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/pangram/gradlew +++ b/exercises/practice/pangram/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/pangram/gradlew.bat b/exercises/practice/pangram/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/pangram/gradlew.bat +++ b/exercises/practice/pangram/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/pangram/src/test/java/PangramCheckerTest.java b/exercises/practice/pangram/src/test/java/PangramCheckerTest.java index 63a83513f..981e40172 100644 --- a/exercises/practice/pangram/src/test/java/PangramCheckerTest.java +++ b/exercises/practice/pangram/src/test/java/PangramCheckerTest.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,60 +15,70 @@ public void setup() { } @Test + @DisplayName("empty sentence") public void emptySentenceIsNotPangram() { assertThat(pangramChecker.isPangram("")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("perfect lower case") public void perfectLowerCasePhraseIsPangram() { assertThat(pangramChecker.isPangram("abcdefghijklmnopqrstuvwxyz")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("only lower case") public void phraseWithOnlyLowerCaseIsPangram() { assertThat(pangramChecker.isPangram("the quick brown fox jumps over the lazy dog")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("missing the letter 'x'") public void phraseMissingCharacterXIsNotPangram() { assertThat(pangramChecker.isPangram("a quick movement of the enemy will jeopardize five gunboats")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("missing the letter 'h'") public void phraseMissingCharacterHIsNotPangram() { assertThat(pangramChecker.isPangram("five boxing wizards jump quickly at it")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("with underscores") public void phraseWithUnderscoresIsPangram() { assertThat(pangramChecker.isPangram("the_quick_brown_fox_jumps_over_the_lazy_dog")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("with numbers") public void phraseWithNumbersIsPangram() { assertThat(pangramChecker.isPangram("the 1 quick brown fox jumps over the 2 lazy dogs")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("missing letters replaced by numbers") public void phraseWithMissingLettersReplacedByNumbersIsNotPangram() { assertThat(pangramChecker.isPangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")).isFalse(); } @Disabled("Remove to run test") @Test + @DisplayName("mixed case and punctuation") public void phraseWithMixedCaseAndPunctuationIsPangram() { assertThat(pangramChecker.isPangram("\"Five quacking Zephyrs jolt my wax bed.\"")).isTrue(); } @Disabled("Remove to run test") @Test + @DisplayName("case insensitive") public void caseInsensitivePhraseIsNotPangram() { assertThat(pangramChecker.isPangram("abcdefghijklm ABCDEFGHIJKLM")).isFalse(); } diff --git a/exercises/practice/parallel-letter-frequency/.approaches/config.json b/exercises/practice/parallel-letter-frequency/.approaches/config.json new file mode 100644 index 000000000..dad02090c --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.approaches/config.json @@ -0,0 +1,27 @@ +{ + "introduction": { + "authors": [ + "masiljangajji" + ] + }, + "approaches": [ + { + "uuid": "dee2a79d-3e64-4220-b99f-55667549c12c", + "slug": "fork-join", + "title": "Fork/Join", + "blurb": "Parallel Computation Using Fork/Join", + "authors": [ + "masiljangajji" + ] + }, + { + "uuid": "75e9e93b-4da4-4474-8b6e-3c0cb9b3a9bb", + "slug": "parallel-stream", + "title": "Parallel Stream", + "blurb": "Parallel Computation Using Parallel Stream", + "authors": [ + "masiljangajji" + ] + } + ] +} diff --git a/exercises/practice/parallel-letter-frequency/.approaches/fork-join/content.md b/exercises/practice/parallel-letter-frequency/.approaches/fork-join/content.md new file mode 100644 index 000000000..de2cd7307 --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.approaches/fork-join/content.md @@ -0,0 +1,91 @@ +# `Fork/Join` + +```java +import java.util.Map; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.RecursiveTask; + +class ParallelLetterFrequency { + + List texts; + ConcurrentMap letterCount; + + ParallelLetterFrequency(String[] texts) { + this.texts = List.of(texts); + letterCount = new ConcurrentHashMap<>(); + } + + Map countLetters() { + if (texts.isEmpty()) { + return letterCount; + } + + ForkJoinPool forkJoinPool = new ForkJoinPool(); + forkJoinPool.invoke(new LetterCountTask(texts, 0, texts.size(), letterCount)); + forkJoinPool.shutdown(); + + return letterCount; + } + + private static class LetterCountTask extends RecursiveTask { + private static final int THRESHOLD = 10; + private final List texts; + private final int start; + private final int end; + private final ConcurrentMap letterCount; + + LetterCountTask(List texts, int start, int end, ConcurrentMap letterCount) { + this.texts = texts; + this.start = start; + this.end = end; + this.letterCount = letterCount; + } + + @Override + protected Void compute() { + if (end - start <= THRESHOLD) { + for (int i = start; i < end; i++) { + for (char c : texts.get(i).toLowerCase().toCharArray()) { + if (Character.isAlphabetic(c)) { + letterCount.merge(c, 1, Integer::sum); + } + } + } + } else { + int mid = (start + end) / 2; + LetterCountTask leftTask = new LetterCountTask(texts, start, mid, letterCount); + LetterCountTask rightTask = new LetterCountTask(texts, mid, end, letterCount); + invokeAll(leftTask, rightTask); + } + return null; + } + } +} +``` + +Using [`ConcurrentHashMap`][ConcurrentHashMap] ensures that frequency counting and updates are safely handled in a parallel environment. + +If there are no strings, a validation step prevents unnecessary processing. + +A [`ForkJoinPool`][ForkJoinPool] is then created. +The core of [`ForkJoinPool`][ForkJoinPool] is the Fork/Join mechanism, which divides tasks into smaller units and processes them in parallel. + +`THRESHOLD` is the criterion for task division. +If the range of texts exceeds the `THRESHOLD`, the task is divided into two subtasks, and [`invokeAll(leftTask, rightTask)`][invokeAll] is called to execute both tasks in parallel. +Each subtask in `LetterCountTask` will continue calling `compute()` (via `invokeAll(leftTask, rightTask)`) to divide itself further until the range is smaller than or equal to the `THRESHOLD`. +For tasks that are within the `THRESHOLD`, letter frequency is calculated. + +The [`Character.isAlphabetic`][isAlphabetic] method identifies all characters classified as alphabetic in Unicode, covering characters from various languages like English, Korean, Japanese, Chinese, etc., returning `true`. +Non-alphabetic characters, including numbers, special characters, and spaces, return `false`. + +Additionally, since uppercase and lowercase letters are treated as the same character (e.g., `A` and `a`), each character is converted to lowercase. + +After updating letter frequencies, the final map is returned. + +[ConcurrentHashMap]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html +[ForkJoinPool]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html +[isAlphabetic]: https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html#isAlphabetic-int- +[invokeAll]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html diff --git a/exercises/practice/parallel-letter-frequency/.approaches/fork-join/snippet.txt b/exercises/practice/parallel-letter-frequency/.approaches/fork-join/snippet.txt new file mode 100644 index 000000000..7e782eb30 --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.approaches/fork-join/snippet.txt @@ -0,0 +1,7 @@ +for (int i = start; i < end; i++) { + for (char c : texts.get(i).toLowerCase().toCharArray()) { + if (Character.isAlphabetic(c)) { + letterCount.merge(c, 1, Integer::sum); + } + } +} \ No newline at end of file diff --git a/exercises/practice/parallel-letter-frequency/.approaches/introduction.md b/exercises/practice/parallel-letter-frequency/.approaches/introduction.md new file mode 100644 index 000000000..8ca8fbf92 --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.approaches/introduction.md @@ -0,0 +1,142 @@ +# Introduction + +There are multiple ways to solve the Parallel Letter Frequency problem. +One approach is to use [`Stream.parallelStream`][stream], and another involves using [`ForkJoinPool`][ForkJoinPool]. + +## General guidance + +To count occurrences of items, a map data structure is often used, though arrays and lists can work as well. +A [`map`][map], being a key-value pair structure, is suitable for recording frequency by incrementing the value for each key. +If the data being counted has a limited range (e.g., `Characters` or `Integers`), an `int[] array` or [`List`][list] can be used to record frequencies. + +Parallel processing typically takes place in a multi-[`thread`][thread] environment. +The Java 8 [`stream`][stream] API provides methods that make parallel processing easier, including the [`parallelStream()`][stream] method. +With [`parallelStream()`][stream], developers can use the [`ForkJoinPool`][ForkJoinPool] model for workload division and parallel execution, without the need to manually manage threads or create custom thread pools. + +The [`ForkJoinPool`][ForkJoinPool] class, optimized for dividing and managing tasks, makes parallel processing efficient. +However, [`parallelStream()`][stream] uses the common [`ForkJoinPool`][ForkJoinPool] by default, meaning multiple [`parallelStream`][stream] instances share the same thread pool unless configured otherwise. + +As a result, parallel streams may interfere with each other when sharing this thread pool, potentially affecting performance. +Although this doesn’t directly impact solving the Parallel Letter Frequency problem, it may introduce issues when thread pool sharing causes conflicts in other applications. +Therefore, a custom [`ForkJoinPool`][ForkJoinPool] approach is also provided below. + +## Approach: `parallelStream` + +```java +import java.util.Map; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +class ParallelLetterFrequency { + + List texts; + ConcurrentMap letterCount; + + ParallelLetterFrequency(String[] texts) { + this.texts = List.of(texts); + letterCount = new ConcurrentHashMap<>(); + } + + Map countLetters() { + if (!letterCount.isEmpty() || texts.isEmpty()) { + return letterCount; + } + texts.parallelStream().forEach(text -> { + for (char c: text.toLowerCase().toCharArray()) { + if (Character.isAlphabetic(c)) { + letterCount.merge(c, 1, Integer::sum); + } + } + }); + return letterCount; + } + +} +``` + +For more information, check the [`parallelStream` approach][approach-parallel-stream]. + +## Approach: `Fork/Join` + +```java +import java.util.Map; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.RecursiveTask; + +class ParallelLetterFrequency { + + List texts; + ConcurrentMap letterCount; + + ParallelLetterFrequency(String[] texts) { + this.texts = List.of(texts); + letterCount = new ConcurrentHashMap<>(); + } + + Map countLetters() { + if (!letterCount.isEmpty() || texts.isEmpty()) { + return letterCount; + } + + ForkJoinPool forkJoinPool = new ForkJoinPool(); + forkJoinPool.invoke(new LetterCountTask(texts, 0, texts.size(), letterCount)); + forkJoinPool.shutdown(); + + return letterCount; + } + + private static class LetterCountTask extends RecursiveTask { + private static final int THRESHOLD = 10; + private final List texts; + private final int start; + private final int end; + private final ConcurrentMap letterCount; + + LetterCountTask(List texts, int start, int end, ConcurrentMap letterCount) { + this.texts = texts; + this.start = start; + this.end = end; + this.letterCount = letterCount; + } + + @Override + protected Void compute() { + if (end - start <= THRESHOLD) { + for (int i = start; i < end; i++) { + for (char c : texts.get(i).toLowerCase().toCharArray()) { + if (Character.isAlphabetic(c)) { + letterCount.merge(c, 1, Integer::sum); + } + } + } + } else { + int mid = (start + end) / 2; + LetterCountTask leftTask = new LetterCountTask(texts, start, mid, letterCount); + LetterCountTask rightTask = new LetterCountTask(texts, mid, end, letterCount); + invokeAll(leftTask, rightTask); + } + return null; + } + } +} + +``` + +For more information, check the [`fork/join` approach][approach-fork-join]. + +## Which approach to use? + +When tasks are simple or do not require a dedicated thread pool (such as in this case), the [`parallelStream`][stream] approach is recommended. +However, if the work is complex or there is a need to isolate thread pools from other concurrent tasks, the [`ForkJoinPool`][ForkJoinPool] approach is preferable. + +[thread]: https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html +[stream]: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html +[ForkJoinPool]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html +[map]: https://docs.oracle.com/javase/8/docs/api/?java/util/Map.html +[list]: https://docs.oracle.com/javase/8/docs/api/?java/util/List.html +[approach-parallel-stream]: https://exercism.org/tracks/java/exercises/parallel-letter-frequency/approaches/parallel-stream +[approach-fork-join]: https://exercism.org/tracks/java/exercises/parallel-letter-frequency/approaches/fork-join diff --git a/exercises/practice/parallel-letter-frequency/.approaches/parallel-stream/content.md b/exercises/practice/parallel-letter-frequency/.approaches/parallel-stream/content.md new file mode 100644 index 000000000..6b532b767 --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.approaches/parallel-stream/content.md @@ -0,0 +1,49 @@ +# `parallelStream` + +```java +import java.util.Map; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; + +class ParallelLetterFrequency { + + List texts; + ConcurrentMap letterCount; + + ParallelLetterFrequency(String[] texts) { + this.texts = List.of(texts); + letterCount = new ConcurrentHashMap<>(); + } + + Map countLetters() { + if (texts.isEmpty()) { + return letterCount; + } + texts.parallelStream().forEach(text -> { + for (char c: text.toLowerCase().toCharArray()) { + if (Character.isAlphabetic(c)) { + letterCount.merge(c, 1, Integer::sum); + } + } + }); + return letterCount; + } + +} +``` + +Using [`ConcurrentHashMap`][ConcurrentHashMap] ensures that frequency counting and updates are safely handled in a parallel environment. + +If there are no strings to process, a validation step avoids unnecessary computation. + +To calculate letter frequency, a parallel stream is used. +The [`Character.isAlphabetic`][isAlphabetic] method identifies all characters classified as alphabetic in Unicode, covering characters from various languages like English, Korean, Japanese, Chinese, etc., returning `true`. +Non-alphabetic characters, including numbers, special characters, and spaces, return `false`. + +Since we treat uppercase and lowercase letters as the same character (e.g., `A` and `a`), characters are converted to lowercase. + +After updating letter frequencies, the final map is returned. + +[ConcurrentHashMap]: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html +[isAlphabetic]: https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html#isAlphabetic-int- diff --git a/exercises/practice/parallel-letter-frequency/.approaches/parallel-stream/snippet.txt b/exercises/practice/parallel-letter-frequency/.approaches/parallel-stream/snippet.txt new file mode 100644 index 000000000..9cbb9cffa --- /dev/null +++ b/exercises/practice/parallel-letter-frequency/.approaches/parallel-stream/snippet.txt @@ -0,0 +1,7 @@ +texts.parallelStream().forEach(text -> { + for (char c: text.toLowerCase().toCharArray()) { + if (Character.isAlphabetic(c)) { + letterCount.merge(c, 1, Integer::sum); + } + } +}); \ No newline at end of file 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/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/parallel-letter-frequency/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/parallel-letter-frequency/gradlew b/exercises/practice/parallel-letter-frequency/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/parallel-letter-frequency/gradlew +++ b/exercises/practice/parallel-letter-frequency/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/parallel-letter-frequency/gradlew.bat b/exercises/practice/parallel-letter-frequency/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/parallel-letter-frequency/gradlew.bat +++ b/exercises/practice/parallel-letter-frequency/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/parallel-letter-frequency/src/test/java/ParallelLetterFrequencyTest.java b/exercises/practice/parallel-letter-frequency/src/test/java/ParallelLetterFrequencyTest.java index 3f812a51e..cce168b52 100644 --- a/exercises/practice/parallel-letter-frequency/src/test/java/ParallelLetterFrequencyTest.java +++ b/exercises/practice/parallel-letter-frequency/src/test/java/ParallelLetterFrequencyTest.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,7 +10,7 @@ public class ParallelLetterFrequencyTest { - private String calculateFrecuencies = + private String calculateFrequencies = "There, peeping among the cloud-wrack above a dark tower high up in the mountains," + "Sam saw a white star twinkle for a while." + "The beauty of it smote his heart," + @@ -205,6 +206,7 @@ public class ParallelLetterFrequencyTest { "- from the days of the Flood to the Schleswig-Holstein period."; @Test + @DisplayName("no texts") public void testNoTexts() { String[] input = {}; Map expectedOutput = new HashMap<>(); @@ -215,6 +217,7 @@ public void testNoTexts() { @Disabled("Remove to run test") @Test + @DisplayName("one text with one letter") public void testOneTextWithOneLetter() { String[] input = { "a" }; Map expectedOutput = new HashMap<>() { @@ -229,6 +232,7 @@ public void testOneTextWithOneLetter() { @Disabled("Remove to run test") @Test + @DisplayName("one text with multiple letters") public void testOneTextWithMultipleLetters() { String[] input = { "bbcccd" }; Map expectedOutput = new HashMap<>() { @@ -245,6 +249,7 @@ public void testOneTextWithMultipleLetters() { @Disabled("Remove to run test") @Test + @DisplayName("two texts with one letter") public void testTwoTextsWithOneLetter() { String[] input = { "e", "f" }; Map expectedOutput = new HashMap<>() { @@ -260,6 +265,7 @@ public void testTwoTextsWithOneLetter() { @Disabled("Remove to run test") @Test + @DisplayName("two texts with multiple letters") public void testTwoTextsWithMultipleLetters() { String[] input = { "ggh", "hhi" }; Map expectedOutput = new HashMap<>() { @@ -276,6 +282,7 @@ public void testTwoTextsWithMultipleLetters() { @Disabled("Remove to run test") @Test + @DisplayName("ignore letter casing") public void testIgnoreLetterCasing() { String[] input = { "m", "M" }; Map expectedOutput = new HashMap<>() { @@ -290,6 +297,7 @@ public void testIgnoreLetterCasing() { @Disabled("Remove to run test") @Test + @DisplayName("ignore whitespace") public void testIgnoreWhitespace() { String[] input = { " ", "\t", "\r\n" }; Map expectedOutput = new HashMap<>(); @@ -300,6 +308,7 @@ public void testIgnoreWhitespace() { @Disabled("Remove to run test") @Test + @DisplayName("ignore punctuation") public void testIgnorePunctuation() { String[] input = { "!", "?", ";", ",", "." }; Map expectedOutput = new HashMap<>(); @@ -310,6 +319,7 @@ public void testIgnorePunctuation() { @Disabled("Remove to run test") @Test + @DisplayName("ignore numbers") public void testIgnoreNumbers() { String[] input = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; Map expectedOutput = new HashMap<>(); @@ -320,6 +330,7 @@ public void testIgnoreNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("Unicode letters") public void testUnicodeLetters() { String[] input = { "本", "φ", "ほ", "ø" }; Map expectedOutput = new HashMap<>() { @@ -337,8 +348,9 @@ public void testUnicodeLetters() { @Disabled("Remove to run test") @Test + @DisplayName("combination of lower- and uppercase letters, punctuation and white space") public void testCombinationOfLowerAndUppercaseLettersPunctuationAndWhiteSpace() { - String[] input = { calculateFrecuencies }; + String[] input = {calculateFrequencies}; Map expectedOutput = new HashMap<>() { { put('a', 32); @@ -369,26 +381,10 @@ public void testCombinationOfLowerAndUppercaseLettersPunctuationAndWhiteSpace() assertThat(p.countLetters()).isEqualTo(expectedOutput); } - - @Disabled("Remove to run test") - @Test - public void testManySmallTexts() { - String[] input = new String[50]; - Arrays.fill(input, "abbccc"); - Map expectedOutput = new HashMap<>() { - { - put('a', 50); - put('b', 100); - put('c', 150); - } - }; - ParallelLetterFrequency p = new ParallelLetterFrequency(input); - - assertThat(p.countLetters()).isEqualTo(expectedOutput); - } @Disabled("Remove to run test") @Test + @DisplayName("large texts") public void testLargeTexts() { String[] input = { largeTexts1, largeTexts2, largeTexts3, largeTexts4 }; Map expectedOutput = new HashMap<>() { @@ -423,6 +419,24 @@ public void testLargeTexts() { ParallelLetterFrequency p = new ParallelLetterFrequency(input); assertThat(p.countLetters()).isEqualTo(expectedOutput); - } + } + + @Disabled("Remove to run test") + @Test + @DisplayName("many small texts") + public void testManySmallTexts() { + String[] input = new String[50]; + Arrays.fill(input, "abbccc"); + Map expectedOutput = new HashMap<>() { + { + put('a', 50); + put('b', 100); + put('c', 150); + } + }; + ParallelLetterFrequency p = new ParallelLetterFrequency(input); + + assertThat(p.countLetters()).isEqualTo(expectedOutput); + } } diff --git a/exercises/practice/pascals-triangle/.docs/introduction.md b/exercises/practice/pascals-triangle/.docs/introduction.md index 60b8ec30d..eab454e5a 100644 --- a/exercises/practice/pascals-triangle/.docs/introduction.md +++ b/exercises/practice/pascals-triangle/.docs/introduction.md @@ -13,7 +13,7 @@ Over the next hour, your teacher reveals some amazing things hidden in this tria - It contains the Fibonacci sequence. - If you color odd and even numbers differently, you get a beautiful pattern called the [Sierpiński triangle][wikipedia-sierpinski-triangle]. -The teacher implores you and your classmates to lookup other uses, and assures you that there are lots more! +The teacher implores you and your classmates to look up other uses, and assures you that there are lots more! At that moment, the school bell rings. You realize that for the past hour, you were completely absorbed in learning about Pascal's triangle. You quickly grab your laptop from your bag and go outside, ready to enjoy both the sunshine _and_ the wonders of Pascal's triangle. 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/pascals-triangle/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/pascals-triangle/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/pascals-triangle/gradlew b/exercises/practice/pascals-triangle/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/pascals-triangle/gradlew +++ b/exercises/practice/pascals-triangle/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/pascals-triangle/gradlew.bat b/exercises/practice/pascals-triangle/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/pascals-triangle/gradlew.bat +++ b/exercises/practice/pascals-triangle/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/pascals-triangle/src/test/java/PascalsTriangleGeneratorTest.java b/exercises/practice/pascals-triangle/src/test/java/PascalsTriangleGeneratorTest.java index c72c2dc07..becdb0825 100644 --- a/exercises/practice/pascals-triangle/src/test/java/PascalsTriangleGeneratorTest.java +++ b/exercises/practice/pascals-triangle/src/test/java/PascalsTriangleGeneratorTest.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 PascalsTriangleGeneratorTest { new PascalsTriangleGenerator(); @Test + @DisplayName("zero rows") public void testTriangleWithZeroRows() { int[][] expectedOutput = new int[][]{}; @@ -17,6 +19,7 @@ public void testTriangleWithZeroRows() { @Disabled("Remove to run test") @Test + @DisplayName("single row") public void testTriangleWithOneRow() { int[][] expectedOutput = new int[][]{ {1} @@ -27,6 +30,7 @@ public void testTriangleWithOneRow() { @Disabled("Remove to run test") @Test + @DisplayName("two rows") public void testTriangleWithTwoRows() { int[][] expectedOutput = new int[][]{ {1}, @@ -38,6 +42,7 @@ public void testTriangleWithTwoRows() { @Disabled("Remove to run test") @Test + @DisplayName("three rows") public void testTriangleWithThreeRows() { int[][] expectedOutput = new int[][]{ {1}, @@ -50,6 +55,7 @@ public void testTriangleWithThreeRows() { @Disabled("Remove to run test") @Test + @DisplayName("four rows") public void testTriangleWithFourRows() { int[][] expectedOutput = new int[][]{ {1}, @@ -63,6 +69,7 @@ public void testTriangleWithFourRows() { @Disabled("Remove to run test") @Test + @DisplayName("five rows") public void testTriangleWithFiveRows() { int[][] expectedOutput = new int[][]{ {1}, @@ -77,6 +84,7 @@ public void testTriangleWithFiveRows() { @Disabled("Remove to run test") @Test + @DisplayName("six rows") public void testTriangleWithSixRows() { int[][] expectedOutput = new int[][]{ {1}, @@ -92,6 +100,7 @@ public void testTriangleWithSixRows() { @Disabled("Remove to run test") @Test + @DisplayName("ten rows") public void testTriangleWithTenRows() { int[][] expectedOutput = new int[][]{ {1}, 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/perfect-numbers/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/perfect-numbers/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/perfect-numbers/gradlew b/exercises/practice/perfect-numbers/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/perfect-numbers/gradlew +++ b/exercises/practice/perfect-numbers/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/perfect-numbers/gradlew.bat b/exercises/practice/perfect-numbers/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/perfect-numbers/gradlew.bat +++ b/exercises/practice/perfect-numbers/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/perfect-numbers/src/test/java/NaturalNumberTest.java b/exercises/practice/perfect-numbers/src/test/java/NaturalNumberTest.java index b7387c4b1..58af24598 100644 --- a/exercises/practice/perfect-numbers/src/test/java/NaturalNumberTest.java +++ b/exercises/practice/perfect-numbers/src/test/java/NaturalNumberTest.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,66 +8,77 @@ public class NaturalNumberTest { @Test + @DisplayName("Smallest perfect number is classified correctly") public void testSmallPerfectNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(6).getClassification()).isEqualTo(Classification.PERFECT); } @Disabled("Remove to run test") @Test + @DisplayName("Medium perfect number is classified correctly") public void testMediumPerfectNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(28).getClassification()).isEqualTo(Classification.PERFECT); } @Disabled("Remove to run test") @Test + @DisplayName("Large perfect number is classified correctly") public void testLargePerfectNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(33550336).getClassification()).isEqualTo(Classification.PERFECT); } @Disabled("Remove to run test") @Test + @DisplayName("Smallest abundant number is classified correctly") public void testSmallAbundantNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(12).getClassification()).isEqualTo(Classification.ABUNDANT); } @Disabled("Remove to run test") @Test + @DisplayName("Medium abundant number is classified correctly") public void testMediumAbundantNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(30).getClassification()).isEqualTo(Classification.ABUNDANT); } @Disabled("Remove to run test") @Test + @DisplayName("Large abundant number is classified correctly") public void testLargeAbundantNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(33550335).getClassification()).isEqualTo(Classification.ABUNDANT); } @Disabled("Remove to run test") @Test + @DisplayName("Smallest prime deficient number is classified correctly") public void testSmallestPrimeDeficientNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(2).getClassification()).isEqualTo(Classification.DEFICIENT); } @Disabled("Remove to run test") @Test + @DisplayName("Smallest non-prime deficient number is classified correctly") public void testSmallestNonPrimeDeficientNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(4).getClassification()).isEqualTo(Classification.DEFICIENT); } @Disabled("Remove to run test") @Test + @DisplayName("Medium deficient number is classified as DEFICIENT") public void testMediumDeficientNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(32).getClassification()).isEqualTo(Classification.DEFICIENT); } @Disabled("Remove to run test") @Test + @DisplayName("Large deficient number is classified as DEFICIENT") public void testLargeDeficientNumberIsClassifiedCorrectly() { assertThat(new NaturalNumber(33550337).getClassification()).isEqualTo(Classification.DEFICIENT); } @Disabled("Remove to run test") @Test + @DisplayName("Edge case (no factors other than itself) is classified correctly") /* * The number 1 has no proper divisors (https://en.wikipedia.org/wiki/Divisor#Further_notions_and_facts), and the * additive identity is 0, so the aliquot sum of 1 should be 0. Hence 1 should be classified as deficient. @@ -77,6 +89,7 @@ public void testThatOneIsCorrectlyClassifiedAsDeficient() { @Disabled("Remove to run test") @Test + @DisplayName("Zero is rejected (as it is not a positive integer)") public void testThatNonNegativeIntegerIsRejected() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new NaturalNumber(0)) @@ -85,6 +98,7 @@ public void testThatNonNegativeIntegerIsRejected() { @Disabled("Remove to run test") @Test + @DisplayName("Negative integer is rejected (as it is not a positive integer)") public void testThatNegativeIntegerIsRejected() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new NaturalNumber(-1)) 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/phone-number/.docs/introduction.md b/exercises/practice/phone-number/.docs/introduction.md new file mode 100644 index 000000000..c4142c5af --- /dev/null +++ b/exercises/practice/phone-number/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +You've joined LinkLine, a leading communications company working to ensure reliable connections for everyone. +The team faces a big challenge: users submit phone numbers in all sorts of formats — dashes, spaces, dots, parentheses, and even prefixes. +Some numbers are valid, while others are impossible to use. + +Your mission is to turn this chaos into order. +You'll clean up valid numbers, formatting them appropriately for use in the system. +At the same time, you'll identify and filter out any invalid entries. + +The success of LinkLine's operations depends on your ability to separate the useful from the unusable. +Are you ready to take on the challenge and keep the connections running smoothly? 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/phone-number/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/phone-number/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/phone-number/gradlew b/exercises/practice/phone-number/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/phone-number/gradlew +++ b/exercises/practice/phone-number/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/phone-number/gradlew.bat b/exercises/practice/phone-number/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/phone-number/gradlew.bat +++ b/exercises/practice/phone-number/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/phone-number/src/test/java/PhoneNumberTest.java b/exercises/practice/phone-number/src/test/java/PhoneNumberTest.java index bfd7c6212..300415b65 100644 --- a/exercises/practice/phone-number/src/test/java/PhoneNumberTest.java +++ b/exercises/practice/phone-number/src/test/java/PhoneNumberTest.java @@ -1,12 +1,14 @@ -import static org.assertj.core.api.Assertions.assertThat; -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 static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + public class PhoneNumberTest { @Test + @DisplayName("cleans the number") public void cleansTheNumber() { String expectedNumber = "2234567890"; String actualNumber = new PhoneNumber("(223) 456-7890").getNumber(); @@ -16,6 +18,7 @@ public void cleansTheNumber() { @Disabled("Remove to run test") @Test + @DisplayName("cleans numbers with dots") public void cleansNumbersWithDots() { String expectedNumber = "2234567890"; String actualNumber = new PhoneNumber("223.456.7890").getNumber(); @@ -26,6 +29,7 @@ public void cleansNumbersWithDots() { @Disabled("Remove to run test") @Test + @DisplayName("cleans numbers with multiple spaces") public void cleansNumbersWithMultipleSpaces() { String expectedNumber = "2234567890"; String actualNumber = new PhoneNumber("223 456 7890 ").getNumber(); @@ -35,6 +39,7 @@ public void cleansNumbersWithMultipleSpaces() { @Disabled("Remove to run test") @Test + @DisplayName("invalid when 9 digits") public void invalidWhen9Digits() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -44,6 +49,7 @@ public void invalidWhen9Digits() { @Disabled("Remove to run test") @Test + @DisplayName("invalid when 11 digits does not start with a 1") public void invalidWhen11DigitsDoesNotStartWith1() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -53,6 +59,7 @@ public void invalidWhen11DigitsDoesNotStartWith1() { @Disabled("Remove to run test") @Test + @DisplayName("valid when 11 digits and starting with 1") public void validWhen11DigitsAndStartingWith1() { String expectedNumber = "2234567890"; String actualNumber = new PhoneNumber("12234567890").getNumber(); @@ -62,6 +69,7 @@ public void validWhen11DigitsAndStartingWith1() { @Disabled("Remove to run test") @Test + @DisplayName("valid when 11 digits and starting with 1 even with punctuation") public void validWhen11DigitsAndStartingWith1EvenWithPunctuation() { String expectedNumber = "2234567890"; String actualNumber = new PhoneNumber("+1 (223) 456-7890").getNumber(); @@ -71,6 +79,7 @@ public void validWhen11DigitsAndStartingWith1EvenWithPunctuation() { @Disabled("Remove to run test") @Test + @DisplayName("invalid when more than 11 digits") public void invalidWhenMoreThan11Digits() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("321234567890")) @@ -79,6 +88,7 @@ public void invalidWhenMoreThan11Digits() { @Disabled("Remove to run test") @Test + @DisplayName("invalid with letters") public void invalidWithLetters() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("523-abc-7890")) @@ -87,6 +97,7 @@ public void invalidWithLetters() { @Disabled("Remove to run test") @Test + @DisplayName("invalid with punctuations") public void invalidWithPunctuations() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("523-@:!-7890")) @@ -95,6 +106,7 @@ public void invalidWithPunctuations() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if area code starts with 0") public void invalidIfAreaCodeStartsWith0() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("(023) 456-7890")) @@ -103,6 +115,7 @@ public void invalidIfAreaCodeStartsWith0() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if area code starts with 1") public void invalidIfAreaCodeStartsWith1() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("(123) 456-7890")) @@ -111,6 +124,7 @@ public void invalidIfAreaCodeStartsWith1() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if exchange code starts with 0") public void invalidIfExchangeCodeStartsWith0() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("(223) 056-7890")) @@ -119,6 +133,7 @@ public void invalidIfExchangeCodeStartsWith0() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if exchange code starts with 1") public void invalidIfExchangeCodeStartsWith1() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("(223) 156-7890")) @@ -127,6 +142,7 @@ public void invalidIfExchangeCodeStartsWith1() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if area code starts with 0 on valid 11-digit number") public void invalidIfAreaCodeStartsWith0OnValid11DigitNumber() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("1 (023) 456-7890")) @@ -135,6 +151,7 @@ public void invalidIfAreaCodeStartsWith0OnValid11DigitNumber() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if area code starts with 1 on valid 11-digit number") public void invalidIfAreaCodeStartsWith1OnValid11DigitNumber() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("1 (123) 456-7890")) @@ -143,6 +160,7 @@ public void invalidIfAreaCodeStartsWith1OnValid11DigitNumber() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if exchange code starts with 0 on valid 11-digit number") public void invalidIfExchangeCodeStartsWith0OnValid11DigitNumber() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("1 (223) 056-7890")) @@ -151,6 +169,7 @@ public void invalidIfExchangeCodeStartsWith0OnValid11DigitNumber() { @Disabled("Remove to run test") @Test + @DisplayName("invalid if exchange code starts with 1 on valid 11-digit number") public void invalidIfExchangeCodeStartsWith1OnValid11DigitNumber() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> new PhoneNumber("1 (223) 156-7890")) 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..b58751cb9 --- /dev/null +++ b/exercises/practice/piecing-it-together/.meta/config.json @@ -0,0 +1,25 @@ +{ + "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" + ], + "invalidator": [ + "build.gradle" + ] + }, + "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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/piecing-it-together/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/piecing-it-together/gradlew b/exercises/practice/piecing-it-together/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/piecing-it-together/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/piecing-it-together/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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..791302fa3 --- /dev/null +++ b/exercises/practice/piecing-it-together/src/test/java/PiecingItTogetherTest.java @@ -0,0 +1,175 @@ +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.assertThatThrownBy; + +public class PiecingItTogetherTest { + private static final double DOUBLE_EQUALITY_TOLERANCE = 1e-9; + + @Test + @DisplayName("1000 pieces puzzle with 1.6 aspect ratio") + 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 + @DisplayName("square puzzle with 32 rows") + 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 + @DisplayName("400 pieces square puzzle with only inside pieces and aspect ratio") + 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 + @DisplayName("1500 pieces landscape puzzle with 30 rows and 1.6 aspect ratio") + 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 + @DisplayName("300 pieces portrait puzzle with 70 border pieces") + 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 + @DisplayName("puzzle with insufficient data") + 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 + @DisplayName("puzzle with contradictory data") + 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/practice/pig-latin/.meta/config.json b/exercises/practice/pig-latin/.meta/config.json index cf00d93e3..e0bbd7c25 100644 --- a/exercises/practice/pig-latin/.meta/config.json +++ b/exercises/practice/pig-latin/.meta/config.json @@ -4,6 +4,7 @@ "aadityakulkarni", "FridaTveit", "jackattack24", + "jagdish-15", "jmrunkle", "jtigger", "Kyle-Pu", diff --git a/exercises/practice/pig-latin/.meta/tests.toml b/exercises/practice/pig-latin/.meta/tests.toml index 49ce6e110..d524305b4 100644 --- a/exercises/practice/pig-latin/.meta/tests.toml +++ b/exercises/practice/pig-latin/.meta/tests.toml @@ -1,69 +1,79 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [11567f84-e8c6-4918-aedb-435f0b73db57] -description = "word beginning with a" +description = "ay is added to words that start with vowels -> word beginning with a" [f623f581-bc59-4f45-9032-90c3ca9d2d90] -description = "word beginning with e" +description = "ay is added to words that start with vowels -> word beginning with e" [7dcb08b3-23a6-4e8a-b9aa-d4e859450d58] -description = "word beginning with i" +description = "ay is added to words that start with vowels -> word beginning with i" [0e5c3bff-266d-41c8-909f-364e4d16e09c] -description = "word beginning with o" +description = "ay is added to words that start with vowels -> word beginning with o" [614ba363-ca3c-4e96-ab09-c7320799723c] -description = "word beginning with u" +description = "ay is added to words that start with vowels -> word beginning with u" [bf2538c6-69eb-4fa7-a494-5a3fec911326] -description = "word beginning with a vowel and followed by a qu" +description = "ay is added to words that start with vowels -> word beginning with a vowel and followed by a qu" [e5be8a01-2d8a-45eb-abb4-3fcc9582a303] -description = "word beginning with p" +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with p" [d36d1e13-a7ed-464d-a282-8820cb2261ce] -description = "word beginning with k" +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with k" [d838b56f-0a89-4c90-b326-f16ff4e1dddc] -description = "word beginning with x" +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with x" [bce94a7a-a94e-4e2b-80f4-b2bb02e40f71] -description = "word beginning with q without a following u" +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with q without a following u" + +[e59dbbe8-ccee-4619-a8e9-ce017489bfc0] +description = "first letter and ay are moved to the end of words that start with consonants -> word beginning with consonant and vowel containing qu" [c01e049a-e3e2-451c-bf8e-e2abb7e438b8] -description = "word beginning with ch" +description = "some letter clusters are treated like a single consonant -> word beginning with ch" [9ba1669e-c43f-4b93-837a-cfc731fd1425] -description = "word beginning with qu" +description = "some letter clusters are treated like a single consonant -> word beginning with qu" [92e82277-d5e4-43d7-8dd3-3a3b316c41f7] -description = "word beginning with qu and a preceding consonant" +description = "some letter clusters are treated like a single consonant -> word beginning with qu and a preceding consonant" [79ae4248-3499-4d5b-af46-5cb05fa073ac] -description = "word beginning with th" +description = "some letter clusters are treated like a single consonant -> word beginning with th" [e0b3ae65-f508-4de3-8999-19c2f8e243e1] -description = "word beginning with thr" +description = "some letter clusters are treated like a single consonant -> word beginning with thr" [20bc19f9-5a35-4341-9d69-1627d6ee6b43] -description = "word beginning with sch" +description = "some letter clusters are treated like a single consonant -> word beginning with sch" [54b796cb-613d-4509-8c82-8fbf8fc0af9e] -description = "word beginning with yt" +description = "some letter clusters are treated like a single vowel -> word beginning with yt" [8c37c5e1-872e-4630-ba6e-d20a959b67f6] -description = "word beginning with xr" +description = "some letter clusters are treated like a single vowel -> word beginning with xr" [a4a36d33-96f3-422c-a233-d4021460ff00] -description = "y is treated like a consonant at the beginning of a word" +description = "position of y in a word determines if it is a consonant or a vowel -> y is treated like a consonant at the beginning of a word" [adc90017-1a12-4100-b595-e346105042c7] -description = "y is treated like a vowel at the end of a consonant cluster" +description = "position of y in a word determines if it is a consonant or a vowel -> y is treated like a vowel at the end of a consonant cluster" [29b4ca3d-efe5-4a95-9a54-8467f2e5e59a] -description = "y as second letter in two letter word" +description = "position of y in a word determines if it is a consonant or a vowel -> y as second letter in two letter word" [44616581-5ce3-4a81-82d0-40c7ab13d2cf] -description = "a whole phrase" +description = "phrases are translated -> a whole phrase" 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/pig-latin/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/pig-latin/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/pig-latin/gradlew b/exercises/practice/pig-latin/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/pig-latin/gradlew +++ b/exercises/practice/pig-latin/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/pig-latin/gradlew.bat b/exercises/practice/pig-latin/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/pig-latin/gradlew.bat +++ b/exercises/practice/pig-latin/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/pig-latin/src/test/java/PigLatinTranslatorTest.java b/exercises/practice/pig-latin/src/test/java/PigLatinTranslatorTest.java index 366a71e2f..0a7466dcd 100644 --- a/exercises/practice/pig-latin/src/test/java/PigLatinTranslatorTest.java +++ b/exercises/practice/pig-latin/src/test/java/PigLatinTranslatorTest.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,132 +15,161 @@ public void setup() { } @Test + @DisplayName("word beginning with a") public void testWordBeginningWithA() { assertThat(pigLatinTranslator.translate("apple")).isEqualTo("appleay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with e") public void testWordBeginningWithE() { assertThat(pigLatinTranslator.translate("ear")).isEqualTo("earay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with i") public void testWordBeginningWithI() { assertThat(pigLatinTranslator.translate("igloo")).isEqualTo("iglooay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with o") public void testWordBeginningWithO() { assertThat(pigLatinTranslator.translate("object")).isEqualTo("objectay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with u") public void testWordBeginningWithU() { assertThat(pigLatinTranslator.translate("under")).isEqualTo("underay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with a vowel and followed by a qu") public void testWordBeginningWithVowelAndFollowedByQu() { assertThat(pigLatinTranslator.translate("equal")).isEqualTo("equalay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with p") public void testWordBeginningWithP() { assertThat(pigLatinTranslator.translate("pig")).isEqualTo("igpay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with k") public void testWordBeginningWithK() { assertThat(pigLatinTranslator.translate("koala")).isEqualTo("oalakay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with x") public void testWordBeginningWithX() { assertThat(pigLatinTranslator.translate("xenon")).isEqualTo("enonxay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with q without a following u") public void testWordBeginningWithQWithoutAFollowingU() { assertThat(pigLatinTranslator.translate("qat")).isEqualTo("atqay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with consonant and vowel containing qu") + public void testWordBeginningWithConsonantAndVowelContainingQu() { + assertThat(pigLatinTranslator.translate("liquid")).isEqualTo("iquidlay"); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("word beginning with ch") public void testChTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("chair")).isEqualTo("airchay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with qu") public void testQuTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("queen")).isEqualTo("eenquay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with qu and a preceding consonant") public void testQuAndAPrecedingConsonantTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("square")).isEqualTo("aresquay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with th") public void testThTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("therapy")).isEqualTo("erapythay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with thr") public void testThrTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("thrush")).isEqualTo("ushthray"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with sch") public void testSchTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("school")).isEqualTo("oolschay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with yt") public void testYtTreatedLikeAVowelAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("yttria")).isEqualTo("yttriaay"); } @Disabled("Remove to run test") @Test + @DisplayName("word beginning with xr") public void testXrTreatedLikeAVowelAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("xray")).isEqualTo("xrayay"); } @Disabled("Remove to run test") @Test + @DisplayName("y is treated like a consonant at the beginning of a word") public void testYTreatedLikeAConsonantAtTheBeginningOfAWord() { assertThat(pigLatinTranslator.translate("yellow")).isEqualTo("ellowyay"); } @Disabled("Remove to run test") @Test + @DisplayName("y is treated like a vowel at the end of a consonant cluster") public void testYTreatedLikeAVowelAtTheEndOfAConsonantCluster() { assertThat(pigLatinTranslator.translate("rhythm")).isEqualTo("ythmrhay"); } @Disabled("Remove to run test") @Test + @DisplayName("y as second letter in two letter word") public void testYAsSecondLetterInTwoLetterWord() { assertThat(pigLatinTranslator.translate("my")).isEqualTo("ymay"); } @Disabled("Remove to run test") @Test + @DisplayName("a whole phrase") public void testAWholePhrase() { assertThat(pigLatinTranslator.translate("quick fast run")).isEqualTo("ickquay astfay unray"); } diff --git a/exercises/practice/poker/.meta/config.json b/exercises/practice/poker/.meta/config.json index d3e25334e..98bae76ac 100644 --- a/exercises/practice/poker/.meta/config.json +++ b/exercises/practice/poker/.meta/config.json @@ -6,10 +6,12 @@ "aadityakulkarni", "FridaTveit", "hgvanpariya", + "jagdish-15", "jmrunkle", "katmpatz", "kytrinyx", "lemoncurry", + "MartinDekanovsky", "mirkoperillo", "msomji", "muzimuzhi", @@ -27,8 +29,6 @@ "src/test/java/PokerTest.java" ], "example": [ - ".meta/src/reference/java/Card.java", - ".meta/src/reference/java/Hand.java", ".meta/src/reference/java/Poker.java" ], "invalidator": [ diff --git a/exercises/practice/poker/.meta/src/reference/java/Card.java b/exercises/practice/poker/.meta/src/reference/java/Card.java deleted file mode 100644 index e07c671d8..000000000 --- a/exercises/practice/poker/.meta/src/reference/java/Card.java +++ /dev/null @@ -1,28 +0,0 @@ -class Card { - private int rank; - private int suit; - - Card(String card) { - rank = parseRank(card); - suit = parseSuit(card); - } - - int getRank() { - return rank; - } - - int getSuit() { - return suit; - } - - private int parseRank(String card) { - if (card.substring(0, 2).equals("10")) { - return 10; - } - return "..23456789TJQKA".indexOf(card.charAt(0)); - } - - private int parseSuit(String card) { - return ".HSDC".indexOf(card.charAt(card.length() - 1)); - } -} diff --git a/exercises/practice/poker/.meta/src/reference/java/Hand.java b/exercises/practice/poker/.meta/src/reference/java/Hand.java deleted file mode 100644 index dc60b44a2..000000000 --- a/exercises/practice/poker/.meta/src/reference/java/Hand.java +++ /dev/null @@ -1,148 +0,0 @@ -import java.util.*; -import java.util.stream.Collectors; - -class Hand { - private String input; - private int score; - - Hand(String hand) { - this.input = hand; - this.score = scoreHand(parseCards(hand)); - } - - int getScore() { - return score; - } - - String getInput() { - return input; - } - - private List parseCards(String hand) { - ArrayList parsedCards = new ArrayList<>(); - String[] cardsToParse = hand.split(" "); - for (String cardToParse : cardsToParse) { - parsedCards.add(new Card(cardToParse)); - } - return parsedCards; - } - - private Map getFrequencyMap(List cards) { - Map frequencyMap = new HashMap<>(); - for (Card c : cards) { - if (frequencyMap.containsKey(c.getRank())) { - frequencyMap.put(c.getRank(), frequencyMap.get(c.getRank()) + 1); - } else { - frequencyMap.put(c.getRank(), 1); - } - } - return frequencyMap; - } - - private int scoreHand(List cards) { - List cardsByRank = cards.stream() - .sorted(Comparator.comparing(Card::getRank)) - .unordered() - .collect(Collectors.toList()); - - Map frequencyMap = getFrequencyMap(cards); - List ranks = frequencyMap - .entrySet() - .stream() - .map(Map.Entry::getKey) - .sorted(Comparator.reverseOrder()) - .collect(Collectors.toList()); - frequencyMap = frequencyMap - .entrySet() - .stream() - .sorted(Map.Entry.comparingByValue(Collections.reverseOrder())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - List rankCounts = frequencyMap - .entrySet() - .stream() - .map(Map.Entry::getValue) - .collect(Collectors.toList()); - List suits = cards - .stream() - .map(Card::getSuit) - .collect(Collectors.toList()); - - return calculatedScore(frequencyMap, cardsByRank, ranks, rankCounts, suits); - } - - private int calculatedScore(Map frequencyMap, List cardsByRank, List ranks, - List rankCounts, List suits) { - if (ranks.equals(Arrays.asList(14, 5, 4, 3, 2))) { - ranks = Arrays.asList(5, 4, 3, 2, 1); - } - - boolean flush = suits - .stream() - .distinct() - .count() == 1; - boolean straight = ranks.stream().distinct().count() == 5 - && ranks.get(0) - ranks.get(4) == 4; - - Iterator iteratorOverFrequencies = frequencyMap.keySet().iterator(); - - int highestFrequency = iteratorOverFrequencies.next(); - - if (straight && flush) { - return 800 + highestFrequency; - } - if (rankCounts.equals(Arrays.asList(4, 1))) { - return 700 + cardsByRank.get(0).getRank(); - } - if (rankCounts.equals(Arrays.asList(3, 2))) { - int triplet = 0; - int pair = 0; - for (Integer key : frequencyMap.keySet()) { - if (frequencyMap.get(key) == 2) { - pair = (int) key; - } - if (frequencyMap.get(key) == 3) { - triplet = 3 * (int) key; - } - } - return 600 + 3 * triplet + pair; - } - if (flush) { - return 500 + highestFrequency; - } - if (straight) { - int maxValue = Collections.max(ranks); - return 400 + maxValue; - } - if (rankCounts.equals(Arrays.asList(3, 1, 1))) { - List uniqueCards = new ArrayList(); - int triplet = 0; - for (Integer key : frequencyMap.keySet()) { - if (frequencyMap.get(key) == 1) { - uniqueCards.add((int) key); - } - if (frequencyMap.get(key) == 3) { - triplet = 3 * (int) key; - } - } - return 300 + triplet + Collections.max(uniqueCards); - } - if (rankCounts.equals(Arrays.asList(2, 2, 1))) { - int productsOfFrequencyAndValue = 0; - for (Integer key : frequencyMap.keySet()) { - int frequencyKey = (int) key; - int frequencyValue = frequencyMap.get(key); - productsOfFrequencyAndValue += frequencyKey * frequencyValue; - } - return 200 + productsOfFrequencyAndValue + 2 * Math.max(highestFrequency, iteratorOverFrequencies.next()); - } - if (rankCounts.equals(Arrays.asList(2, 1, 1, 1))) { - return 100 + highestFrequency; - } - ranks.sort(Comparator.naturalOrder()); - int result = 0; - for (int i = 0; i < ranks.size(); i++) { - result += ranks.get(0) * (i + 1); - } - return result + ranks.get(ranks.size() - 1); - } -} diff --git a/exercises/practice/poker/.meta/src/reference/java/Poker.java b/exercises/practice/poker/.meta/src/reference/java/Poker.java index e80acbad6..e8181bf05 100644 --- a/exercises/practice/poker/.meta/src/reference/java/Poker.java +++ b/exercises/practice/poker/.meta/src/reference/java/Poker.java @@ -1,34 +1,132 @@ -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; class Poker { - private final List bestHands; - Poker(final List hands) { - bestHands = bestHands(hands); + private List hands; + private List numericalValues; + private List counts; + + public Poker(List hands) { + this.hands = hands; } - List getBestHands() { + public List getBestHands() { + List bestHands = new ArrayList<>(); + bestHands.add(hands.get(0)); + + for (int i = 1; i < hands.size(); i++) { + if (getHandRank(hands.get(i)) > getHandRank(bestHands.get(0))) { + bestHands.set(0, hands.get(i)); + } else if (getHandRank(hands.get(i)) == getHandRank(bestHands.get(0))) { + getHandRank(bestHands.get(0)); + List firstHand = counts; + + getHandRank(hands.get(i)); + List secondHand = counts; + + if (firstHand.equals(secondHand)) { + bestHands.add(hands.get(i)); + } else { + for (int j = 4; j >= 2; j--) { + if (firstHand.contains(j) && secondHand.contains(j)) { + if (firstHand.lastIndexOf(j) < secondHand.lastIndexOf(j)) { + bestHands.set(0, hands.get(i)); + break; + } else if (firstHand.lastIndexOf(j) > secondHand.lastIndexOf(j)) { + break; + } else if (firstHand.lastIndexOf(j) == secondHand.lastIndexOf(j) && j == 2) { + if (firstHand.indexOf(j) < secondHand.indexOf(j)) { + bestHands.set(0, hands.get(i)); + } + } + } + } + for (int k = firstHand.size() - 1; k >= 0; k--) { + if (firstHand.get(k) <= 1 && secondHand.get(k) <= 1) { + if (firstHand.get(k) < secondHand.get(k)) { + bestHands.set(0, hands.get(i)); + break; + } else if (firstHand.get(k) > secondHand.get(k)) { + break; + } + } + } + } + } + } return bestHands; } - private List bestHands(final List hands) { - ArrayList scoredHands = new ArrayList<>(); + public int getHandRank(String hand) { + String[] cards = hand.split(" "); + List values = new ArrayList<>(); + List suits = new ArrayList<>(); + for (String card : cards) { + if (card.length() == 2) { + values.add(card.substring(0, 1)); + suits.add(card.substring(1)); + } else { + values.add(card.substring(0, 2)); + suits.add(card.substring(2)); + } + } + + for (int i = 0; i < values.size(); i++) { + switch (values.get(i)) { + case "J" -> values.set(i, "11"); + case "Q" -> values.set(i, "12"); + case "K" -> values.set(i, "13"); + case "A" -> { + if (values.contains("2") && values.contains("3") && values.contains("4") && values.contains("5")) { + values.set(i, "1"); + } else { + values.set(i, "14"); + } + } + } + } + + numericalValues = new ArrayList<>(); + for (String value : values) { + numericalValues.add(Integer.valueOf(value)); + } + Collections.sort(numericalValues); - for (String s : hands) { - scoredHands.add(new Hand(s)); + List possibleValues = new ArrayList<>(); + counts = new ArrayList<>(); + for (int i = 1; i <= 14; i++) { + counts.add(i - 1, countOccurrences(i)); + possibleValues.add(i); } - Optional maxScoreIfAny = scoredHands - .stream() - .map(Hand::getScore) - .max(Comparator.naturalOrder()); - return maxScoreIfAny - .map(maxScore -> scoredHands - .stream() - .filter(h -> h.getScore() == maxScore) - .map(Hand::getInput) - .collect(Collectors.toList())) - .orElseGet(Collections::emptyList); + boolean isStraight = Collections.indexOfSubList(possibleValues, numericalValues) != -1; + + boolean isFlush = suits.stream().distinct().count() == 1; + + if (isStraight & isFlush) { + return 8; // straight flush + } else if (counts.contains(4)) { + return 7; // four of a kind + } else if (counts.contains(3) && counts.contains(2)) { + return 6; // full house + } else if (isFlush) { + return 5; // flush + } else if (isStraight) { + return 4; // straight + } else if (counts.contains(3)) { + return 3; // three of a kind + } else if (counts.stream().filter(value -> value == 2).count() == 2) { + return 2; // two pair + } else if (counts.contains(2)) { + return 1; // pair + } else { + return 0; // high card + } } + + public int countOccurrences(int valueToFind) { + return (int) numericalValues.stream().filter(value -> value.equals(valueToFind)).count(); + } } diff --git a/exercises/practice/poker/.meta/tests.toml b/exercises/practice/poker/.meta/tests.toml index 194b314d6..2e654ef63 100644 --- a/exercises/practice/poker/.meta/tests.toml +++ b/exercises/practice/poker/.meta/tests.toml @@ -1,6 +1,13 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [161f485e-39c2-4012-84cf-bec0c755b66c] description = "single hand always wins" @@ -14,12 +21,18 @@ description = "a tie has multiple winners" [61ed83a9-cfaa-40a5-942a-51f52f0a8725] description = "multiple hands with the same high cards, tie compares next highest ranked, down to last card" +[da01becd-f5b0-4342-b7f3-1318191d0580] +description = "winning high card hand also has the lowest card" + [f7175a89-34ff-44de-b3d7-f6fd97d1fca4] description = "one pair beats high card" [e114fd41-a301-4111-a9e7-5a7f72a76561] description = "highest pair wins" +[b3acd3a7-f9fa-4647-85ab-e0a9e07d1365] +description = "both hands have the same pair, high card wins" + [935bb4dc-a622-4400-97fa-86e7d06b1f76] description = "two pairs beats one pair" @@ -32,6 +45,12 @@ description = "both hands have two pairs, with the same highest ranked pair, tie [15a7a315-0577-47a3-9981-d6cf8e6f387b] description = "both hands have two identically ranked pairs, tie goes to remaining card (kicker)" +[f761e21b-2560-4774-a02a-b3e9366a51ce] +description = "both hands have two pairs that add to the same value, win goes to highest pair" + +[fc6277ac-94ac-4078-8d39-9d441bc7a79e] +description = "two pairs first ranked by largest pair" + [21e9f1e6-2d72-49a1-a930-228e5e0195dc] description = "three of a kind beats two pair" @@ -40,6 +59,11 @@ description = "both hands have three of a kind, tie goes to highest ranked tripl [eb856cc2-481c-4b0d-9835-4d75d07a5d9d] description = "with multiple decks, two players can have same three of a kind, ties go to highest remaining cards" +include = false + +[26a4a7d4-34a2-4f18-90b4-4a8dd35d2bb1] +description = "with multiple decks, two players can have same three of a kind, ties go to highest remaining cards" +reimplements = "eb856cc2-481c-4b0d-9835-4d75d07a5d9d" [a858c5d9-2f28-48e7-9980-b7fa04060a60] description = "a straight beats three of a kind" @@ -50,6 +74,9 @@ description = "aces can end a straight (10 J Q K A)" [76856b0d-35cd-49ce-a492-fe5db53abc02] description = "aces can start a straight (A 2 3 4 5)" +[e214b7df-dcba-45d3-a2e5-342d8c46c286] +description = "aces cannot be in the middle of a straight (Q K A 2 3)" + [6980c612-bbff-4914-b17a-b044e4e69ea1] description = "both hands with a straight, tie goes to highest ranked card" @@ -61,6 +88,11 @@ description = "flush beats a straight" [4d90261d-251c-49bd-a468-896bf10133de] description = "both hands have a flush, tie goes to high card, down to the last one if necessary" +include = false + +[e04137c5-c19a-4dfc-97a1-9dfe9baaa2ff] +description = "both hands have a flush, tie goes to high card, down to the last one if necessary" +reimplements = "4d90261d-251c-49bd-a468-896bf10133de" [3a19361d-8974-455c-82e5-f7152f5dba7c] description = "full house beats a flush" @@ -83,5 +115,17 @@ description = "with multiple decks, both hands with identical four of a kind, ti [923bd910-dc7b-4f7d-a330-8b42ec10a3ac] description = "straight flush beats four of a kind" +[d9629e22-c943-460b-a951-2134d1b43346] +description = "aces can end a straight flush (10 J Q K A)" + +[05d5ede9-64a5-4678-b8ae-cf4c595dc824] +description = "aces can start a straight flush (A 2 3 4 5)" + +[ad655466-6d04-49e8-a50c-0043c3ac18ff] +description = "aces cannot be in the middle of a straight flush (Q K A 2 3)" + [d0927f70-5aec-43db-aed8-1cbd1b6ee9ad] -description = "both hands have straight flush, tie goes to highest-ranked card" +description = "both hands have a straight flush, tie goes to highest-ranked card" + +[be620e09-0397-497b-ac37-d1d7a4464cfc] +description = "even though an ace is usually high, a 5-high straight flush is the lowest-scoring straight flush" 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/poker/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/poker/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/poker/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/poker/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/poker/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/poker/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/poker/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/poker/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/poker/gradlew b/exercises/practice/poker/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/poker/gradlew +++ b/exercises/practice/poker/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/poker/gradlew.bat b/exercises/practice/poker/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/poker/gradlew.bat +++ b/exercises/practice/poker/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/poker/src/test/java/PokerTest.java b/exercises/practice/poker/src/test/java/PokerTest.java index 4c4bd8529..367b776df 100644 --- a/exercises/practice/poker/src/test/java/PokerTest.java +++ b/exercises/practice/poker/src/test/java/PokerTest.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; @@ -8,6 +9,7 @@ public class PokerTest { @Test + @DisplayName("single hand always wins") public void oneHand() { String hand = "4S 5S 7H 8D JC"; assertThat(new Poker(Collections.singletonList(hand)).getBestHands()) @@ -16,6 +18,7 @@ public void oneHand() { @Disabled("Remove to run test") @Test + @DisplayName("highest card out of all hands wins") public void highestCardWins() { String highest8 = "4D 5S 6S 8D 3C"; String highest10 = "2S 4C 7S 9H 10H"; @@ -26,6 +29,7 @@ public void highestCardWins() { @Disabled("Remove to run test") @Test + @DisplayName("a tie has multiple winners") public void tieHasMultipleWinners() { String highest8 = "4D 5S 6S 8D 3C"; String highest10 = "2S 4C 7S 9H 10H"; @@ -37,6 +41,7 @@ public void tieHasMultipleWinners() { @Disabled("Remove to run test") @Test + @DisplayName("multiple hands with the same high cards, tie compares next highest ranked, down to last card") public void sameHighCards() { String nextHighest3 = "3S 5H 6S 8D 7H"; String nextHighest2 = "2S 5D 6D 8C 7S"; @@ -46,6 +51,17 @@ public void sameHighCards() { @Disabled("Remove to run test") @Test + @DisplayName("winning high card hand also has the lowest card") + public void winningWithLowestCard() { + String lowest2 = "2S 5H 6S 8D 7H"; + String lowest3 = "3S 4D 6D 8C 7S"; + assertThat(new Poker(Arrays.asList(lowest2, lowest3)).getBestHands()) + .containsExactly(lowest2); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("one pair beats high card") public void nothingVsOnePair() { String nothing = "4S 5H 6C 8D KH"; String pairOf4 = "2S 4H 6S 4D JH"; @@ -55,6 +71,7 @@ public void nothingVsOnePair() { @Disabled("Remove to run test") @Test + @DisplayName("highest pair wins") public void twoPairs() { String pairOf2 = "4S 2H 6S 2D JH"; String pairOf4 = "2S 4H 6C 4D JD"; @@ -64,6 +81,17 @@ public void twoPairs() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have the same pair, high card wins") + public void samePair() { + String pairOf4Lower = "4H 4S AH JC 3D"; + String pairOf4Higher = "4C 4D AS 5D 6C"; + assertThat(new Poker(Arrays.asList(pairOf4Lower, pairOf4Higher)).getBestHands()) + .containsExactly(pairOf4Lower); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("two pairs beats one pair") public void onePairVsDoublePair() { String pairOf8 = "2S 8H 6S 8D JH"; String doublePair = "4S 5H 4C 8C 5C"; @@ -73,6 +101,7 @@ public void onePairVsDoublePair() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have two pairs, highest ranked pair wins") public void twoDoublePairs() { String doublePair2And8 = "2S 8H 2D 8D 3H"; String doublePair4And5 = "4S 5H 4C 8S 5D"; @@ -82,6 +111,7 @@ public void twoDoublePairs() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have two pairs, with the same highest ranked pair, tie goes to low pair") public void sameHighestPair() { String doublePair2AndQ = "2S QS 2C QD JH"; String doublePairJAndQ = "JD QH JS 8D QC"; @@ -91,6 +121,7 @@ public void sameHighestPair() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have two identically ranked pairs, tie goes to remaining card (kicker)") public void identicallyRankedPairs() { String kicker8 = "JD QH JS 8D QC"; String kicker2 = "JS QS JC 2D QD"; @@ -100,6 +131,27 @@ public void identicallyRankedPairs() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have two pairs that add to the same value, win goes to highest pair") + public void twoPairsAddingToSameValue() { + String doublePair6And3 = "6S 6H 3S 3H AS"; + String doublePair7And2 = "7H 7S 2H 2S AC"; + assertThat(new Poker(Arrays.asList(doublePair6And3, doublePair7And2)).getBestHands()) + .containsExactly(doublePair7And2); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("two pairs first ranked by largest pair") + public void rankedByLargestPair() { + String doublePairs5And4 = "5C 2S 5S 4H 4C"; + String doublePairs6And2 = "6S 2S 6H 7C 2C"; + assertThat(new Poker(Arrays.asList(doublePairs5And4, doublePairs6And2)).getBestHands()) + .containsExactly(doublePairs6And2); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("three of a kind beats two pair") public void doublePairVsThree() { String doublePair2And8 = "2S 8H 2H 8D JH"; String threeOf4 = "4S 5H 4C 8S 4H"; @@ -109,6 +161,7 @@ public void doublePairVsThree() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have three of a kind, tie goes to highest ranked triplet") public void twoThrees() { String threeOf2 = "2S 2H 2C 8D JH"; String threeOf1 = "4S AH AS 8C AD"; @@ -118,8 +171,9 @@ public void twoThrees() { @Disabled("Remove to run test") @Test + @DisplayName("with multiple decks, two players can have same three of a kind, ties go to highest remaining cards") public void sameThreesMultipleDecks() { - String remainingCard7 = "4S AH AS 7C AD"; + String remainingCard7 = "5S AH AS 7C AD"; String remainingCard8 = "4S AH AS 8C AD"; assertThat(new Poker(Arrays.asList(remainingCard7, remainingCard8)).getBestHands()) .containsExactly(remainingCard8); @@ -127,6 +181,7 @@ public void sameThreesMultipleDecks() { @Disabled("Remove to run test") @Test + @DisplayName("a straight beats three of a kind") public void threeVsStraight() { String threeOf4 = "4S 5H 4C 8D 4H"; String straight = "3S 4D 2S 6D 5C"; @@ -136,6 +191,7 @@ public void threeVsStraight() { @Disabled("Remove to run test") @Test + @DisplayName("aces can end a straight (10 J Q K A)") public void acesCanEndAStraight() { String hand = "4S 5H 4C 8D 4H"; String straightEndsA = "10D JH QS KD AC"; @@ -145,6 +201,7 @@ public void acesCanEndAStraight() { @Disabled("Remove to run test") @Test + @DisplayName("aces can start a straight (A 2 3 4 5)") public void acesCanStartAStraight() { String hand = "4S 5H 4C 8D 4H"; String straightStartA = "4D AH 3S 2D 5C"; @@ -154,6 +211,17 @@ public void acesCanStartAStraight() { @Disabled("Remove to run test") @Test + @DisplayName("aces cannot be in the middle of a straight (Q K A 2 3)") + public void acesCannotBeInMiddleOfStraight() { + String hand = "2C 3D 7H 5H 2S"; + String straightMiddleA = "QS KH AC 2D 3S"; + assertThat(new Poker(Arrays.asList(hand, straightMiddleA)).getBestHands()) + .containsExactly(hand); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("both hands with a straight, tie goes to highest ranked card") public void twoStraights() { String straightTo8 = "4S 6C 7S 8D 5H"; String straightTo9 = "5S 7H 8S 9D 6H"; @@ -163,6 +231,7 @@ public void twoStraights() { @Disabled("Remove to run test") @Test + @DisplayName("even though an ace is usually high, a 5-high straight is the lowest-scoring straight") public void theLowestStraightStartsWithAce() { String straight = "2H 3C 4D 5D 6H"; String straightStartA = "4S AH 3S 2D 5H"; @@ -172,6 +241,7 @@ public void theLowestStraightStartsWithAce() { @Disabled("Remove to run test") @Test + @DisplayName("flush beats a straight") public void straightVsFlush() { String straightTo8 = "4C 6H 7D 8D 5H"; String flushTo7 = "2S 4S 5S 6S 7S"; @@ -181,15 +251,17 @@ public void straightVsFlush() { @Disabled("Remove to run test") @Test - public void twoFlushes() { - String flushTo8 = "4H 7H 8H 9H 6H"; - String flushTo7 = "2S 4S 5S 6S 7S"; - assertThat(new Poker(Arrays.asList(flushTo8, flushTo7)).getBestHands()) - .containsExactly(flushTo8); + @DisplayName("both hands have a flush, tie goes to high card, down to the last one if necessary") + public void twoFlushs() { + String flushTo9 = "2H 7H 8H 9H 6H"; + String flushTo7 = "3S 5S 6S 7S 8S"; + assertThat(new Poker(Arrays.asList(flushTo9, flushTo7)).getBestHands()) + .containsExactly(flushTo9); } @Disabled("Remove to run test") @Test + @DisplayName("full house beats a flush") public void flushVsFull() { String flushTo8 = "3H 6H 7H 8H 5H"; String full = "4S 5H 4C 5D 4H"; @@ -199,6 +271,7 @@ public void flushVsFull() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have a full house, tie goes to highest-ranked triplet") public void twoFulls() { String fullOf4By9 = "4H 4S 4D 9S 9D"; String fullOf5By8 = "5H 5S 5D 8S 8D"; @@ -208,6 +281,7 @@ public void twoFulls() { @Disabled("Remove to run test") @Test + @DisplayName("with multiple decks, both hands have a full house with the same triplet, tie goes to the pair") public void twoFullssameThripletMultipleDecks() { String fullOf5By9 = "5H 5S 5D 9S 9D"; String fullOf5By8 = "5H 5S 5D 8S 8D"; @@ -217,6 +291,7 @@ public void twoFullssameThripletMultipleDecks() { @Disabled("Remove to run test") @Test + @DisplayName("four of a kind beats a full house") public void fullVsSquare() { String full = "4S 5H 4D 5D 4H"; String squareOf3 = "3S 3H 2S 3D 3C"; @@ -226,6 +301,7 @@ public void fullVsSquare() { @Disabled("Remove to run test") @Test + @DisplayName("both hands have four of a kind, tie goes to high quad") public void twoSquares() { String squareOf2 = "2S 2H 2C 8D 2D"; String squareOf5 = "4S 5H 5S 5D 5C"; @@ -235,6 +311,7 @@ public void twoSquares() { @Disabled("Remove to run test") @Test + @DisplayName("with multiple decks, both hands with identical four of a kind, tie determined by kicker") public void sameSquaresMultipleDecks() { String kicker2 = "3S 3H 2S 3D 3C"; String kicker4 = "3S 3H 4S 3D 3C"; @@ -244,6 +321,7 @@ public void sameSquaresMultipleDecks() { @Disabled("Remove to run test") @Test + @DisplayName("straight flush beats four of a kind") public void squareVsStraightFlush() { String squareOf5 = "4S 5H 5S 5D 5C"; String straightFlushTo9 = "7S 8S 9S 6S 10S"; @@ -253,10 +331,51 @@ public void squareVsStraightFlush() { @Disabled("Remove to run test") @Test + @DisplayName("aces can end a straight flush (10 J Q K A)") + public void acesEndingStraightFlush() { + String hand = "KC AH AS AD AC"; + String straightFlushEndingWithA = "10C JC QC KC AC"; + assertThat(new Poker(Arrays.asList(hand, straightFlushEndingWithA)).getBestHands()) + .containsExactly(straightFlushEndingWithA); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("aces can start a straight flush (A 2 3 4 5)") + public void acesStartingStraightFlush() { + String straightFlushStartingWithA = "4H AH 3H 2H 5H"; + String hand = "KS AH AS AD AC"; + assertThat(new Poker(Arrays.asList(straightFlushStartingWithA, hand)).getBestHands()) + .containsExactly(straightFlushStartingWithA); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("aces cannot be in the middle of a straight flush (Q K A 2 3)") + public void acesCannotBeInMiddleOfStraightFlush() { + String straightFlushWithAInMiddle = "QH KH AH 2H 3H"; + String hand = "2C AC QC 10C KC"; + assertThat(new Poker(Arrays.asList(straightFlushWithAInMiddle, hand)).getBestHands()) + .containsExactly(hand); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("both hands have a straight flush, tie goes to highest-ranked card") public void twoStraightFlushes() { String straightFlushTo8 = "4H 6H 7H 8H 5H"; String straightFlushTo9 = "5S 7S 8S 9S 6S"; assertThat(new Poker(Arrays.asList(straightFlushTo8, straightFlushTo9)).getBestHands()) .containsExactly(straightFlushTo9); } + + @Disabled("Remove to run test") + @Test + @DisplayName("even though an ace is usually high, a 5-high straight flush is the lowest-scoring straight flush") + public void straightFlushTo5IsTheLowestScoring() { + String straightFlushTo6 = "2H 3H 4H 5H 6H"; + String straightFlushTo5 = "4D AD 3D 2D 5D"; + assertThat(new Poker(Arrays.asList(straightFlushTo6, straightFlushTo5)).getBestHands()) + .containsExactly(straightFlushTo6); + } } diff --git a/exercises/practice/pov/.meta/config.json b/exercises/practice/pov/.meta/config.json index 451fa6122..a4e51ec6e 100644 --- a/exercises/practice/pov/.meta/config.json +++ b/exercises/practice/pov/.meta/config.json @@ -18,5 +18,5 @@ }, "blurb": "Reparent a graph on a selected node.", "source": "Adaptation of exercise from 4clojure", - "source_url": "https://www.4clojure.com/" + "source_url": "https://github.com/oxalorg/4ever-clojure" } 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/pov/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/pov/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/pov/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/pov/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/pov/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/pov/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/pov/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/pov/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/pov/gradlew b/exercises/practice/pov/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/pov/gradlew +++ b/exercises/practice/pov/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/pov/gradlew.bat b/exercises/practice/pov/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/pov/gradlew.bat +++ b/exercises/practice/pov/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/prime-factors/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/prime-factors/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/prime-factors/gradlew b/exercises/practice/prime-factors/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/prime-factors/gradlew +++ b/exercises/practice/prime-factors/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/prime-factors/gradlew.bat b/exercises/practice/prime-factors/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/prime-factors/gradlew.bat +++ b/exercises/practice/prime-factors/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.docs/instructions.md b/exercises/practice/protein-translation/.docs/instructions.md index 7dc34d2ed..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 polypeptide 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 +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: -Codons: `"AUG", "UUU", "UCU"` -=> which become a polypeptide 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. - -| Codon | Protein | -| :----------------- | :------------ | +| 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/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/protein-translation/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/protein-translation/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/protein-translation/gradlew b/exercises/practice/protein-translation/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/protein-translation/gradlew +++ b/exercises/practice/protein-translation/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/protein-translation/gradlew.bat b/exercises/practice/protein-translation/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/protein-translation/gradlew.bat +++ b/exercises/practice/protein-translation/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/proverb/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/proverb/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/proverb/gradlew b/exercises/practice/proverb/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/proverb/gradlew +++ b/exercises/practice/proverb/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/proverb/gradlew.bat b/exercises/practice/proverb/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/proverb/gradlew.bat +++ b/exercises/practice/proverb/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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 diff --git a/exercises/practice/pythagorean-triplet/.docs/instructions.md b/exercises/practice/pythagorean-triplet/.docs/instructions.md index 1c1a8aea6..ced833d7a 100644 --- a/exercises/practice/pythagorean-triplet/.docs/instructions.md +++ b/exercises/practice/pythagorean-triplet/.docs/instructions.md @@ -1,4 +1,4 @@ -# Instructions +# Description A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for which, diff --git a/exercises/practice/pythagorean-triplet/.docs/introduction.md b/exercises/practice/pythagorean-triplet/.docs/introduction.md new file mode 100644 index 000000000..3453c6ed4 --- /dev/null +++ b/exercises/practice/pythagorean-triplet/.docs/introduction.md @@ -0,0 +1,19 @@ +# Introduction + +You are an accomplished problem-solver, known for your ability to tackle the most challenging mathematical puzzles. +One evening, you receive an urgent letter from an inventor called the Triangle Tinkerer, who is working on a groundbreaking new project. +The letter reads: + +> Dear Mathematician, +> +> I need your help. +> I am designing a device that relies on the unique properties of Pythagorean triplets — sets of three integers that satisfy the equation a² + b² = c². +> This device will revolutionize navigation, but for it to work, I must program it with every possible triplet where the sum of a, b, and c equals a specific number, N. +> Calculating these triplets by hand would take me years, but I hear you are more than up to the task. +> +> Time is of the essence. +> The future of my invention — and perhaps even the future of mathematical innovation — rests on your ability to solve this problem. + +Motivated by the importance of the task, you set out to find all Pythagorean triplets that satisfy the condition. +Your work could have far-reaching implications, unlocking new possibilities in science and engineering. +Can you rise to the challenge and make history? diff --git a/exercises/practice/pythagorean-triplet/.meta/config.json b/exercises/practice/pythagorean-triplet/.meta/config.json index 6cfac6820..9905ac631 100644 --- a/exercises/practice/pythagorean-triplet/.meta/config.json +++ b/exercises/practice/pythagorean-triplet/.meta/config.json @@ -36,7 +36,7 @@ "build.gradle" ] }, - "blurb": "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the triplet.", - "source": "Problem 9 at Project Euler", + "blurb": "Given an integer N, find all Pythagorean triplets for which a + b + c = N.", + "source": "A variation of Problem 9 from Project Euler", "source_url": "https://projecteuler.net/problem=9" } 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/pythagorean-triplet/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/pythagorean-triplet/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/pythagorean-triplet/gradlew b/exercises/practice/pythagorean-triplet/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/pythagorean-triplet/gradlew +++ b/exercises/practice/pythagorean-triplet/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/pythagorean-triplet/gradlew.bat b/exercises/practice/pythagorean-triplet/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/pythagorean-triplet/gradlew.bat +++ b/exercises/practice/pythagorean-triplet/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.approaches/config.json b/exercises/practice/queen-attack/.approaches/config.json new file mode 100644 index 000000000..e087317ce --- /dev/null +++ b/exercises/practice/queen-attack/.approaches/config.json @@ -0,0 +1,22 @@ +{ + "introduction": { + "authors": [ + "jagdish-15" + ], + "contributors": [ + "kahgoh", + "tasxatzial" + ] + }, + "approaches": [ + { + "uuid": "b2e474c8-b778-41e7-83c0-8e41cc84af9e", + "slug": "difference-comparison", + "title": "Difference Comparison Approach", + "blurb": "Use difference comparison checks to determine if queens can attack each other.", + "authors": [ + "jagdish-15" + ] + } + ] +} diff --git a/exercises/practice/queen-attack/.approaches/difference-comparison/content.md b/exercises/practice/queen-attack/.approaches/difference-comparison/content.md new file mode 100644 index 000000000..56b3cdfb6 --- /dev/null +++ b/exercises/practice/queen-attack/.approaches/difference-comparison/content.md @@ -0,0 +1,46 @@ +# Difference Comparison Approach + +```java +class QueenAttackCalculator { + private final Queen queen1; + private final Queen queen2; + + QueenAttackCalculator(Queen queen1, Queen queen2) { + if (queen1 == null || queen2 == null) { + throw new IllegalArgumentException("You must supply valid positions for both Queens."); + } + if (queen1.getRow() == queen2.getRow() && queen1.getColumn() == queen2.getColumn()) { + throw new IllegalArgumentException("Queens cannot occupy the same position."); + } + this.queen1 = queen1; + this.queen2 = queen2; + } + + boolean canQueensAttackOneAnother() { + int rowDifference = Math.abs(queen1.getRow() - queen2.getRow()); + int columnDifference = Math.abs(queen1.getColumn() - queen2.getColumn()); + return rowDifference == 0 || columnDifference == 0 || rowDifference == columnDifference; + } +} +``` + +## Explanation + +### Constructor + +The constructor takes two `Queen` objects, `queen1` and `queen2`, and stores them as instance variables after validating the following conditions: + +- Either queen is `null`. +- Both queens occupy the same position. + +If either of these conditions is true, an exception is thrown. + +### `canQueensAttackOneAnother` Method + +This method calculates the row and column differences between the two queens and checks the following conditions: + +- The row difference is zero (the queens are on the same row). +- The column difference is zero (the queens are on the same column). +- The row and column differences are equal (the queens are on the same diagonal). + +If any of these conditions are true, the method returns `true`, indicating that the queens can attack each other. diff --git a/exercises/practice/queen-attack/.approaches/difference-comparison/snippet.txt b/exercises/practice/queen-attack/.approaches/difference-comparison/snippet.txt new file mode 100644 index 000000000..90f224025 --- /dev/null +++ b/exercises/practice/queen-attack/.approaches/difference-comparison/snippet.txt @@ -0,0 +1,5 @@ +boolean canQueensAttackOneAnother() { + int rowDifference = Math.abs(queen1.getRow() - queen2.getRow()); + int columnDifference = Math.abs(queen1.getColumn() - queen2.getColumn()); + return rowDifference == 0 || columnDifference == 0 || rowDifference == columnDifference; +} \ No newline at end of file diff --git a/exercises/practice/queen-attack/.approaches/introduction.md b/exercises/practice/queen-attack/.approaches/introduction.md new file mode 100644 index 000000000..3c2d6d765 --- /dev/null +++ b/exercises/practice/queen-attack/.approaches/introduction.md @@ -0,0 +1,43 @@ +# Introduction + +In this exercise, we determine if two queens on a chessboard can attack each other based on their positions. +A queen in chess can move any number of squares horizontally, vertically, or diagonally. +The task is to check if two queens, placed on specific coordinates, can attack each other. + +## General Guidance + +The problem boils down to checking three conditions: + +1. **Same Row**: The queens are on the same row. +2. **Same Column**: The queens are on the same column. +3. **Same Diagonal**: The queens are on the same diagonal, i.e., the absolute difference between their row and column positions is equal. + +## Approach: Difference Comparison Approach + +```java +class QueenAttackCalculator { + private final Queen queen1; + private final Queen queen2; + + QueenAttackCalculator(Queen queen1, Queen queen2) { + if (queen1 == null || queen2 == null) { + throw new IllegalArgumentException("You must supply valid positions for both Queens."); + } + if (queen1.getRow() == queen2.getRow() && queen1.getColumn() == queen2.getColumn()) { + throw new IllegalArgumentException("Queens cannot occupy the same position."); + } + this.queen1 = queen1; + this.queen2 = queen2; + } + + boolean canQueensAttackOneAnother() { + int rowDifference = Math.abs(queen1.getRow() - queen2.getRow()); + int columnDifference = Math.abs(queen1.getColumn() - queen2.getColumn()); + return rowDifference == 0 || columnDifference == 0 || rowDifference == columnDifference; + } +} +``` + +For more details on the implementation of this approach, check out the [Difference Comparison Approach][difference-comparison-approach]. + +[difference-comparison-approach]: https://exercism.org/tracks/java/exercises/queen-attack/approaches/difference-comparison 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/queen-attack/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/queen-attack/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/queen-attack/gradlew b/exercises/practice/queen-attack/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/queen-attack/gradlew +++ b/exercises/practice/queen-attack/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/queen-attack/gradlew.bat b/exercises/practice/queen-attack/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/queen-attack/gradlew.bat +++ b/exercises/practice/queen-attack/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/rail-fence-cipher/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/rail-fence-cipher/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/rail-fence-cipher/gradlew b/exercises/practice/rail-fence-cipher/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/rail-fence-cipher/gradlew +++ b/exercises/practice/rail-fence-cipher/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/rail-fence-cipher/gradlew.bat b/exercises/practice/rail-fence-cipher/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/rail-fence-cipher/gradlew.bat +++ b/exercises/practice/rail-fence-cipher/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/raindrops/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/raindrops/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/raindrops/gradlew b/exercises/practice/raindrops/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/raindrops/gradlew +++ b/exercises/practice/raindrops/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/raindrops/gradlew.bat b/exercises/practice/raindrops/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/raindrops/gradlew.bat +++ b/exercises/practice/raindrops/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/rate-limiter/.docs/instructions.md b/exercises/practice/rate-limiter/.docs/instructions.md new file mode 100644 index 000000000..1e229cd6e --- /dev/null +++ b/exercises/practice/rate-limiter/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Your task is to build a fixed‑window rate limiter. + +Imagine a single server connected to one or more clients. +A client sends a request, the server does some work, and returns a response. +But processing takes time. +If a client sends too many requests too quickly, the server can become overwhelmed — everything slows down or fails. + +A rate limiter is a small component that decides whether to allow or reject a request based on how frequently that client has been making requests. +Different strategies exist; in this exercise you’ll implement a fixed‑window rate limiter. + +Fixed‑window rate limiting groups time into equal‑length windows (for example, every 10 seconds) and allows up to a certain number of requests within each window for each client. +Once the window resets, the allowance refreshes for the next window. +Each client is tracked separately, so another client can make requests within that same period. + +For example, consider a rate limiter configured to limit 2 requests per 10 seconds per client. +Lets say a client sends a request: + +- Being its first request, the request is permitted. +- A second request within 10 seconds after the first one is also permitted. +- However, further requests after that would be denied _until_ at least 10 seconds has elapsed since the first request. +- If a second client sends its first request within 10 seconds with of the first client's first request, it would also be permitted, _regardless_ of whether the first client has sent a second request. diff --git a/exercises/practice/rate-limiter/.meta/config.json b/exercises/practice/rate-limiter/.meta/config.json new file mode 100644 index 000000000..252db1567 --- /dev/null +++ b/exercises/practice/rate-limiter/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "andreatanky" + ], + "files": { + "solution": [ + "src/main/java/RateLimiter.java" + ], + "test": [ + "src/test/java/RateLimiterTest.java" + ], + "example": [ + ".meta/src/reference/java/RateLimiter.java", + ".meta/src/reference/java/TimeSource.java" + ], + "editor": [ + "src/main/java/TimeSource.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Practice stateful logic and time handling by implementing a fixed-window rate limiter" +} diff --git a/exercises/practice/rate-limiter/.meta/src/reference/java/RateLimiter.java b/exercises/practice/rate-limiter/.meta/src/reference/java/RateLimiter.java new file mode 100644 index 000000000..a3cebe23c --- /dev/null +++ b/exercises/practice/rate-limiter/.meta/src/reference/java/RateLimiter.java @@ -0,0 +1,49 @@ +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +public class RateLimiter { + + private static final class WindowState { + Instant windowStart; + int usedCount; + + WindowState(Instant windowStart, int usedCount) { + this.windowStart = windowStart; + this.usedCount = usedCount; + } + } + + private final int limit; + private final Duration windowSize; + private final TimeSource timeSource; + private final Map states = new HashMap<>(); + + public RateLimiter(int limit, Duration windowSize, TimeSource timeSource) { + this.limit = limit; + this.windowSize = windowSize; + this.timeSource = timeSource; + } + + public boolean allow(K clientId) { + Instant now = timeSource.now(); + + WindowState s = states.get(clientId); + if (s == null) { + s = new WindowState(now, 0); + states.put(clientId, s); + } + + if (!now.isBefore(s.windowStart.plus(windowSize))) { + s.windowStart = now; + s.usedCount = 0; + } + + if (s.usedCount < limit) { + s.usedCount += 1; + return true; + } + return false; + } +} diff --git a/exercises/practice/rate-limiter/.meta/src/reference/java/TimeSource.java b/exercises/practice/rate-limiter/.meta/src/reference/java/TimeSource.java new file mode 100644 index 000000000..db3bc2a86 --- /dev/null +++ b/exercises/practice/rate-limiter/.meta/src/reference/java/TimeSource.java @@ -0,0 +1,18 @@ +import java.time.Duration; +import java.time.Instant; + +public class TimeSource { + private Instant now; + + public TimeSource(Instant start) { + this.now = start; + } + + public Instant now() { + return now; + } + + public void advance(Duration d) { + this.now = this.now.plus(d); + } +} diff --git a/exercises/practice/rate-limiter/build.gradle b/exercises/practice/rate-limiter/build.gradle new file mode 100644 index 000000000..d28f35dee --- /dev/null +++ b/exercises/practice/rate-limiter/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/rate-limiter/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rate-limiter/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/rate-limiter/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rate-limiter/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rate-limiter/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/rate-limiter/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/rate-limiter/gradlew b/exercises/practice/rate-limiter/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/rate-limiter/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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/rate-limiter/gradlew.bat b/exercises/practice/rate-limiter/gradlew.bat new file mode 100644 index 000000000..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/rate-limiter/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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/rate-limiter/src/main/java/RateLimiter.java b/exercises/practice/rate-limiter/src/main/java/RateLimiter.java new file mode 100644 index 000000000..8a28ed946 --- /dev/null +++ b/exercises/practice/rate-limiter/src/main/java/RateLimiter.java @@ -0,0 +1,13 @@ +import java.time.Duration; +import java.time.Instant; + +public class RateLimiter { + + public RateLimiter(int limit, Duration windowSize, TimeSource timeSource) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } + + public boolean allow(K clientId) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); + } +} diff --git a/exercises/practice/rate-limiter/src/main/java/TimeSource.java b/exercises/practice/rate-limiter/src/main/java/TimeSource.java new file mode 100644 index 000000000..bc2c998d3 --- /dev/null +++ b/exercises/practice/rate-limiter/src/main/java/TimeSource.java @@ -0,0 +1,21 @@ +import java.time.Duration; +import java.time.Instant; + +/** + * NOTE: There is no need to change this file and is treated as read only by the Exercism test runners. + */ +public class TimeSource { + private Instant now; + + public TimeSource(Instant start) { + this.now = start; + } + + public Instant now() { + return now; + } + + public void advance(Duration d) { + this.now = this.now.plus(d); + } +} diff --git a/exercises/practice/rate-limiter/src/test/java/RateLimiterTest.java b/exercises/practice/rate-limiter/src/test/java/RateLimiterTest.java new file mode 100644 index 000000000..c740b4bf3 --- /dev/null +++ b/exercises/practice/rate-limiter/src/test/java/RateLimiterTest.java @@ -0,0 +1,194 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import java.time.Duration; +import java.time.Instant; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +class RateLimiterTest { + + @Test + @DisplayName("Allows up to window limit") + void allowsUpToLimit() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(3, Duration.ofNanos(10_000L), clock); + + assertThat(limiter.allow("A")).isTrue(); + clock.advance(Duration.ofNanos(1L)); + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("A")).isFalse(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Denies requests just before boundary") + void denyCloseToBoundary() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(2, Duration.ofNanos(10_000L), clock); + + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("A")).isFalse(); + + // Just before boundary: still same window + clock.advance(Duration.ofNanos(9_999L)); + assertThat(limiter.allow("A")).isFalse(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Allows first request at exact boundary") + void allowsNewBoundary() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(2, Duration.ofNanos(10_000L), clock); + + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("A")).isFalse(); + + // At exact boundary: new window + clock.advance(Duration.ofNanos(10_000L)); + assertThat(limiter.allow("A")).isTrue(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Resets at boundary, then counts within window") + void continuesCountingWithinWindowAfterBoundaryReset() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(2, Duration.ofNanos(5_000L), clock); + + assertThat(limiter.allow("key")).isTrue(); + clock.advance(Duration.ofNanos(1L)); + assertThat(limiter.allow("key")).isTrue(); + assertThat(limiter.allow("key")).isFalse(); + + // Jump to next window + clock.advance(Duration.ofNanos(5_000L)); + assertThat(limiter.allow("key")).isTrue(); + assertThat(limiter.allow("key")).isTrue(); + assertThat(limiter.allow("key")).isFalse(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Independent counters/windows per key") + void separateKeysHaveIndependentCountersAndWindows() { + TimeSource clock = new TimeSource(Instant.EPOCH.plusNanos(42L)); + RateLimiter limiter = new RateLimiter<>(1, Duration.ofNanos(100L), clock); + + assertThat(limiter.allow("A")).isTrue(); + // Advance a tiny amount to model time moving between requests + clock.advance(Duration.ofNanos(1L)); + assertThat(limiter.allow("A")).isFalse(); + assertThat(limiter.allow("B")).isTrue(); // independent key + assertThat(limiter.allow("B")).isFalse(); + + clock.advance(Duration.ofNanos(100L)); // new window for both at boundary + assertThat(limiter.allow("A")).isTrue(); + assertThat(limiter.allow("B")).isTrue(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Long gaps reset window") + void longGapsResetWindow() { + TimeSource clock = new TimeSource(Instant.EPOCH.plusNanos(1_000L)); + RateLimiter limiter = new RateLimiter<>(2, Duration.ofNanos(50L), clock); + + assertThat(limiter.allow("X")).isTrue(); + clock.advance(Duration.ofNanos(1L)); + assertThat(limiter.allow("X")).isTrue(); + assertThat(limiter.allow("X")).isFalse(); + + // Advance several windows worth + clock.advance(Duration.ofNanos(1_000L)); + assertThat(limiter.allow("X")).isTrue(); + assertThat(limiter.allow("X")).isTrue(); + assertThat(limiter.allow("X")).isFalse(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Exact boundary starts a new window each time") + void exactBoundaryIsNewWindowEveryTime() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(1, Duration.ofNanos(10L), clock); + + assertThat(limiter.allow("k")).isTrue(); + assertThat(limiter.allow("k")).isFalse(); + + // Move exactly to boundary repeatedly; each time should allow once + for (int i = 0; i < 5; i++) { + clock.advance(Duration.ofNanos(10L)); + assertThat(limiter.allow("k")).isTrue(); + assertThat(limiter.allow("k")).isFalse(); + } + } + + + @Disabled("Remove to run test") + @Test + @DisplayName("Supports UUID keys") + void supportsUuidKeys() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(1, Duration.ofSeconds(1L), clock); + + UUID a = UUID.fromString("00000000-0000-0000-0000-000000000001"); + UUID b = UUID.fromString("00000000-0000-0000-0000-000000000002"); + + assertThat(limiter.allow(a)).isTrue(); + assertThat(limiter.allow(a)).isFalse(); + // Advance slightly so the second client's window anchors later + clock.advance(Duration.ofMillis(1L)); + assertThat(limiter.allow(b)).isTrue(); + assertThat(limiter.allow(b)).isFalse(); + + // Advance exactly one second to hit the window boundary + clock.advance(Duration.ofSeconds(1L)); + assertThat(limiter.allow(a)).isTrue(); + assertThat(limiter.allow(b)).isTrue(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Supports Integer keys") + void supportsIntegerKeys() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(1, Duration.ofNanos(100L), clock); + + assertThat(limiter.allow(42)).isTrue(); + assertThat(limiter.allow(42)).isFalse(); + clock.advance(Duration.ofNanos(1L)); + assertThat(limiter.allow(84)).isTrue(); // independent key of different type + assertThat(limiter.allow(84)).isFalse(); + + clock.advance(Duration.ofNanos(100L)); // boundary resets both + assertThat(limiter.allow(42)).isTrue(); + assertThat(limiter.allow(84)).isTrue(); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Supports Long keys") + void supportsLongKeys() { + TimeSource clock = new TimeSource(Instant.EPOCH); + RateLimiter limiter = new RateLimiter<>(2, Duration.ofNanos(50L), clock); + + assertThat(limiter.allow(1L)).isTrue(); + assertThat(limiter.allow(1L)).isTrue(); + assertThat(limiter.allow(1L)).isFalse(); + + clock.advance(Duration.ofNanos(1L)); + assertThat(limiter.allow(2L)).isTrue(); // independent key + assertThat(limiter.allow(2L)).isTrue(); + assertThat(limiter.allow(2L)).isFalse(); + + clock.advance(Duration.ofNanos(50L)); + assertThat(limiter.allow(1L)).isTrue(); + assertThat(limiter.allow(2L)).isTrue(); + } +} 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/rational-numbers/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/rational-numbers/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/rational-numbers/gradlew b/exercises/practice/rational-numbers/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/rational-numbers/gradlew +++ b/exercises/practice/rational-numbers/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/rational-numbers/gradlew.bat b/exercises/practice/rational-numbers/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/rational-numbers/gradlew.bat +++ b/exercises/practice/rational-numbers/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/rational-numbers/src/test/java/RationalTest.java b/exercises/practice/rational-numbers/src/test/java/RationalTest.java index 7eb882688..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; @@ -8,15 +9,16 @@ public class RationalTest { // Helper methods - private static final double DOUBLE_EQUALITY_TOLERANCE = 1e-15; + private static final double DOUBLE_EQUALITY_TOLERANCE = 1e-8; private void assertDoublesEqual(double x, double y) { - assertThat(x).isEqualTo(y, within(DOUBLE_EQUALITY_TOLERANCE)); + assertThat(x).isCloseTo(y, within(DOUBLE_EQUALITY_TOLERANCE)); } // 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/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/react/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/react/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/react/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/react/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/react/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/react/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/react/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/react/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/react/gradlew b/exercises/practice/react/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/react/gradlew +++ b/exercises/practice/react/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/react/gradlew.bat b/exercises/practice/react/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/react/gradlew.bat +++ b/exercises/practice/react/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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)); 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/rectangles/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/rectangles/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/rectangles/gradlew b/exercises/practice/rectangles/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/rectangles/gradlew +++ b/exercises/practice/rectangles/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/rectangles/gradlew.bat b/exercises/practice/rectangles/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/rectangles/gradlew.bat +++ b/exercises/practice/rectangles/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/.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..34073b40a --- /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]: https://web.archive.org/web/20250816223614/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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/relative-distance/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/relative-distance/gradlew b/exercises/practice/relative-distance/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/relative-distance/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/relative-distance/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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..b10a9459f --- /dev/null +++ b/exercises/practice/relative-distance/src/test/java/RelativeDistanceTest.java @@ -0,0 +1,267 @@ +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +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 + @DisplayName("Direct parent-child relation") + 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 + @DisplayName("Sibling relationship") + 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 + @DisplayName("Two degrees of separation, grandchild") + 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 + @DisplayName("Unrelated individuals") + 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 + @DisplayName("Complex graph, cousins") + 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 + @DisplayName("Complex graph, no shortcut, far removed nephew") + 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 + @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<>() { + { + 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/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-duo/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/resistor-color-duo/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/resistor-color-duo/gradlew b/exercises/practice/resistor-color-duo/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/resistor-color-duo/gradlew +++ b/exercises/practice/resistor-color-duo/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/resistor-color-duo/gradlew.bat b/exercises/practice/resistor-color-duo/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/resistor-color-duo/gradlew.bat +++ b/exercises/practice/resistor-color-duo/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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-trio/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/resistor-color-trio/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/resistor-color-trio/gradlew b/exercises/practice/resistor-color-trio/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/resistor-color-trio/gradlew +++ b/exercises/practice/resistor-color-trio/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/resistor-color-trio/gradlew.bat b/exercises/practice/resistor-color-trio/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/resistor-color-trio/gradlew.bat +++ b/exercises/practice/resistor-color-trio/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/resistor-color/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/resistor-color/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/resistor-color/gradlew b/exercises/practice/resistor-color/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/resistor-color/gradlew +++ b/exercises/practice/resistor-color/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/resistor-color/gradlew.bat b/exercises/practice/resistor-color/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/resistor-color/gradlew.bat +++ b/exercises/practice/resistor-color/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/rest-api/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/rest-api/gradlew b/exercises/practice/rest-api/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/rest-api/gradlew +++ b/exercises/practice/rest-api/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/rest-api/gradlew.bat b/exercises/practice/rest-api/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/rest-api/gradlew.bat +++ b/exercises/practice/rest-api/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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/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/reverse-string/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/reverse-string/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/reverse-string/gradlew b/exercises/practice/reverse-string/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/reverse-string/gradlew +++ b/exercises/practice/reverse-string/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/reverse-string/gradlew.bat b/exercises/practice/reverse-string/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/reverse-string/gradlew.bat +++ b/exercises/practice/reverse-string/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell 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"); } diff --git a/exercises/practice/rna-transcription/.docs/instructions.md b/exercises/practice/rna-transcription/.docs/instructions.md index 36da381f5..4dbfd3a27 100644 --- a/exercises/practice/rna-transcription/.docs/instructions.md +++ b/exercises/practice/rna-transcription/.docs/instructions.md @@ -1,12 +1,12 @@ # Instructions -Your task is determine the RNA complement of a given DNA sequence. +Your task is to determine the RNA complement of a given DNA sequence. Both DNA and RNA strands are a sequence of nucleotides. -The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), guanine (**G**) and thymine (**T**). +The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), guanine (**G**), and thymine (**T**). -The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), guanine (**G**) and uracil (**U**). +The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), guanine (**G**), and uracil (**U**). Given a DNA strand, its transcribed RNA strand is formed by replacing each nucleotide with its complement: 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/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/rna-transcription/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/rna-transcription/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/rna-transcription/gradlew b/exercises/practice/rna-transcription/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/rna-transcription/gradlew +++ b/exercises/practice/rna-transcription/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/rna-transcription/gradlew.bat b/exercises/practice/rna-transcription/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/rna-transcription/gradlew.bat +++ b/exercises/practice/rna-transcription/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/rna-transcription/src/test/java/RnaTranscriptionTest.java b/exercises/practice/rna-transcription/src/test/java/RnaTranscriptionTest.java index 7de6deaf8..7b1cc753a 100644 --- a/exercises/practice/rna-transcription/src/test/java/RnaTranscriptionTest.java +++ b/exercises/practice/rna-transcription/src/test/java/RnaTranscriptionTest.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("Empty RNA sequence") public void testEmptyRnaSequence() { assertThat(rnaTranscription.transcribe("")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("RNA complement of cytosine is guanine") public void testRnaTranscriptionOfCytosineIsGuanine() { assertThat(rnaTranscription.transcribe("C")).isEqualTo("G"); } @Disabled("Remove to run test") @Test + @DisplayName("RNA complement of guanine is cytosine") public void testRnaTranscriptionOfGuanineIsCytosine() { assertThat(rnaTranscription.transcribe("G")).isEqualTo("C"); } @Disabled("Remove to run test") @Test + @DisplayName("RNA complement of thymine is adenine") public void testRnaTranscriptionOfThymineIsAdenine() { assertThat(rnaTranscription.transcribe("T")).isEqualTo("A"); } @Disabled("Remove to run test") @Test + @DisplayName("RNA complement of adenine is uracil") public void testRnaTranscriptionOfAdenineIsUracil() { assertThat(rnaTranscription.transcribe("A")).isEqualTo("U"); } @Disabled("Remove to run test") @Test + @DisplayName("RNA complement") public void testRnaTranscription() { assertThat(rnaTranscription.transcribe("ACGTGGTCTTAA")).isEqualTo("UGCACCAGAAUU"); } 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-name/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/robot-name/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/robot-name/gradlew b/exercises/practice/robot-name/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/robot-name/gradlew +++ b/exercises/practice/robot-name/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/robot-name/gradlew.bat b/exercises/practice/robot-name/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/robot-name/gradlew.bat +++ b/exercises/practice/robot-name/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/robot-name/src/test/java/RobotTest.java b/exercises/practice/robot-name/src/test/java/RobotTest.java index 6c19ae877..542d4a586 100644 --- a/exercises/practice/robot-name/src/test/java/RobotTest.java +++ b/exercises/practice/robot-name/src/test/java/RobotTest.java @@ -1,11 +1,12 @@ -import static org.assertj.core.api.Assertions.assertThat; +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.HashSet; import java.util.Set; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; public class RobotTest { @@ -18,24 +19,28 @@ public void setUp() { } @Test + @DisplayName("Robot has a valid name") public void hasName() { assertIsValidName(robot.getName()); } @Test @Disabled("Remove to run test") + @DisplayName("Same robot returns the same name on repeated calls") public void sameRobotsHaveSameNames() { assertThat(robot.getName()).isEqualTo(robot.getName()); } @Disabled("Remove to run test") @Test + @DisplayName("Different robots have different names") public void differentRobotsHaveDifferentNames() { assertThat(robot.getName()).isNotEqualTo(new Robot().getName()); } @Disabled("Remove to run test") @Test + @DisplayName("Resetting a robot assigns a new valid name") public void resetName() { final String name = robot.getName(); robot.reset(); @@ -46,6 +51,7 @@ public void resetName() { @Disabled("Remove to run test") @Test + @DisplayName("Robot names are unique") public void robotNamesAreUnique() { Set robotNames = new HashSet<>(); int sampleSize = 5000; 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/robot-simulator/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/robot-simulator/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/robot-simulator/gradlew b/exercises/practice/robot-simulator/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/robot-simulator/gradlew +++ b/exercises/practice/robot-simulator/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/robot-simulator/gradlew.bat b/exercises/practice/robot-simulator/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/robot-simulator/gradlew.bat +++ b/exercises/practice/robot-simulator/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/robot-simulator/src/test/java/RobotTest.java b/exercises/practice/robot-simulator/src/test/java/RobotTest.java index 27deb9ce4..5c8edbc0c 100644 --- a/exercises/practice/robot-simulator/src/test/java/RobotTest.java +++ b/exercises/practice/robot-simulator/src/test/java/RobotTest.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 RobotTest { /* Create robot */ @Test + @DisplayName("at origin facing north") public void atOriginFacingNorth() { Orientation initialOrientation = Orientation.NORTH; GridPosition initialGridPosition = new GridPosition(0, 0); @@ -19,6 +21,7 @@ public void atOriginFacingNorth() { @Disabled("Remove to run test") @Test + @DisplayName("at negative position facing south") public void atNegativePositionFacingSouth() { GridPosition initialGridPosition = new GridPosition(-1, -1); Orientation initialOrientation = Orientation.SOUTH; @@ -32,6 +35,7 @@ public void atNegativePositionFacingSouth() { @Disabled("Remove to run test") @Test + @DisplayName("changes north to east") public void changesNorthToEast() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.NORTH); @@ -45,6 +49,7 @@ public void changesNorthToEast() { @Disabled("Remove to run test") @Test + @DisplayName("changes east to south") public void changesEastToSouth() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.EAST); @@ -58,6 +63,7 @@ public void changesEastToSouth() { @Disabled("Remove to run test") @Test + @DisplayName("changes south to west") public void changesSouthToWest() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.SOUTH); @@ -71,6 +77,7 @@ public void changesSouthToWest() { @Disabled("Remove to run test") @Test + @DisplayName("changes west to north") public void changesWestToNorth() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.WEST); @@ -86,6 +93,7 @@ public void changesWestToNorth() { @Disabled("Remove to run test") @Test + @DisplayName("changes north to west") public void changesNorthToWest() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.NORTH); @@ -99,6 +107,7 @@ public void changesNorthToWest() { @Disabled("Remove to run test") @Test + @DisplayName("changes west to south") public void changesWestToSouth() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.WEST); @@ -112,6 +121,7 @@ public void changesWestToSouth() { @Disabled("Remove to run test") @Test + @DisplayName("changes south to east") public void changesSouthToEast() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.SOUTH); @@ -125,6 +135,7 @@ public void changesSouthToEast() { @Disabled("Remove to run test") @Test + @DisplayName("changes east to north") public void changesEastToNorth() { GridPosition initialGridPosition = new GridPosition(0, 0); Robot robot = new Robot(initialGridPosition, Orientation.EAST); @@ -140,6 +151,7 @@ public void changesEastToNorth() { @Disabled("Remove to run test") @Test + @DisplayName("facing north increments Y") public void facingNorthIncrementsY() { Orientation initialOrientation = Orientation.NORTH; Robot robot = new Robot(new GridPosition(0, 0), initialOrientation); @@ -153,6 +165,7 @@ public void facingNorthIncrementsY() { @Disabled("Remove to run test") @Test + @DisplayName("facing south decrements Y") public void facingSouthDecrementsY() { Orientation initialOrientation = Orientation.SOUTH; Robot robot = new Robot(new GridPosition(0, 0), initialOrientation); @@ -166,6 +179,7 @@ public void facingSouthDecrementsY() { @Disabled("Remove to run test") @Test + @DisplayName("facing east increments X") public void facingEastIncrementsX() { Orientation initialOrientation = Orientation.EAST; Robot robot = new Robot(new GridPosition(0, 0), initialOrientation); @@ -179,6 +193,7 @@ public void facingEastIncrementsX() { @Disabled("Remove to run test") @Test + @DisplayName("facing west decrements X") public void facingWestDecrementsX() { Orientation initialOrientation = Orientation.WEST; Robot robot = new Robot(new GridPosition(0, 0), initialOrientation); @@ -194,6 +209,7 @@ public void facingWestDecrementsX() { @Disabled("Remove to run test") @Test + @DisplayName("moving east and north from README") public void movingEastAndNorthFromReadme() { Robot robot = new Robot(new GridPosition(7, 3), Orientation.NORTH); @@ -209,6 +225,7 @@ public void movingEastAndNorthFromReadme() { @Disabled("Remove to run test") @Test + @DisplayName("moving west and north") public void movingWestAndNorth() { Robot robot = new Robot(new GridPosition(0, 0), Orientation.NORTH); @@ -223,6 +240,7 @@ public void movingWestAndNorth() { @Disabled("Remove to run test") @Test + @DisplayName("moving west and south") public void movingWestAndSouth() { Robot robot = new Robot(new GridPosition(2, -7), Orientation.EAST); @@ -237,6 +255,7 @@ public void movingWestAndSouth() { @Disabled("Remove to run test") @Test + @DisplayName("moving east and north") public void movingEastAndNorth() { Robot robot = new Robot(new GridPosition(8, 4), Orientation.SOUTH); 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/roman-numerals/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/roman-numerals/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/roman-numerals/gradlew b/exercises/practice/roman-numerals/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/roman-numerals/gradlew +++ b/exercises/practice/roman-numerals/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/roman-numerals/gradlew.bat b/exercises/practice/roman-numerals/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/roman-numerals/gradlew.bat +++ b/exercises/practice/roman-numerals/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/roman-numerals/src/test/java/RomanNumeralsTest.java b/exercises/practice/roman-numerals/src/test/java/RomanNumeralsTest.java index 5d02ddc6f..8b7c21da8 100644 --- a/exercises/practice/roman-numerals/src/test/java/RomanNumeralsTest.java +++ b/exercises/practice/roman-numerals/src/test/java/RomanNumeralsTest.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 RomanNumeralsTest { private RomanNumerals romanNumerals; @Test + @DisplayName("1 is I") public void test1ToRomanNumberI() { romanNumerals = new RomanNumerals(1); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("I"); @@ -15,6 +17,7 @@ public void test1ToRomanNumberI() { @Disabled("Remove to run test") @Test + @DisplayName("2 is II") public void test2ToRomanNumberII() { romanNumerals = new RomanNumerals(2); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("II"); @@ -22,6 +25,7 @@ public void test2ToRomanNumberII() { @Disabled("Remove to run test") @Test + @DisplayName("3 is III") public void test3ToRomanNumberIII() { romanNumerals = new RomanNumerals(3); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("III"); @@ -29,6 +33,7 @@ public void test3ToRomanNumberIII() { @Disabled("Remove to run test") @Test + @DisplayName("4 is IV") public void test4ToRomanNumberIV() { romanNumerals = new RomanNumerals(4); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("IV"); @@ -36,6 +41,7 @@ public void test4ToRomanNumberIV() { @Disabled("Remove to run test") @Test + @DisplayName("5 is V") public void test5ToRomanNumberV() { romanNumerals = new RomanNumerals(5); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("V"); @@ -43,6 +49,7 @@ public void test5ToRomanNumberV() { @Disabled("Remove to run test") @Test + @DisplayName("6 is VI") public void test6ToRomanNumberVI() { romanNumerals = new RomanNumerals(6); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("VI"); @@ -50,6 +57,7 @@ public void test6ToRomanNumberVI() { @Disabled("Remove to run test") @Test + @DisplayName("9 is IX") public void test9ToRomanNumberIX() { romanNumerals = new RomanNumerals(9); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("IX"); @@ -57,6 +65,7 @@ public void test9ToRomanNumberIX() { @Disabled("Remove to run test") @Test + @DisplayName("16 is XVI") public void test16ToRomanNumberXVI() { romanNumerals = new RomanNumerals(16); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("XVI"); @@ -64,6 +73,7 @@ public void test16ToRomanNumberXVI() { @Disabled("Remove to run test") @Test + @DisplayName("27 is XXVII") public void test27ToRomanNumberXXVII() { romanNumerals = new RomanNumerals(27); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("XXVII"); @@ -71,6 +81,7 @@ public void test27ToRomanNumberXXVII() { @Disabled("Remove to run test") @Test + @DisplayName("48 is XLVIII") public void test48ToRomanNumberXLVIII() { romanNumerals = new RomanNumerals(48); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("XLVIII"); @@ -78,6 +89,7 @@ public void test48ToRomanNumberXLVIII() { @Disabled("Remove to run test") @Test + @DisplayName("49 is XLIX") public void test49ToRomanNumberXLIX() { romanNumerals = new RomanNumerals(49); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("XLIX"); @@ -85,6 +97,7 @@ public void test49ToRomanNumberXLIX() { @Disabled("Remove to run test") @Test + @DisplayName("59 is LIX") public void test59ToRomanNumberLIX() { romanNumerals = new RomanNumerals(59); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("LIX"); @@ -92,6 +105,7 @@ public void test59ToRomanNumberLIX() { @Disabled("Remove to run test") @Test + @DisplayName("66 is LXVI") public void test66ToRomanNumberLXVI() { romanNumerals = new RomanNumerals(66); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("LXVI"); @@ -99,6 +113,7 @@ public void test66ToRomanNumberLXVI() { @Disabled("Remove to run test") @Test + @DisplayName("93 is XCIII") public void test93ToRomanNumberXCIII() { romanNumerals = new RomanNumerals(93); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("XCIII"); @@ -106,6 +121,7 @@ public void test93ToRomanNumberXCIII() { @Disabled("Remove to run test") @Test + @DisplayName("141 is CXLI") public void test141ToRomanNumberCXLI() { romanNumerals = new RomanNumerals(141); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("CXLI"); @@ -113,6 +129,7 @@ public void test141ToRomanNumberCXLI() { @Disabled("Remove to run test") @Test + @DisplayName("163 is CLXIII") public void test163ToRomanNumberCLXIII() { romanNumerals = new RomanNumerals(163); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("CLXIII"); @@ -120,6 +137,7 @@ public void test163ToRomanNumberCLXIII() { @Disabled("Remove to run test") @Test + @DisplayName("166 is CLXVI") public void test166ToRomanNumberCLXVI() { romanNumerals = new RomanNumerals(166); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("CLXVI"); @@ -127,6 +145,7 @@ public void test166ToRomanNumberCLXVI() { @Disabled("Remove to run test") @Test + @DisplayName("402 is CDII") public void test402ToRomanNumberCDII() { romanNumerals = new RomanNumerals(402); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("CDII"); @@ -134,6 +153,7 @@ public void test402ToRomanNumberCDII() { @Disabled("Remove to run test") @Test + @DisplayName("575 is DLXXV") public void test575ToRomanNumberDLXXV() { romanNumerals = new RomanNumerals(575); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("DLXXV"); @@ -141,6 +161,7 @@ public void test575ToRomanNumberDLXXV() { @Disabled("Remove to run test") @Test + @DisplayName("666 is DCLXVI") public void test666ToRomanNumberDCLXVI() { romanNumerals = new RomanNumerals(666); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("DCLXVI"); @@ -148,6 +169,7 @@ public void test666ToRomanNumberDCLXVI() { @Disabled("Remove to run test") @Test + @DisplayName("911 is CMXI") public void test911ToRomanNumberCMXI() { romanNumerals = new RomanNumerals(911); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("CMXI"); @@ -155,6 +177,7 @@ public void test911ToRomanNumberCMXI() { @Disabled("Remove to run test") @Test + @DisplayName("1024 is MXXIV") public void test1024ToRomanNumberMXXIV() { romanNumerals = new RomanNumerals(1024); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("MXXIV"); @@ -162,6 +185,7 @@ public void test1024ToRomanNumberMXXIV() { @Disabled("Remove to run test") @Test + @DisplayName("1666 is MDCLXVI") public void test1666ToRomanNumberMDCLXVI() { romanNumerals = new RomanNumerals(1666); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("MDCLXVI"); @@ -169,6 +193,7 @@ public void test1666ToRomanNumberMDCLXVI() { @Disabled("Remove to run test") @Test + @DisplayName("3000 is MMM") public void test3000ToRomanNumberMMM() { romanNumerals = new RomanNumerals(3000); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("MMM"); @@ -176,6 +201,7 @@ public void test3000ToRomanNumberMMM() { @Disabled("Remove to run test") @Test + @DisplayName("3001 is MMMI") public void test3001ToRomanNumberMMMI() { romanNumerals = new RomanNumerals(3001); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("MMMI"); @@ -183,6 +209,7 @@ public void test3001ToRomanNumberMMMI() { @Disabled("Remove to run test") @Test + @DisplayName("3888 is MMMDCCCLXXXVIII") public void test3888ToRomanNumberMMMDCCCLXXXVIII() { romanNumerals = new RomanNumerals(3888); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("MMMDCCCLXXXVIII"); @@ -190,6 +217,7 @@ public void test3888ToRomanNumberMMMDCCCLXXXVIII() { @Disabled("Remove to run test") @Test + @DisplayName("3999 is MMMCMXCIX") public void test3999ToRomanNumberMMMCMXCIX() { romanNumerals = new RomanNumerals(3999); assertThat(romanNumerals.getRomanNumeral()).isEqualTo("MMMCMXCIX"); 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/rotational-cipher/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/rotational-cipher/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/rotational-cipher/gradlew b/exercises/practice/rotational-cipher/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/rotational-cipher/gradlew +++ b/exercises/practice/rotational-cipher/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/rotational-cipher/gradlew.bat b/exercises/practice/rotational-cipher/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/rotational-cipher/gradlew.bat +++ b/exercises/practice/rotational-cipher/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/rotational-cipher/src/test/java/RotationalCipherTest.java b/exercises/practice/rotational-cipher/src/test/java/RotationalCipherTest.java index cd18aa56e..be75833ab 100644 --- a/exercises/practice/rotational-cipher/src/test/java/RotationalCipherTest.java +++ b/exercises/practice/rotational-cipher/src/test/java/RotationalCipherTest.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 RotationalCipherTest { private RotationalCipher rotationalCipher; @Test + @DisplayName("rotate a by 0, same output as input") public void rotateSingleCharacterBy0() { rotationalCipher = new RotationalCipher(0); assertThat(rotationalCipher.rotate("a")).isEqualTo("a"); @@ -15,6 +17,7 @@ public void rotateSingleCharacterBy0() { @Disabled("Remove to run test") @Test + @DisplayName("rotate a by 1") public void rotateSingleCharacterBy1() { rotationalCipher = new RotationalCipher(1); assertThat(rotationalCipher.rotate("a")).isEqualTo("b"); @@ -22,6 +25,7 @@ public void rotateSingleCharacterBy1() { @Disabled("Remove to run test") @Test + @DisplayName("rotate a by 26, same output as input") public void rotateSingleCharacterBy26() { rotationalCipher = new RotationalCipher(26); assertThat(rotationalCipher.rotate("a")).isEqualTo("a"); @@ -29,6 +33,7 @@ public void rotateSingleCharacterBy26() { @Disabled("Remove to run test") @Test + @DisplayName("rotate m by 13") public void rotateSingleCharacterBy13() { rotationalCipher = new RotationalCipher(13); assertThat(rotationalCipher.rotate("m")).isEqualTo("z"); @@ -36,6 +41,7 @@ public void rotateSingleCharacterBy13() { @Disabled("Remove to run test") @Test + @DisplayName("rotate n by 13 with wrap around alphabet") public void rotateSingleCharacterWithWrapAround() { rotationalCipher = new RotationalCipher(13); assertThat(rotationalCipher.rotate("n")).isEqualTo("a"); @@ -43,6 +49,7 @@ public void rotateSingleCharacterWithWrapAround() { @Disabled("Remove to run test") @Test + @DisplayName("rotate capital letters") public void rotateCapitalLetters() { rotationalCipher = new RotationalCipher(5); assertThat(rotationalCipher.rotate("OMG")).isEqualTo("TRL"); @@ -50,6 +57,7 @@ public void rotateCapitalLetters() { @Disabled("Remove to run test") @Test + @DisplayName("rotate spaces") public void rotateSpaces() { rotationalCipher = new RotationalCipher(5); assertThat(rotationalCipher.rotate("O M G")).isEqualTo("T R L"); @@ -57,6 +65,7 @@ public void rotateSpaces() { @Disabled("Remove to run test") @Test + @DisplayName("rotate numbers") public void rotateNumbers() { rotationalCipher = new RotationalCipher(4); assertThat(rotationalCipher.rotate("Testing 1 2 3 testing")).isEqualTo("Xiwxmrk 1 2 3 xiwxmrk"); @@ -64,6 +73,7 @@ public void rotateNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("rotate punctuation") public void rotatePunctuation() { rotationalCipher = new RotationalCipher(21); assertThat(rotationalCipher.rotate("Let's eat, Grandma!")).isEqualTo("Gzo'n zvo, Bmviyhv!"); @@ -71,6 +81,7 @@ public void rotatePunctuation() { @Disabled("Remove to run test") @Test + @DisplayName("rotate all letters") public void rotateAllLetters() { rotationalCipher = new RotationalCipher(13); assertThat(rotationalCipher.rotate("Gur dhvpx oebja sbk whzcf bire gur ynml qbt.")) 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/run-length-encoding/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/run-length-encoding/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/run-length-encoding/gradlew b/exercises/practice/run-length-encoding/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/run-length-encoding/gradlew +++ b/exercises/practice/run-length-encoding/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/run-length-encoding/gradlew.bat b/exercises/practice/run-length-encoding/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/run-length-encoding/gradlew.bat +++ b/exercises/practice/run-length-encoding/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/run-length-encoding/src/test/java/RunLengthEncodingTest.java b/exercises/practice/run-length-encoding/src/test/java/RunLengthEncodingTest.java index 6923ccdbd..47373f2e2 100644 --- a/exercises/practice/run-length-encoding/src/test/java/RunLengthEncodingTest.java +++ b/exercises/practice/run-length-encoding/src/test/java/RunLengthEncodingTest.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,24 +14,28 @@ public void setUp() { } @Test + @DisplayName("empty string") public void encodeEmpty() { assertThat(runLengthEncoding.encode("")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("single characters only are encoded without count") public void encodeWithOnlySingleValues() { assertThat(runLengthEncoding.encode("XYZ")).isEqualTo("XYZ"); } @Disabled("Remove to run test") @Test + @DisplayName("string with no single characters") public void encodeWithNoSingleValues() { assertThat(runLengthEncoding.encode("AABBBCCCC")).isEqualTo("2A3B4C"); } @Disabled("Remove to run test") @Test + @DisplayName("single characters mixed with repeated characters") public void encodeWithMixedValues() { assertThat(runLengthEncoding.encode( "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB")) @@ -39,6 +44,7 @@ public void encodeWithMixedValues() { @Disabled("Remove to run test") @Test + @DisplayName("multiple whitespace mixed in string") public void encodeWithWhitespaceValues() { assertThat(runLengthEncoding.encode(" hsqq qww ")) .isEqualTo("2 hs2q q2w2 "); @@ -46,30 +52,35 @@ public void encodeWithWhitespaceValues() { @Disabled("Remove to run test") @Test + @DisplayName("lowercase characters") public void encodeWithLowercaseValues() { assertThat(runLengthEncoding.encode("aabbbcccc")).isEqualTo("2a3b4c"); } @Disabled("Remove to run test") @Test + @DisplayName("empty string") public void decodeEmpty() { assertThat(runLengthEncoding.decode("")).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("single characters only") public void decodeWithOnlySingleValues() { assertThat(runLengthEncoding.decode("XYZ")).isEqualTo("XYZ"); } @Disabled("Remove to run test") @Test + @DisplayName("string with no single characters") public void decodeWithNoSingleValues() { assertThat(runLengthEncoding.decode("2A3B4C")).isEqualTo("AABBBCCCC"); } @Disabled("Remove to run test") @Test + @DisplayName("single characters with repeated characters") public void decodeWithMixedValues() { assertThat(runLengthEncoding.decode("12WB12W3B24WB")) .isEqualTo("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB"); @@ -77,18 +88,21 @@ public void decodeWithMixedValues() { @Disabled("Remove to run test") @Test + @DisplayName("multiple whitespace mixed in string") public void decodeWithWhitespaceValues() { assertThat(runLengthEncoding.decode("2 hs2q q2w2 ")).isEqualTo(" hsqq qww "); } @Disabled("Remove to run test") @Test + @DisplayName("lowercase string") public void decodeWithLowercaseValues() { assertThat(runLengthEncoding.decode("2a3b4c")).isEqualTo("aabbbcccc"); } @Disabled("Remove to run test") @Test + @DisplayName("encode followed by decode gives original string") public void encodeThenDecode() { String inOut = "zzz ZZ zZ"; String encoded = runLengthEncoding.encode(inOut); 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/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/saddle-points/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/saddle-points/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/saddle-points/gradlew b/exercises/practice/saddle-points/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/saddle-points/gradlew +++ b/exercises/practice/saddle-points/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/saddle-points/gradlew.bat b/exercises/practice/saddle-points/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/saddle-points/gradlew.bat +++ b/exercises/practice/saddle-points/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/saddle-points/src/test/java/MatrixTest.java b/exercises/practice/saddle-points/src/test/java/MatrixTest.java index 6bcb5afcb..56b5087fc 100644 --- a/exercises/practice/saddle-points/src/test/java/MatrixTest.java +++ b/exercises/practice/saddle-points/src/test/java/MatrixTest.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 MatrixTest { @Test + @DisplayName("Can identify single saddle point") public void testCanIdentifySingleSaddlePoint() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(9, 8, 7), @@ -26,6 +28,7 @@ public void testCanIdentifySingleSaddlePoint() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify that empty matrix has no saddle points") public void testCanIdentifyThatEmptyMatrixHasNoSaddlePoints() { Matrix matrix = new Matrix(new ArrayList<>()); @@ -36,6 +39,7 @@ public void testCanIdentifyThatEmptyMatrixHasNoSaddlePoints() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify lack of saddle points when there are none") public void testCanIdentifyLackOfSaddlePointsWhenThereAreNone() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(1, 2, 3), @@ -50,6 +54,7 @@ public void testCanIdentifyLackOfSaddlePointsWhenThereAreNone() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify multiple saddle points in a column") public void testCanIdentifyMultipleSaddlePointsInAColumn() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(4, 5, 4), @@ -68,6 +73,7 @@ public void testCanIdentifyMultipleSaddlePointsInAColumn() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify multiple saddle points in a Row") public void testCanIdentifyMultipleSaddlePointsInARow() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(6, 7, 8), @@ -86,6 +92,7 @@ public void testCanIdentifyMultipleSaddlePointsInARow() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify saddle point in bottom right corner") public void testCanIdentifySaddlePointInBottomRightCorner() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(8, 7, 9), @@ -100,6 +107,7 @@ public void testCanIdentifySaddlePointInBottomRightCorner() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify saddle points in a non square matrix") public void testCanIdentifySaddlePointsInANonSquareMatrix() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(3, 1, 3), @@ -116,6 +124,7 @@ public void testCanIdentifySaddlePointsInANonSquareMatrix() { @Disabled("Remove to run test") @Test + @DisplayName("Can identify that saddle points in a single column matrix are those with the minimum value") public void testCanIdentifyThatSaddlePointsInASingleColumnMatrixAreThoseWithMinimumValue() { Matrix matrix = new Matrix(Arrays.asList( Collections.singletonList(2), @@ -134,6 +143,7 @@ public void testCanIdentifyThatSaddlePointsInASingleColumnMatrixAreThoseWithMini @Disabled("Remove to run test") @Test + @DisplayName("Can identify that saddle points in a single row matrix are those with the maximum value") public void testCanIdentifyThatSaddlePointsInASingleRowMatrixAreThoseWithMaximumValue() { Matrix matrix = new Matrix(Arrays.asList( Arrays.asList(2, 5, 3, 5) 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/satellite/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/satellite/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/satellite/gradlew b/exercises/practice/satellite/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/satellite/gradlew +++ b/exercises/practice/satellite/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/satellite/gradlew.bat b/exercises/practice/satellite/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/satellite/gradlew.bat +++ b/exercises/practice/satellite/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/satellite/src/test/java/SatelliteTest.java b/exercises/practice/satellite/src/test/java/SatelliteTest.java index 52bd9e97c..7b3c62d93 100644 --- a/exercises/practice/satellite/src/test/java/SatelliteTest.java +++ b/exercises/practice/satellite/src/test/java/SatelliteTest.java @@ -3,12 +3,14 @@ import java.util.List; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class SatelliteTest { Satellite satellite = new Satellite(); @Test + @DisplayName("Empty tree") public void emptyTree() { List preorder = List.of(); List inorder = List.of(); @@ -22,6 +24,7 @@ public void emptyTree() { @Disabled("Remove to run test") @Test + @DisplayName("Tree with one item") public void treeWithOneItem() { List preorder = List.of('a'); List inorder = List.of('a'); @@ -35,6 +38,7 @@ public void treeWithOneItem() { @Disabled("Remove to run test") @Test + @DisplayName("Tree with many items") public void treeWithManyItems() { List preorder = List.of('a', 'i', 'x', 'f', 'r'); List inorder = List.of('i', 'a', 'f', 'x', 'r'); @@ -48,6 +52,7 @@ public void treeWithManyItems() { @Disabled("Remove to run test") @Test + @DisplayName("Reject traversals of different length") public void rejectTraversalsOfDifferentLengths() { List preorder = List.of('a', 'b'); List inorder = List.of('b', 'a', 'r'); @@ -60,6 +65,7 @@ public void rejectTraversalsOfDifferentLengths() { @Disabled("Remove to run test") @Test + @DisplayName("Reject inconsistent traversals of same length") public void rejectInconsistentTraversalsOfSameLength() { List preorder = List.of('x', 'y', 'z'); List inorder = List.of('a', 'b', 'c'); @@ -71,6 +77,7 @@ public void rejectInconsistentTraversalsOfSameLength() { @Disabled("Remove to run test") @Test + @DisplayName("Reject traversals with repeated items") public void rejectTraversalsWithRepeatedItems() { List preorder = List.of('a', 'b', 'a'); List inorder = List.of('b', 'a', 'a'); diff --git a/exercises/practice/satellite/src/test/java/TreeTest.java b/exercises/practice/satellite/src/test/java/TreeTest.java index d51389ac5..eb091bfe4 100644 --- a/exercises/practice/satellite/src/test/java/TreeTest.java +++ b/exercises/practice/satellite/src/test/java/TreeTest.java @@ -1,5 +1,6 @@ import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class TreeTest { @@ -15,6 +16,7 @@ public void inorder() { .containsExactly('A', 'B', 'C', 'D', 'E', 'F'); } + @Disabled("Remove to run test") @Test public void preorder() { Tree tree = new Tree( @@ -27,6 +29,7 @@ public void preorder() { .containsExactly('A', 'B', 'C', 'D', 'E', 'F'); } + @Disabled("Remove to run test") @Test public void postorder() { Tree tree = new Tree( 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/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/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/say/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/say/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/say/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/say/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/say/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/say/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/say/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/say/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/say/gradlew b/exercises/practice/say/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/say/gradlew +++ b/exercises/practice/say/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/say/gradlew.bat b/exercises/practice/say/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/say/gradlew.bat +++ b/exercises/practice/say/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/say/src/test/java/SayTest.java b/exercises/practice/say/src/test/java/SayTest.java index 9e7090d53..2a999c517 100644 --- a/exercises/practice/say/src/test/java/SayTest.java +++ b/exercises/practice/say/src/test/java/SayTest.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.Test; import static org.assertj.core.api.Assertions.*; @@ -8,102 +9,119 @@ public class SayTest { private Say say = new Say(); @Test + @DisplayName("zero") public void zero() { assertThat(say.say(0)).isEqualTo("zero"); } @Disabled("Remove to run test") @Test + @DisplayName("one") public void one() { assertThat(say.say(1)).isEqualTo("one"); } @Disabled("Remove to run test") @Test + @DisplayName("fourteen") public void fourteen() { assertThat(say.say(14)).isEqualTo("fourteen"); } @Disabled("Remove to run test") @Test + @DisplayName("twenty") public void twenty() { assertThat(say.say(20)).isEqualTo("twenty"); } @Disabled("Remove to run test") @Test + @DisplayName("twenty-two") public void twentyTwo() { assertThat(say.say(22)).isEqualTo("twenty-two"); } @Disabled("Remove to run test") @Test + @DisplayName("thirty") public void thirty() { assertThat(say.say(30)).isEqualTo("thirty"); } @Disabled("Remove to run test") @Test + @DisplayName("ninety-nine") public void ninetyNine() { assertThat(say.say(99)).isEqualTo("ninety-nine"); } @Disabled("Remove to run test") @Test + @DisplayName("one hundred") public void oneHundred() { assertThat(say.say(100)).isEqualTo("one hundred"); } @Disabled("Remove to run test") @Test + @DisplayName("one hundred twenty-three") public void oneHundredTwentyThree() { assertThat(say.say(123)).isEqualTo("one hundred twenty-three"); } @Disabled("Remove to run test") @Test + @DisplayName("two hundred") public void twoHundred() { assertThat(say.say(200)).isEqualTo("two hundred"); } @Disabled("Remove to run test") @Test + @DisplayName("nine hundred ninety-nine") public void nineHundredNinetyNine() { assertThat(say.say(999)).isEqualTo("nine hundred ninety-nine"); } @Disabled("Remove to run test") @Test + @DisplayName("one thousand") public void oneThousand() { assertThat(say.say(1_000)).isEqualTo("one thousand"); } @Disabled("Remove to run test") @Test + @DisplayName("one thousand two hundred thirty-four") public void oneThousandTwoHundredThirtyFour() { assertThat(say.say(1_234)).isEqualTo("one thousand two hundred thirty-four"); } @Disabled("Remove to run test") @Test + @DisplayName("one million") public void oneMillion() { assertThat(say.say(1_000_000)).isEqualTo("one million"); } @Disabled("Remove to run test") @Test + @DisplayName("one million two thousand three hundred forty-five") public void oneMillionTwoThousandThreeHundredFortyFive() { assertThat(say.say(1_002_345)).isEqualTo("one million two thousand three hundred forty-five"); } @Disabled("Remove to run test") @Test + @DisplayName("one billion") public void oneBillion() { assertThat(say.say(1_000_000_000)).isEqualTo("one billion"); } @Disabled("Remove to run test") @Test + @DisplayName("a big number") public void nineHundredEightySevenBillionSixHundredFiftyFourThreeHundredTwentyOneThousandOneHundredTwentyThree() { assertThat(say.say(987_654_321_123L)) .isEqualTo("nine hundred eighty-seven billion six hundred fifty-four million" + @@ -112,6 +130,7 @@ public void nineHundredEightySevenBillionSixHundredFiftyFourThreeHundredTwentyOn @Disabled("Remove to run test") @Test + @DisplayName("numbers below zero are out of range") public void illegalNegativeNumber() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> say.say(-1)); @@ -119,6 +138,7 @@ public void illegalNegativeNumber() { @Disabled("Remove to run test") @Test + @DisplayName("numbers above 999,999,999,999 are out of range") public void illegalTooBigNumber() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> say.say(1_000_000_000_000L)); diff --git a/exercises/practice/scrabble-score/.approaches/if-statements/content.md b/exercises/practice/scrabble-score/.approaches/if-statements/content.md index ae274fe8c..14d7c2ad8 100644 --- a/exercises/practice/scrabble-score/.approaches/if-statements/content.md +++ b/exercises/practice/scrabble-score/.approaches/if-statements/content.md @@ -35,21 +35,17 @@ class Scrabble { ``` This approach defines a [`private`][private] [`final`][final] variable to be returned by the `getScore()` method. -It is `private` because it does not need to be directly accessed from outside the class, and it is `final` -because its value does not need to be changed once it is set. +It is `private` because it does not need to be directly accessed from outside the class, and it is `final` because its value does not need to be changed once it is set. In the constructor, a local variable is defined for being updated in the [`for` loop][for-loop]. ~~~~exercism/note -Using the same for a variable in a nested local scope that is used in its enclosing higher scope is called -[variable shadowing](https://www.geeksforgeeks.org/shadowing-in-java/). +Using the same for a variable in a nested local scope that is used in its enclosing higher scope is called [variable shadowing](https://www.geeksforgeeks.org/shadowing-in-java/). ~~~~ The variable is updated by a series of `if` statements that checks each letter of the uppercased word. -The letter is selected as a `String` by the [`substring()`][substring] method and is passed to the -[`contains()`][contains] method for the `String` representing the letters for a particular score. -The first `if` statement checks for the most common letters, and each succeeding `if` statement -checks for letters less common than the statement before it. +The letter is selected as a `String` by the [`substring()`][substring] method and is passed to the [`contains()`][contains] method for the `String` representing the letters for a particular score. +The first `if` statement checks for the most common letters, and each succeeding `if` statement checks for letters less common than the statement before it. When the loop is done, the class-level `score` variable is set to the value of the local `score` variable. [private]: https://en.wikibooks.org/wiki/Java_Programming/Keywords/private diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md index f0254b392..dd3a630c1 100644 --- a/exercises/practice/scrabble-score/.approaches/introduction.md +++ b/exercises/practice/scrabble-score/.approaches/introduction.md @@ -1,17 +1,14 @@ # Introduction -There are various idiomatiuc ways to solve Scrabble Score. -The approaches could be to use a series of `if` statements, or a single `switch` statment. +There are various idiomatic ways to solve Scrabble Score. +The approaches could be to use a series of `if` statements, or a single `switch` statement. Another approach could be to look up the score in a `HashMap` from inside the `reduce()` method. ## General guidance -Regardless of the approach used, one thing to look out for is to whether to calculate the score -in the constructor (or a method called by the constructor) or in the `getScore()` method. -A benefit to calculating in the constructor is that the score is calculated only once, -no matter how many times `getScore()` is called. -A benefit to calculating in `getScore()` is that, if it is not called, -then the calculation never has to be performed. +Regardless of the approach used, one thing to look out for is to whether to calculate the score in the constructor (or a method called by the constructor) or in the `getScore()` method. +A benefit to calculating in the constructor is that the score is calculated only once, no matter how many times `getScore()` is called. +A benefit to calculating in `getScore()` is that, if it is not called, then the calculation never has to be performed. But then, in that case, why instantiate the `Scrabble` object at all? ## Approach: `if` statements @@ -149,8 +146,7 @@ For more information, check the [`HashMap` with `reduce()` approach][approach-ma ## Which approach to use? -Since benchmarking with the [Java Microbenchmark Harness][jmh] is currently outside the scope of this document, -the choice between the approaches can be made by perceived readability. +Since benchmarking with the [Java Microbenchmark Harness][jmh] is currently outside the scope of this document, the choice between the approaches can be made by perceived readability. [approach-if-statements]: https://exercism.org/tracks/java/exercises/scrabble-score/approaches/if-statements [approach-switch-statement]: https://exercism.org/tracks/java/exercises/scrabble-score/approaches/switch-statement diff --git a/exercises/practice/scrabble-score/.approaches/map-reduce/content.md b/exercises/practice/scrabble-score/.approaches/map-reduce/content.md index af19d9eea..108c67df9 100644 --- a/exercises/practice/scrabble-score/.approaches/map-reduce/content.md +++ b/exercises/practice/scrabble-score/.approaches/map-reduce/content.md @@ -46,25 +46,23 @@ class Scrabble { This approach starts by importing from packages for what is needed. A [`private`][private] [`final`][final] variable is defined to be returned by the `getScore()` method. -It is `private` because it does not need to be directly accessed from outside the class, and it is `final` -because its value does not need to be changed once it is set. +It is `private` because it does not need to be directly accessed from outside the class, and it is `final` because its value does not need to be changed once it is set. Several `private` `final` [`static`][static] variables are then defined: -a [`HashMap`][hashmap] to hold the lookups of the scores for the letters; -a `String` array of the letters grouped by their common score; -and an `int` array of the scores for the letters. + +- a [`HashMap`][hashmap] to hold the lookups of the scores for the letters +- a `String` array of the letters grouped by their common score +- an `int` array of the scores for the letters + They are `static` because they don't need to differ between object instantiations, so they can belong to the class itself. -In a [static block][static-block], the [`IntStream.range()`][range] method is called to iterate an index from `0` -up to but including the length of the array of letters. -In a [`forEach()`][foreach] method, each index value is passed into a [lambda][lambda] which calls the [`chars{}`][chars] -method on each string at the index of the letters array. +In a [static block][static-block], the [`IntStream.range()`][range] method is called to iterate an index from `0` up to but including the length of the array of letters. +In a [`forEach()`][foreach] method, each index value is passed into a [lambda][lambda] which calls the [`chars{}`][chars] method on each string at the index of the letters array. In another `forEach`, each letter is passed into a lambda that adds the letter and its corresponding score to the `HashMap`. This works because the groups of letters and their scores are at the same index in their respective arrays. In the constructor, `chars()` is called on the uppercased word and chained to the [`reduce()`][reduce] method. -The accumulator is initialized to `0`, and the accumulator and each letter is passed to a lambda that adds the score -looked up from the `HashMap` for the letter. +The accumulator is initialized to `0`, and the accumulator and each letter is passed to a lambda that adds the score looked up from the `HashMap` for the letter. The score variable is set to the value returned from `reduce()`, which is the value of its accumulator. [private]: https://en.wikibooks.org/wiki/Java_Programming/Keywords/private diff --git a/exercises/practice/scrabble-score/.approaches/switch-statement/content.md b/exercises/practice/scrabble-score/.approaches/switch-statement/content.md index 30e084a36..80205cfe5 100644 --- a/exercises/practice/scrabble-score/.approaches/switch-statement/content.md +++ b/exercises/practice/scrabble-score/.approaches/switch-statement/content.md @@ -45,26 +45,22 @@ class Scrabble { ``` This approach defines a [`private`][private] [`final`][final] variable to be returned by the `getScore()` method. -It is `private` because it does not need to be directly accessed from outside the class, and it is `final` -because its value does not need to be changed once it is set. +It is `private` because it does not need to be directly accessed from outside the class, and it is `final` because its value does not need to be changed once it is set. In the constructor, a local variable is defined for being updated in the [`for` loop][for-loop]. ~~~~exercism/note -Using the same for a variable in a nested local scope that is used in its enclosing higher scope is called -[variable shadowing](https://www.geeksforgeeks.org/shadowing-in-java/). +Using the same name for a variable in a nested local scope that is used in its enclosing higher scope is called [variable shadowing](https://www.geeksforgeeks.org/shadowing-in-java/). ~~~~ The variable is updated by a [`switch`][switch] statement that checks each letter of the lowercased word. ~~~~exercism/note -If most of the input will already be lower case, it is a bit more performant to normalize the input as lowercased, -since fewer characters will need to be changed. +If most of the input will already be lower case, it is a bit more performant to normalize the input as lowercased, since fewer characters will need to be changed. However, it may be considered that to use upper case letters is more readable. ~~~~ -The letter is selected as a `char` by the [`charAt()`][charat] method and is passed to the -`switch`, with each case representing the letters for a particular score. +The letter is selected as a `char` by the [`charAt()`][charat] method and is passed to the `switch`, with each case representing the letters for a particular score. When the loop is done, the class-level `score` variable is set to the value of the local `score` variable. [private]: https://en.wikibooks.org/wiki/Java_Programming/Keywords/private 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/scrabble-score/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/scrabble-score/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/scrabble-score/gradlew b/exercises/practice/scrabble-score/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/scrabble-score/gradlew +++ b/exercises/practice/scrabble-score/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/scrabble-score/gradlew.bat b/exercises/practice/scrabble-score/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/scrabble-score/gradlew.bat +++ b/exercises/practice/scrabble-score/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/scrabble-score/src/test/java/ScrabbleScoreTest.java b/exercises/practice/scrabble-score/src/test/java/ScrabbleScoreTest.java index 1be75ea9b..9fc05c0f5 100644 --- a/exercises/practice/scrabble-score/src/test/java/ScrabbleScoreTest.java +++ b/exercises/practice/scrabble-score/src/test/java/ScrabbleScoreTest.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 ScrabbleScoreTest { @Test + @DisplayName("lowercase letter") public void testALowerCaseLetter() { Scrabble scrabble = new Scrabble("a"); assertThat(scrabble.getScore()).isEqualTo(1); @@ -13,6 +15,7 @@ public void testALowerCaseLetter() { @Disabled("Remove to run test") @Test + @DisplayName("uppercase letter") public void testAUpperCaseLetter() { Scrabble scrabble = new Scrabble("A"); assertThat(scrabble.getScore()).isEqualTo(1); @@ -20,6 +23,7 @@ public void testAUpperCaseLetter() { @Disabled("Remove to run test") @Test + @DisplayName("valuable letter") public void testAValuableLetter() { Scrabble scrabble = new Scrabble("f"); assertThat(scrabble.getScore()).isEqualTo(4); @@ -27,6 +31,7 @@ public void testAValuableLetter() { @Disabled("Remove to run test") @Test + @DisplayName("short word") public void testAShortWord() { Scrabble scrabble = new Scrabble("at"); assertThat(scrabble.getScore()).isEqualTo(2); @@ -34,6 +39,7 @@ public void testAShortWord() { @Disabled("Remove to run test") @Test + @DisplayName("short, valuable word") public void testAShortValuableWord() { Scrabble scrabble = new Scrabble("zoo"); assertThat(scrabble.getScore()).isEqualTo(12); @@ -41,6 +47,7 @@ public void testAShortValuableWord() { @Disabled("Remove to run test") @Test + @DisplayName("medium word") public void testAMediumWord() { Scrabble scrabble = new Scrabble("street"); assertThat(scrabble.getScore()).isEqualTo(6); @@ -48,6 +55,7 @@ public void testAMediumWord() { @Disabled("Remove to run test") @Test + @DisplayName("medium, valuable word") public void testAMediumValuableWord() { Scrabble scrabble = new Scrabble("quirky"); assertThat(scrabble.getScore()).isEqualTo(22); @@ -55,6 +63,7 @@ public void testAMediumValuableWord() { @Disabled("Remove to run test") @Test + @DisplayName("long, mixed-case word") public void testALongMixCaseWord() { Scrabble scrabble = new Scrabble("OxyphenButazone"); assertThat(scrabble.getScore()).isEqualTo(41); @@ -62,6 +71,7 @@ public void testALongMixCaseWord() { @Disabled("Remove to run test") @Test + @DisplayName("english-like word") public void testAEnglishLikeWord() { Scrabble scrabble = new Scrabble("pinata"); assertThat(scrabble.getScore()).isEqualTo(8); @@ -69,6 +79,7 @@ public void testAEnglishLikeWord() { @Disabled("Remove to run test") @Test + @DisplayName("empty input") public void testAnEmptyInput() { Scrabble scrabble = new Scrabble(""); assertThat(scrabble.getScore()).isEqualTo(0); @@ -76,6 +87,7 @@ public void testAnEmptyInput() { @Disabled("Remove to run test") @Test + @DisplayName("entire alphabet available") public void testEntireAlphabetAvailable() { Scrabble scrabble = new Scrabble("abcdefghijklmnopqrstuvwxyz"); assertThat(scrabble.getScore()).isEqualTo(87); 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/secret-handshake/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/secret-handshake/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/secret-handshake/gradlew b/exercises/practice/secret-handshake/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/secret-handshake/gradlew +++ b/exercises/practice/secret-handshake/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/secret-handshake/gradlew.bat b/exercises/practice/secret-handshake/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/secret-handshake/gradlew.bat +++ b/exercises/practice/secret-handshake/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/secret-handshake/src/test/java/HandshakeCalculatorTest.java b/exercises/practice/secret-handshake/src/test/java/HandshakeCalculatorTest.java index 286bff45b..a1cc3ce32 100644 --- a/exercises/practice/secret-handshake/src/test/java/HandshakeCalculatorTest.java +++ b/exercises/practice/secret-handshake/src/test/java/HandshakeCalculatorTest.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,30 +9,35 @@ public class HandshakeCalculatorTest { private final HandshakeCalculator handshakeCalculator = new HandshakeCalculator(); @Test + @DisplayName("wink for 1") public void testThatInput1YieldsAWink() { assertThat(handshakeCalculator.calculateHandshake(1)).containsExactly(Signal.WINK); } @Disabled("Remove to run test") @Test + @DisplayName("double blink for 10") public void testThatInput2YieldsADoubleBlink() { assertThat(handshakeCalculator.calculateHandshake(2)).containsExactly(Signal.DOUBLE_BLINK); } @Disabled("Remove to run test") @Test + @DisplayName("close your eyes for 100") public void testThatInput4YieldsACloseYourEyes() { assertThat(handshakeCalculator.calculateHandshake(4)).containsExactly(Signal.CLOSE_YOUR_EYES); } @Disabled("Remove to run test") @Test + @DisplayName("jump for 1000") public void testThatInput8YieldsAJump() { assertThat(handshakeCalculator.calculateHandshake(8)).containsExactly(Signal.JUMP); } @Disabled("Remove to run test") @Test + @DisplayName("combine two actions") public void testAnInputThatYieldsTwoActions() { assertThat(handshakeCalculator.calculateHandshake(3)) .containsExactly(Signal.WINK, Signal.DOUBLE_BLINK); @@ -39,6 +45,7 @@ public void testAnInputThatYieldsTwoActions() { @Disabled("Remove to run test") @Test + @DisplayName("reverse two actions") public void testAnInputThatYieldsTwoReversedActions() { assertThat(handshakeCalculator.calculateHandshake(19)) .containsExactly(Signal.DOUBLE_BLINK, Signal.WINK); @@ -46,18 +53,21 @@ public void testAnInputThatYieldsTwoReversedActions() { @Disabled("Remove to run test") @Test + @DisplayName("reversing one action gives the same action") public void testReversingASingleActionYieldsTheSameAction() { assertThat(handshakeCalculator.calculateHandshake(24)).containsExactly(Signal.JUMP); } @Disabled("Remove to run test") @Test + @DisplayName("reversing no actions still gives no actions") public void testReversingNoActionsYieldsNoActions() { assertThat(handshakeCalculator.calculateHandshake(16)).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("all possible actions") public void testInputThatYieldsAllActions() { assertThat(handshakeCalculator.calculateHandshake(15)) .containsExactly(Signal.WINK, Signal.DOUBLE_BLINK, Signal.CLOSE_YOUR_EYES, Signal.JUMP); @@ -65,6 +75,7 @@ public void testInputThatYieldsAllActions() { @Disabled("Remove to run test") @Test + @DisplayName("reverse all possible actions") public void testInputThatYieldsAllActionsReversed() { assertThat(handshakeCalculator.calculateHandshake(31)) .containsExactly(Signal.JUMP, Signal.CLOSE_YOUR_EYES, Signal.DOUBLE_BLINK, Signal.WINK); @@ -72,12 +83,18 @@ public void testInputThatYieldsAllActionsReversed() { @Disabled("Remove to run test") @Test + @DisplayName("do nothing for zero") public void testThatInput0YieldsNoActions() { assertThat(handshakeCalculator.calculateHandshake(0)).isEmpty(); } + /* The following tests diverge from the canonical test data to test numbers with binary representation with + * more than five digits are correctly handled. For more details, check out issue #1965 here: + * (https://github.com/exercism/java/issues/1965). + */ @Disabled("Remove to run test") @Test + @DisplayName("handles input with more than five bits with reversal") public void testThatHandlesMoreThanFiveBinaryPlacesWithReversal() { assertThat(handshakeCalculator.calculateHandshake(51)) .containsExactly(Signal.DOUBLE_BLINK, Signal.WINK); @@ -85,6 +102,7 @@ public void testThatHandlesMoreThanFiveBinaryPlacesWithReversal() { @Disabled("Remove to run test") @Test + @DisplayName("handles input with more than five bits without reversal") public void testThatHandlesMoreThanFiveBinaryPlacesWithoutReversal() { assertThat(handshakeCalculator.calculateHandshake(35)) .containsExactly(Signal.WINK, Signal.DOUBLE_BLINK); @@ -92,6 +110,7 @@ public void testThatHandlesMoreThanFiveBinaryPlacesWithoutReversal() { @Disabled("Remove to run test") @Test + @DisplayName("all actions for input with more than five bits") public void testInputThatYieldsAllActionsFromMoreThanFiveBinaryPlaces() { assertThat(handshakeCalculator.calculateHandshake(111)) .containsExactly(Signal.WINK, Signal.DOUBLE_BLINK, Signal.CLOSE_YOUR_EYES, Signal.JUMP); 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/series/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/series/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/series/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/series/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/series/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/series/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/series/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/series/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/series/gradlew b/exercises/practice/series/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/series/gradlew +++ b/exercises/practice/series/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/series/gradlew.bat b/exercises/practice/series/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/series/gradlew.bat +++ b/exercises/practice/series/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/series/src/test/java/SeriesTest.java b/exercises/practice/series/src/test/java/SeriesTest.java index 2009d437b..726e9fd24 100644 --- a/exercises/practice/series/src/test/java/SeriesTest.java +++ b/exercises/practice/series/src/test/java/SeriesTest.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; @@ -11,6 +12,7 @@ public class SeriesTest { @Test + @DisplayName("slices of one from one") public void slicesOfOneFromOne() { Series series = new Series("1"); List expected = Collections.singletonList("1"); @@ -20,6 +22,7 @@ public void slicesOfOneFromOne() { @Disabled("Remove to run test") @Test + @DisplayName("slices of one from two") public void slicesOfOneFromTwo() { Series series = new Series("12"); List expected = Arrays.asList("1", "2"); @@ -29,6 +32,7 @@ public void slicesOfOneFromTwo() { @Disabled("Remove to run test") @Test + @DisplayName("slices of two") public void slicesOfTwo() { Series series = new Series("35"); List expected = Collections.singletonList("35"); @@ -38,6 +42,7 @@ public void slicesOfTwo() { @Disabled("Remove to run test") @Test + @DisplayName("slices of two overlap") public void slicesOfTwoOverlap() { Series series = new Series("9142"); List expected = Arrays.asList("91", "14", "42"); @@ -47,6 +52,7 @@ public void slicesOfTwoOverlap() { @Disabled("Remove to run test") @Test + @DisplayName("slices can include duplicates") public void slicesIncludeDuplicates() { Series series = new Series("777777"); List expected = Arrays.asList( @@ -61,6 +67,7 @@ public void slicesIncludeDuplicates() { @Disabled("Remove to run test") @Test + @DisplayName("slices of a long series") public void slicesOfLongSeries() { Series series = new Series("918493904243"); List expected = Arrays.asList( @@ -79,6 +86,7 @@ public void slicesOfLongSeries() { @Disabled("Remove to run test") @Test + @DisplayName("slice length is too large") public void sliceLengthIsToolarge() { Series series = new Series("12345"); @@ -89,6 +97,7 @@ public void sliceLengthIsToolarge() { @Disabled("Remove to run test") @Test + @DisplayName("slice length is way too large") public void sliceLengthIsWayToolarge() { Series series = new Series("12345"); @@ -99,6 +108,7 @@ public void sliceLengthIsWayToolarge() { @Disabled("Remove to run test") @Test + @DisplayName("slice length cannot be zero") public void sliceLengthZero() { Series series = new Series("12345"); @@ -109,6 +119,7 @@ public void sliceLengthZero() { @Disabled("Remove to run test") @Test + @DisplayName("slice length cannot be negative") public void sliceLengthNegative() { Series series = new Series("123"); @@ -119,6 +130,7 @@ public void sliceLengthNegative() { @Disabled("Remove to run test") @Test + @DisplayName("empty series is invalid") public void emptySeries() { assertThatExceptionOfType(IllegalArgumentException.class) diff --git a/exercises/practice/sgf-parsing/.meta/config.json b/exercises/practice/sgf-parsing/.meta/config.json index aec3220c4..a793a42b4 100644 --- a/exercises/practice/sgf-parsing/.meta/config.json +++ b/exercises/practice/sgf-parsing/.meta/config.json @@ -2,6 +2,10 @@ "authors": [ "tlphat" ], + "contributors": [ + "jagdish-15", + "kahgoh" + ], "files": { "solution": [ "src/main/java/SgfParsing.java" diff --git a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java index 94422d3e7..6a9f4d684 100644 --- a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java +++ b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java @@ -29,26 +29,52 @@ private int parseFromIndex(String input, int index, SgfNode root) throws SgfPars StringBuilder buffer = new StringBuilder(); Map> properties = new HashMap<>(); String key = null; + boolean escape = false; + boolean inValue = false; while (index < input.length()) { - switch (input.charAt(index)) { - case '(': - index = addNewChild(input, index, root); - break; - case ')': - break; - case '[': - key = loadKeyFromBuffer(buffer, properties); - break; - case ']': - properties.get(key).add(popStringFromBuffer(buffer)); - if (input.charAt(index + 1) == ')') { - root.setProperties(properties); - return index + 1; - } - index = examineNextNode(input, index, root, properties); - break; - default: - index = appendCharToBuffer(input, index, buffer); + char nextChar = input.charAt(index); + if (escape) { + if (nextChar != '\n') { + appendChar(buffer, nextChar); + } + escape = false; + } else { + switch (nextChar) { + case '(': + if (inValue) { + buffer.append(nextChar); + } else { + index = addNewChild(input, index, root); + } + break; + case ')': + if (inValue) { + buffer.append(nextChar); + } + break; + case '[': + if (inValue) { + buffer.append(nextChar); + } else { + key = loadKeyFromBuffer(buffer, properties); + inValue = true; + } + break; + case ']': + properties.get(key).add(popStringFromBuffer(buffer)); + if (input.charAt(index + 1) == ')') { + root.setProperties(properties); + return index + 1; + } + index = examineNextNode(input, index, root, properties); + inValue = false; + break; + case '\\': + escape = true; + break; + default: + appendChar(buffer, nextChar); + } } ++index; } @@ -101,14 +127,13 @@ private int examineNextNode(String input, int index, SgfNode root, Map /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/sgf-parsing/gradlew.bat b/exercises/practice/sgf-parsing/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/sgf-parsing/gradlew.bat +++ b/exercises/practice/sgf-parsing/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java b/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java index d4246921b..afd458506 100644 --- a/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java +++ b/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java @@ -5,11 +5,13 @@ import java.util.Map; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; public class SgfParsingTest { @Test + @DisplayName("empty input") public void emptyInput() { String input = ""; assertThatExceptionOfType(SgfParsingException.class).as("tree missing") @@ -18,6 +20,7 @@ public void emptyInput() { @Test @Disabled("Remove to run test") + @DisplayName("tree with no nodes") public void treeWithNoNodes() { String input = "()"; assertThatExceptionOfType(SgfParsingException.class) @@ -27,6 +30,7 @@ public void treeWithNoNodes() { @Test @Disabled("Remove to run test") + @DisplayName("node without tree") public void nodeWithoutTree() { String input = ";"; assertThatExceptionOfType(SgfParsingException.class).as("tree missing") @@ -35,6 +39,7 @@ public void nodeWithoutTree() { @Test @Disabled("Remove to run test") + @DisplayName("node without properties") public void nodeWithoutProperties() throws SgfParsingException { String input = "(;)"; SgfNode expected = new SgfNode(); @@ -44,6 +49,7 @@ public void nodeWithoutProperties() throws SgfParsingException { @Test @Disabled("Remove to run test") + @DisplayName("single node tree") public void singleNodeTree() throws SgfParsingException { String input = "(;A[B])"; SgfNode expected = new SgfNode(Map.of("A", List.of("B"))); @@ -53,6 +59,7 @@ public void singleNodeTree() throws SgfParsingException { @Test @Disabled("Remove to run test") + @DisplayName("multiple properties") public void multipleProperties() throws SgfParsingException { String input = "(;A[b]C[d])"; SgfNode expected = new SgfNode(Map.of("A", List.of("b"), @@ -63,6 +70,7 @@ public void multipleProperties() throws SgfParsingException { @Test @Disabled("Remove to run test") + @DisplayName("properties without delimiter") public void propertiesWithoutDelimiter() { String input = "(;A)"; assertThatExceptionOfType(SgfParsingException.class).as("properties without delimiter") @@ -71,6 +79,7 @@ public void propertiesWithoutDelimiter() { @Test @Disabled("Remove to run test") + @DisplayName("all lowercase property") public void allLowercaseProperty() { String input = "(;a[b])"; assertThatExceptionOfType(SgfParsingException.class).as("property must be in uppercase") @@ -79,6 +88,7 @@ public void allLowercaseProperty() { @Test @Disabled("Remove to run test") + @DisplayName("upper and lowercase property") public void upperAndLowercaseProperty() { String input = "(;Aa[b])"; assertThatExceptionOfType(SgfParsingException.class).as("property must be in uppercase") @@ -87,6 +97,7 @@ public void upperAndLowercaseProperty() { @Test @Disabled("Remove to run test") + @DisplayName("two nodes") public void twoNodes() throws SgfParsingException { String input = "(;A[B];B[C])"; SgfNode expected = new SgfNode(Map.of("A", List.of("B")), @@ -99,6 +110,7 @@ public void twoNodes() throws SgfParsingException { @Test @Disabled("Remove to run test") + @DisplayName("two child trees") public void twoChildTrees() throws SgfParsingException { String input = "(;A[B](;B[C])(;C[D]))"; SgfNode expected = new SgfNode(Map.of("A", List.of("B")), @@ -112,6 +124,7 @@ public void twoChildTrees() throws SgfParsingException { @Test @Disabled("Remove to run test") + @DisplayName("multiple property values") public void multiplePropertyValues() throws SgfParsingException { String input = "(;A[b][c][d])"; SgfNode expected = new SgfNode(Map.of("A", List.of("b", "c", "d"))); @@ -121,11 +134,125 @@ public void multiplePropertyValues() throws SgfParsingException { @Test @Disabled("Remove to run test") - public void escapedProperty() throws SgfParsingException { - String input = "(;A[\\]b\nc\nd\t\te \n\\]])"; - SgfNode expected = new SgfNode(Map.of("A", List.of("]b\nc\nd\t\te \n]"))); + @DisplayName("within property values, whitespace characters such as tab are converted to spaces") + public void withinPropertyValueWhitespace() throws SgfParsingException { + String input = "(;A[hello\t\tworld])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("hello world"))); SgfNode actual = new SgfParsing().parse(input); assertThat(actual).isEqualTo(expected); } + @Test + @Disabled("Remove to run test") + @DisplayName("within property values, newlines remain as newlines") + public void withinPropertyValueNewline() throws SgfParsingException { + String input = "(;A[hello\n\nworld])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("hello\n\nworld"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("escaped closing bracket within property value becomes just a closing bracket") + public void escapedClosingBracket() throws SgfParsingException { + String input = "(;A[\\]])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("]"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("escaped backslash in property value becomes just a backslash") + public void escapedBacklash() throws SgfParsingException { + String input = "(;A[\\\\])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("\\"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("opening bracket within property value doesn't need to be escaped") + public void openingBracketNeedNotToBeEscaped() throws SgfParsingException { + String input = "(;A[x[y\\]z][foo]B[bar];C[baz])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("x[y]z", "foo"), + "B", List.of("bar")), + List.of( + new SgfNode(Map.of("C", List.of("baz"))) + )); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("semicolon in property value doesn't need to be escaped") + public void semicolonNeedNotToBeEscaped() throws SgfParsingException { + String input = "(;A[a;b][foo]B[bar];C[baz])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("a;b", "foo"), + "B", List.of("bar")), + List.of( + new SgfNode(Map.of("C", List.of("baz"))) + )); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("parentheses in property value don't need to be escaped") + public void paranthesesNeedNotToBeEscaped() throws SgfParsingException { + String input = "(;A[x(y)z][foo]B[bar];C[baz])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("x(y)z", "foo"), + "B", List.of("bar")), + List.of( + new SgfNode(Map.of("C", List.of("baz"))) + )); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("escaped tab in property value is converted to space") + public void escapedTab() throws SgfParsingException { + String input = "(;A[hello\\\tworld])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("hello world"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + @Test + @Disabled("Remove to run test") + @DisplayName("escaped newline in property value is converted to nothing at all") + public void escapedNewline() throws SgfParsingException { + String input = "(;A[hello\\\nworld])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("helloworld"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + + @Test + @Disabled("Remove to run test") + @DisplayName("escaped t and n in property value are just letters, not whitespace") + public void escapedTAndN() throws SgfParsingException { + String input = "(;A[\\t = t and \\n = n])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("t = t and n = n"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } + + + @Test + @Disabled("Remove to run test") + @DisplayName("mixing various kinds of whitespace and escaped characters in property value") + public void mixOfEscapedCharactersAndWhitespaces() throws SgfParsingException { + String input = "(;A[\\]b\nc\\\nd\t\te\\\\ \\\n\\]])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("]b\ncd e\\ ]"))); + SgfNode actual = new SgfParsing().parse(input); + assertThat(actual).isEqualTo(expected); + } } 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/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/sieve/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/sieve/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/sieve/gradlew b/exercises/practice/sieve/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/sieve/gradlew +++ b/exercises/practice/sieve/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/sieve/gradlew.bat b/exercises/practice/sieve/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/sieve/gradlew.bat +++ b/exercises/practice/sieve/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/sieve/src/test/java/SieveTest.java b/exercises/practice/sieve/src/test/java/SieveTest.java index b213ac0f7..529329323 100644 --- a/exercises/practice/sieve/src/test/java/SieveTest.java +++ b/exercises/practice/sieve/src/test/java/SieveTest.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 SieveTest { @Test + @DisplayName("no primes under two") public void noPrimesUnder2() { Sieve sieve = new Sieve(1); List expectedOutput = Collections.emptyList(); @@ -19,6 +21,7 @@ public void noPrimesUnder2() { @Disabled("Remove to run test") @Test + @DisplayName("find first prime") public void findFirstPrime() { Sieve sieve = new Sieve(2); List expectedOutput = Collections.singletonList(2); @@ -28,6 +31,7 @@ public void findFirstPrime() { @Disabled("Remove to run test") @Test + @DisplayName("find primes up to 10") public void findPrimesUpTo10() { Sieve sieve = new Sieve(10); List expectedOutput = Arrays.asList(2, 3, 5, 7); @@ -37,6 +41,7 @@ public void findPrimesUpTo10() { @Disabled("Remove to run test") @Test + @DisplayName("limit is prime") public void limitIsPrime() { Sieve sieve = new Sieve(13); List expectedOutput = Arrays.asList(2, 3, 5, 7, 11, 13); @@ -46,6 +51,7 @@ public void limitIsPrime() { @Disabled("Remove to run test") @Test + @DisplayName("find primes up to 1000") public void findPrimesUpTo1000() { Sieve sieve = new Sieve(1000); List expectedOutput = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, diff --git a/exercises/practice/simple-cipher/.docs/instructions.md b/exercises/practice/simple-cipher/.docs/instructions.md index 475af6182..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 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" } 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-cipher/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/simple-cipher/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/simple-cipher/gradlew b/exercises/practice/simple-cipher/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/simple-cipher/gradlew +++ b/exercises/practice/simple-cipher/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/simple-cipher/gradlew.bat b/exercises/practice/simple-cipher/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/simple-cipher/gradlew.bat +++ b/exercises/practice/simple-cipher/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/simple-cipher/src/test/java/SimpleCipherTest.java b/exercises/practice/simple-cipher/src/test/java/SimpleCipherTest.java index 1aadc854f..af7adbe4b 100644 --- a/exercises/practice/simple-cipher/src/test/java/SimpleCipherTest.java +++ b/exercises/practice/simple-cipher/src/test/java/SimpleCipherTest.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; @@ -18,6 +19,7 @@ public void setup() { * problem with shift ciphers, some characters will always output the key verbatim. */ @Test + @DisplayName("Can encode") public void randomKeyCipherCanEncode() { String plainText = "aaaaaaaaaa"; String cipherText = randomKeyCipher.getKey().substring(0, 10); @@ -26,6 +28,7 @@ public void randomKeyCipherCanEncode() { @Disabled("Remove to run test") @Test + @DisplayName("Can decode") public void randomKeyCipherCanDecode() { String cipherText = "aaaaaaaaaa"; assertThat(randomKeyCipher.decode(randomKeyCipher.getKey().substring(0, 10))) @@ -34,6 +37,10 @@ public void randomKeyCipherCanDecode() { @Disabled("Remove to run test") @Test + @DisplayName( + "Is reversible. I.e., if you apply decode in a encoded result, " + + "you must see the same plaintext encode parameter as a result of the decode method" + ) public void randomKeyCipherIsReversible() { String plainText = "abcdefghij"; assertThat(randomKeyCipher.decode(randomKeyCipher.encode(plainText))).isEqualTo(plainText); @@ -41,12 +48,14 @@ public void randomKeyCipherIsReversible() { @Disabled("Remove to run test") @Test + @DisplayName("Key is made only of lowercase letters") public void randomKeyCipherKeyIsLowercaseLetters() { assertThat(randomKeyCipher.getKey()).matches("^[a-z]+$"); } @Disabled("Remove to run test") @Test + @DisplayName("Can encode") public void substitutionCipherCanEncode() { String plainText = "aaaaaaaaaa"; String cipherText = "abcdefghij"; @@ -55,6 +64,7 @@ public void substitutionCipherCanEncode() { @Disabled("Remove to run test") @Test + @DisplayName("Can decode") public void substitutionCipherCanDecode() { String plainText = "abcdefghij"; String cipherText = "aaaaaaaaaa"; @@ -63,6 +73,10 @@ public void substitutionCipherCanDecode() { @Disabled("Remove to run test") @Test + @DisplayName( + "Is reversible. I.e., if you apply decode in a encoded result, " + + "you must see the same plaintext encode parameter as a result of the decode method" + ) public void substitutionCipherIsReversibleGivenKey() { String plainText = "abcdefghij"; assertThat(substitutionCipher.decode(substitutionCipher.encode(plainText))).isEqualTo(plainText); @@ -70,6 +84,7 @@ public void substitutionCipherIsReversibleGivenKey() { @Disabled("Remove to run test") @Test + @DisplayName("Can double shift encode") public void substitutionCipherCanDoubleShiftEncode() { String plainText = "iamapandabear"; String cipherText = "qayaeaagaciai"; @@ -78,6 +93,7 @@ public void substitutionCipherCanDoubleShiftEncode() { @Disabled("Remove to run test") @Test + @DisplayName("Can wrap on encode") public void substitutionCipherCanWrapEncode() { String plainText = "zzzzzzzzzz"; String cipherText = "zabcdefghi"; @@ -86,6 +102,7 @@ public void substitutionCipherCanWrapEncode() { @Disabled("Remove to run test") @Test + @DisplayName("Can wrap on decode") public void substitutionCipherCanWrapDecode() { String plainText = "zabcdefghi"; String cipherText = "zzzzzzzzzz"; @@ -94,6 +111,7 @@ public void substitutionCipherCanWrapDecode() { @Disabled("Remove to run test") @Test + @DisplayName("Can decode messages longer than the key") public void substitutionCipherMessageLongerThanKey() { String plainText = "iamapandabear"; String key = "abc"; 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/simple-linked-list/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/simple-linked-list/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/simple-linked-list/gradlew b/exercises/practice/simple-linked-list/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/simple-linked-list/gradlew +++ b/exercises/practice/simple-linked-list/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/simple-linked-list/gradlew.bat b/exercises/practice/simple-linked-list/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/simple-linked-list/gradlew.bat +++ b/exercises/practice/simple-linked-list/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/simple-linked-list/src/test/java/SimpleLinkedListTest.java b/exercises/practice/simple-linked-list/src/test/java/SimpleLinkedListTest.java index 76e219ff8..109d3b3a7 100644 --- a/exercises/practice/simple-linked-list/src/test/java/SimpleLinkedListTest.java +++ b/exercises/practice/simple-linked-list/src/test/java/SimpleLinkedListTest.java @@ -1,14 +1,16 @@ -import static org.assertj.core.api.Assertions.assertThat; -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.NoSuchElementException; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + public class SimpleLinkedListTest { @Test + @DisplayName("A new list is empty") public void aNewListIsEmpty() { SimpleLinkedList list = new SimpleLinkedList<>(); assertThat(list.size()).isEqualTo(0); @@ -16,6 +18,7 @@ public void aNewListIsEmpty() { @Disabled("Remove to run test") @Test + @DisplayName("Create list from array") public void canCreateFromArray() { Character[] values = new Character[]{'1', '2', '3'}; SimpleLinkedList list = new SimpleLinkedList(values); @@ -24,6 +27,7 @@ public void canCreateFromArray() { @Disabled("Remove to run test") @Test + @DisplayName("Popping an empty list throws NoSuchElementException") public void popOnEmptyListWillThrow() { SimpleLinkedList list = new SimpleLinkedList(); @@ -32,6 +36,7 @@ public void popOnEmptyListWillThrow() { @Disabled("Remove to run test") @Test + @DisplayName("Pop returns last added element (LIFO)") public void popReturnsLastAddedElement() { SimpleLinkedList list = new SimpleLinkedList(); list.push(9); @@ -44,6 +49,7 @@ public void popReturnsLastAddedElement() { @Disabled("Remove to run test") @Test + @DisplayName("Reverse reverses the list order") public void reverseReversesList() { SimpleLinkedList list = new SimpleLinkedList(); list.push("9"); @@ -61,6 +67,7 @@ public void reverseReversesList() { @Disabled("Remove to run test") @Test + @DisplayName("Can return list as an array") public void canReturnListAsArray() { SimpleLinkedList list = new SimpleLinkedList(); list.push('9'); @@ -74,6 +81,7 @@ public void canReturnListAsArray() { @Disabled("Remove to run test") @Test + @DisplayName("Can return empty list as an empty array") public void canReturnEmptyListAsEmptyArray() { SimpleLinkedList list = new SimpleLinkedList(); Object[] expected = {}; 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/space-age/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/space-age/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/space-age/gradlew b/exercises/practice/space-age/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/space-age/gradlew +++ b/exercises/practice/space-age/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/space-age/gradlew.bat b/exercises/practice/space-age/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/space-age/gradlew.bat +++ b/exercises/practice/space-age/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/space-age/src/test/java/SpaceAgeTest.java b/exercises/practice/space-age/src/test/java/SpaceAgeTest.java index 4b2a9d43a..248d6938e 100644 --- a/exercises/practice/space-age/src/test/java/SpaceAgeTest.java +++ b/exercises/practice/space-age/src/test/java/SpaceAgeTest.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 SpaceAgeTest { private static final double MAXIMUM_DELTA = 1E-02; @Test + @DisplayName("age on Earth") public void ageOnEarth() { SpaceAge age = new SpaceAge(1000000000); @@ -17,6 +19,7 @@ public void ageOnEarth() { @Disabled("Remove to run test") @Test + @DisplayName("age on Mercury") public void ageOnMercury() { SpaceAge age = new SpaceAge(2134835688); @@ -25,6 +28,7 @@ public void ageOnMercury() { @Disabled("Remove to run test") @Test + @DisplayName("age on Venus") public void ageOnVenus() { SpaceAge age = new SpaceAge(189839836); @@ -33,6 +37,7 @@ public void ageOnVenus() { @Disabled("Remove to run test") @Test + @DisplayName("age on Mars") public void ageOnMars() { SpaceAge age = new SpaceAge(2129871239L); @@ -41,6 +46,7 @@ public void ageOnMars() { @Disabled("Remove to run test") @Test + @DisplayName("age on Jupiter") public void ageOnJupiter() { SpaceAge age = new SpaceAge(901876382); @@ -49,6 +55,7 @@ public void ageOnJupiter() { @Disabled("Remove to run test") @Test + @DisplayName("age on Saturn") public void ageOnSaturn() { SpaceAge age = new SpaceAge(2000000000L); @@ -57,6 +64,7 @@ public void ageOnSaturn() { @Disabled("Remove to run test") @Test + @DisplayName("age on Uranus") public void ageOnUranus() { SpaceAge age = new SpaceAge(1210123456L); @@ -65,6 +73,7 @@ public void ageOnUranus() { @Disabled("Remove to run test") @Test + @DisplayName("age on Neptune") public void ageOnNeptune() { SpaceAge age = new SpaceAge(1821023456L); 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/spiral-matrix/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/spiral-matrix/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/spiral-matrix/gradlew b/exercises/practice/spiral-matrix/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/spiral-matrix/gradlew +++ b/exercises/practice/spiral-matrix/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/spiral-matrix/gradlew.bat b/exercises/practice/spiral-matrix/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/spiral-matrix/gradlew.bat +++ b/exercises/practice/spiral-matrix/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/spiral-matrix/src/test/java/SpiralMatrixBuilderTest.java b/exercises/practice/spiral-matrix/src/test/java/SpiralMatrixBuilderTest.java index ee9a971f9..215afdc10 100644 --- a/exercises/practice/spiral-matrix/src/test/java/SpiralMatrixBuilderTest.java +++ b/exercises/practice/spiral-matrix/src/test/java/SpiralMatrixBuilderTest.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,12 +15,14 @@ public void setUp() { } @Test + @DisplayName("empty spiral") public void testEmptySpiral() { assertThat(spiralMatrixBuilder.buildMatrixOfSize(0)).isEmpty(); } @Disabled("Remove to run test") @Test + @DisplayName("trivial spiral") public void testTrivialSpiral() { int[][] expected = { {1} @@ -30,6 +33,7 @@ public void testTrivialSpiral() { @Disabled("Remove to run test") @Test + @DisplayName("spiral of size 2") public void testSpiralOfSize2() { int[][] expected = { {1, 2}, @@ -41,6 +45,7 @@ public void testSpiralOfSize2() { @Disabled("Remove to run test") @Test + @DisplayName("spiral of size 3") public void testSpiralOfSize3() { int[][] expected = { {1, 2, 3}, @@ -53,6 +58,7 @@ public void testSpiralOfSize3() { @Disabled("Remove to run test") @Test + @DisplayName("spiral of size 4") public void testSpiralOfSize4() { int[][] expected = { { 1, 2, 3, 4}, @@ -66,6 +72,7 @@ public void testSpiralOfSize4() { @Disabled("Remove to run test") @Test + @DisplayName("spiral of size 5") public void testSpiralOfSize5() { int[][] expected = { { 1, 2, 3, 4, 5}, 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..6fb187621 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "EmmanuelBerkowicz" + ], + "files": { + "solution": [ + "src/main/java/SplitSecondStopwatch.java" + ], + "test": [ + "src/test/java/SplitSecondStopwatchTest.java" + ], + "example": [ + ".meta/src/reference/java/SplitSecondStopwatch.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/split-second-stopwatch/gradlew b/exercises/practice/split-second-stopwatch/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/split-second-stopwatch/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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/practice/square-root/.docs/instructions.md b/exercises/practice/square-root/.docs/instructions.md index e9905e9d4..d258b8687 100644 --- a/exercises/practice/square-root/.docs/instructions.md +++ b/exercises/practice/square-root/.docs/instructions.md @@ -1,13 +1,18 @@ # Instructions -Given a natural radicand, return its square root. +Your task is to calculate the square root of a given number. -Note that the term "radicand" refers to the number for which the root is to be determined. -That is, it is the number under the root symbol. +- Try to avoid using the pre-existing math libraries of your language. +- As input you'll be given a positive whole number, i.e. 1, 2, 3, 4… +- You are only required to handle cases where the result is a positive whole number. -Check out the Wikipedia pages on [square root][square-root] and [methods of computing square roots][computing-square-roots]. +Some potential approaches: -Recall also that natural numbers are positive real whole numbers (i.e. 1, 2, 3 and up). +- Linear or binary search for a number that gives the input number when squared. +- Successive approximation using Newton's or Heron's method. +- Calculating one digit at a time or one bit at a time. -[square-root]: https://en.wikipedia.org/wiki/Square_root +You can check out the Wikipedia pages on [integer square root][integer-square-root] and [methods of computing square roots][computing-square-roots] to help with choosing a method of calculation. + +[integer-square-root]: https://en.wikipedia.org/wiki/Integer_square_root [computing-square-roots]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots diff --git a/exercises/practice/square-root/.docs/introduction.md b/exercises/practice/square-root/.docs/introduction.md new file mode 100644 index 000000000..1d692934f --- /dev/null +++ b/exercises/practice/square-root/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +We are launching a deep space exploration rocket and we need a way to make sure the navigation system stays on target. + +As the first step in our calculation, we take a target number and find its square root (that is, the number that when multiplied by itself equals the target number). + +The journey will be very long. +To make the batteries last as long as possible, we had to make our rocket's onboard computer very power efficient. +Unfortunately that means that we can't rely on fancy math libraries and functions, as they use more power. +Instead we want to implement our own square root calculation. 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/square-root/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/square-root/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/square-root/gradlew b/exercises/practice/square-root/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/square-root/gradlew +++ b/exercises/practice/square-root/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/square-root/gradlew.bat b/exercises/practice/square-root/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/square-root/gradlew.bat +++ b/exercises/practice/square-root/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md b/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md index f525d3585..1a03ebb6c 100644 --- a/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md +++ b/exercises/practice/state-of-tic-tac-toe/.docs/instructions.md @@ -3,7 +3,7 @@ In this exercise, you're going to implement a program that determines the state of a [tic-tac-toe][] game. (_You may also know the game as "noughts and crosses" or "Xs and Os"._) -The games is played on a 3×3 grid. +The game is played on a 3×3 grid. Players take turns to place `X`s and `O`s on the grid. The game ends when one player has won by placing three of marks in a row, column, or along a diagonal of the grid, or when the entire grid is filled up. 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/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/state-of-tic-tac-toe/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/state-of-tic-tac-toe/gradlew b/exercises/practice/state-of-tic-tac-toe/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/state-of-tic-tac-toe/gradlew +++ b/exercises/practice/state-of-tic-tac-toe/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/state-of-tic-tac-toe/gradlew.bat b/exercises/practice/state-of-tic-tac-toe/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/state-of-tic-tac-toe/gradlew.bat +++ b/exercises/practice/state-of-tic-tac-toe/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/state-of-tic-tac-toe/src/test/java/StateOfTicTacToeTest.java b/exercises/practice/state-of-tic-tac-toe/src/test/java/StateOfTicTacToeTest.java index e5352ae50..d264db005 100644 --- a/exercises/practice/state-of-tic-tac-toe/src/test/java/StateOfTicTacToeTest.java +++ b/exercises/practice/state-of-tic-tac-toe/src/test/java/StateOfTicTacToeTest.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("Finished game where X won via left column victory") public void testFinishedGameWhereXWonViaLeftColumnVictory() { assertThat( @@ -23,6 +25,7 @@ public void testFinishedGameWhereXWonViaLeftColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via middle column victory") public void testFinishedGameWhereXWonViaMiddleColumnVictory() { assertThat( @@ -32,6 +35,7 @@ public void testFinishedGameWhereXWonViaMiddleColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via right column victory") public void testFinishedGameWhereXWonViaRightColumnVictory() { assertThat( @@ -41,6 +45,7 @@ public void testFinishedGameWhereXWonViaRightColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via left column victory") public void testFinishedGameWhereOWonViaLeftColumnVictory() { assertThat( @@ -50,6 +55,7 @@ public void testFinishedGameWhereOWonViaLeftColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via middle column victory") public void testFinishedGameWhereOWonViaMiddleColumnVictory() { assertThat( @@ -59,6 +65,7 @@ public void testFinishedGameWhereOWonViaMiddleColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via right column victory") public void testFinishedGameWhereOWonViaRightColumnVictory() { assertThat( @@ -68,6 +75,7 @@ public void testFinishedGameWhereOWonViaRightColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via top row victory") public void testFinishedGameWhereXWonViaTopRowVictory() { assertThat( @@ -77,6 +85,7 @@ public void testFinishedGameWhereXWonViaTopRowVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via middle row victory") public void testFinishedGameWhereXWonViaMiddleRowVictory() { assertThat( @@ -86,6 +95,7 @@ public void testFinishedGameWhereXWonViaMiddleRowVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via middle row victory") public void testFinishedGameWhereXWonViaBottomRowVictory() { assertThat( @@ -95,6 +105,7 @@ public void testFinishedGameWhereXWonViaBottomRowVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via top row victory") public void testFinishedGameWhereOWonViaTopRowVictory() { assertThat( @@ -104,6 +115,7 @@ public void testFinishedGameWhereOWonViaTopRowVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via middle row victory") public void testFinishedGameWhereOWonViaMiddleRowVictory() { assertThat( @@ -113,6 +125,7 @@ public void testFinishedGameWhereOWonViaMiddleRowVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via bottom row victory") public void testFinishedGameWhereOWonViaBottomRowVictory() { assertThat( @@ -122,6 +135,7 @@ public void testFinishedGameWhereOWonViaBottomRowVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via falling diagonal victory") public void testFinishedGameWhereXWonViaFallingDiagonalVictory() { assertThat( @@ -131,6 +145,7 @@ public void testFinishedGameWhereXWonViaFallingDiagonalVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via rising diagonal victory") public void testFinishedGameWhereXWonViaRisingDiagonalVictory() { assertThat( @@ -140,6 +155,7 @@ public void testFinishedGameWhereXWonViaRisingDiagonalVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via falling diagonal victory") public void testFinishedGameWhereOWonViaFallingDiagonalVictory() { assertThat( @@ -149,6 +165,7 @@ public void testFinishedGameWhereOWonViaFallingDiagonalVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where O won via rising diagonal victory") public void testFinishedGameWhereOWonViaRisingDiagonalVictory() { assertThat( @@ -158,6 +175,7 @@ public void testFinishedGameWhereOWonViaRisingDiagonalVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via a row and a column victory") public void testFinishedGameWhereXWonViaARowAndAColumnVictory() { assertThat( @@ -167,6 +185,7 @@ public void testFinishedGameWhereXWonViaARowAndAColumnVictory() { @Disabled("Remove to run test") @Test + @DisplayName("Finished game where X won via two diagonal victories") public void testFinishedGameWhereXWonViaTwoDiagonalVictories() { assertThat( @@ -176,6 +195,7 @@ public void testFinishedGameWhereXWonViaTwoDiagonalVictories() { @Disabled("Remove to run test") @Test + @DisplayName("Drawn games") public void testDraw() { assertThat( @@ -185,6 +205,7 @@ public void testDraw() { @Disabled("Remove to run test") @Test + @DisplayName("Another draw") public void testAnotherDraw() { assertThat( @@ -194,6 +215,7 @@ public void testAnotherDraw() { @Disabled("Remove to run test") @Test + @DisplayName("Ongoing game: one move in") public void testOngoingGameOneMoveIn() { assertThat( @@ -203,6 +225,7 @@ public void testOngoingGameOneMoveIn() { @Disabled("Remove to run test") @Test + @DisplayName("Ongoing game: two moves in") public void testOngoingGameTwoMovesIn() { assertThat( @@ -212,6 +235,7 @@ public void testOngoingGameTwoMovesIn() { @Disabled("Remove to run test") @Test + @DisplayName("Ongoing game: five moves in") public void testOngoingGameFiveMovesIn() { assertThat( @@ -221,6 +245,7 @@ public void testOngoingGameFiveMovesIn() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid board: X went twice") public void testInvalidBoardXWentTwice() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -230,6 +255,7 @@ public void testInvalidBoardXWentTwice() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid board: O started") public void testInvalidBoardOStarted() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -239,6 +265,7 @@ public void testInvalidBoardOStarted() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid board") public void testInvalidBoard() { assertThatExceptionOfType(IllegalArgumentException.class) @@ -248,6 +275,7 @@ public void testInvalidBoard() { @Disabled("Remove to run test") @Test + @DisplayName("Invalid board: players kept playing after a win") public void testInvalidBoardPlayersKeptPlayingAfterAWin() { assertThatExceptionOfType(IllegalArgumentException.class) diff --git a/exercises/practice/sublist/.docs/instructions.md b/exercises/practice/sublist/.docs/instructions.md index 7535931af..8228edc6c 100644 --- a/exercises/practice/sublist/.docs/instructions.md +++ b/exercises/practice/sublist/.docs/instructions.md @@ -8,8 +8,8 @@ Given any two lists `A` and `B`, determine if: - None of the above is true, thus lists `A` and `B` are unequal Specifically, list `A` is equal to list `B` if both lists have the same values in the same order. -List `A` is a superlist of `B` if `A` contains a sub-sequence of values equal to `B`. -List `A` is a sublist of `B` if `B` contains a sub-sequence of values equal to `A`. +List `A` is a superlist of `B` if `A` contains a contiguous sub-sequence of values equal to `B`. +List `A` is a sublist of `B` if `B` contains a contiguous sub-sequence of values equal to `A`. Examples: diff --git a/exercises/practice/sublist/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/sublist/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/sublist/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/sublist/gradlew b/exercises/practice/sublist/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/sublist/gradlew +++ b/exercises/practice/sublist/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/sublist/gradlew.bat b/exercises/practice/sublist/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/sublist/gradlew.bat +++ b/exercises/practice/sublist/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/sublist/src/test/java/RelationshipComputerTest.java b/exercises/practice/sublist/src/test/java/RelationshipComputerTest.java index e9e4adc62..bcb2d6f8d 100644 --- a/exercises/practice/sublist/src/test/java/RelationshipComputerTest.java +++ b/exercises/practice/sublist/src/test/java/RelationshipComputerTest.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; @@ -10,6 +11,7 @@ public class RelationshipComputerTest { @Test + @DisplayName("empty lists") public void testThatTwoEmptyListsAreConsideredEqual() { Relationship relationship = new RelationshipComputer<>().computeRelationship( emptyList(), @@ -20,6 +22,7 @@ public void testThatTwoEmptyListsAreConsideredEqual() { @Disabled("Remove to run test") @Test + @DisplayName("empty list within non empty list") public void testEmptyListIsSublistOfNonEmptyList() { Relationship relationship = new RelationshipComputer<>().computeRelationship( emptyList(), @@ -30,6 +33,7 @@ public void testEmptyListIsSublistOfNonEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("non empty list contains empty list") public void testNonEmptyListIsSuperlistOfEmptyList() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList('1', '2', '3'), @@ -40,6 +44,7 @@ public void testNonEmptyListIsSuperlistOfEmptyList() { @Disabled("Remove to run test") @Test + @DisplayName("list equals itself") public void testListIsEqualToItself() { List anyList = asList("1", "2", "3"); @@ -52,6 +57,7 @@ public void testListIsEqualToItself() { @Disabled("Remove to run test") @Test + @DisplayName("different lists") public void testDifferentListsOfTheSameLengthAreUnequal() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList(1, 2, 3), @@ -62,6 +68,7 @@ public void testDifferentListsOfTheSameLengthAreUnequal() { @Disabled("Remove to run test") @Test + @DisplayName("false start") public void testSublistCheckDoesNotAbortAfterFalseStart() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList('1', '2', '5'), @@ -72,6 +79,7 @@ public void testSublistCheckDoesNotAbortAfterFalseStart() { @Disabled("Remove to run test") @Test + @DisplayName("consecutive") public void testSublistCheckHandlesExtraneousRepeatsOfFirstEntry() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList("1", "1", "2"), @@ -82,6 +90,7 @@ public void testSublistCheckHandlesExtraneousRepeatsOfFirstEntry() { @Disabled("Remove to run test") @Test + @DisplayName("sublist at start") public void testSublistAtStart() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList(0, 1, 2), @@ -92,6 +101,7 @@ public void testSublistAtStart() { @Disabled("Remove to run test") @Test + @DisplayName("sublist in middle") public void testSublistInMiddle() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList('2', '3', '4'), @@ -102,6 +112,7 @@ public void testSublistInMiddle() { @Disabled("Remove to run test") @Test + @DisplayName("sublist at end") public void testSublistAtEnd() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList("3", "4", "5"), @@ -112,6 +123,7 @@ public void testSublistAtEnd() { @Disabled("Remove to run test") @Test + @DisplayName("at start of superlist") public void testAtStartOfSuperlist() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList(0, 1, 2, 3, 4, 5), @@ -122,6 +134,7 @@ public void testAtStartOfSuperlist() { @Disabled("Remove to run test") @Test + @DisplayName("in middle of superlist") public void testInMiddleOfSuperlist() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList('0', '1', '2', '3', '4', '5'), @@ -132,6 +145,7 @@ public void testInMiddleOfSuperlist() { @Disabled("Remove to run test") @Test + @DisplayName("at end of superlist") public void testAtEndOfSuperlist() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList("0", "1", "2", "3", "4", "5"), @@ -142,6 +156,7 @@ public void testAtEndOfSuperlist() { @Disabled("Remove to run test") @Test + @DisplayName("first list missing element from second list") public void testFirstListMissingElementFromSecondList() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList(1, 3), @@ -152,6 +167,7 @@ public void testFirstListMissingElementFromSecondList() { @Disabled("Remove to run test") @Test + @DisplayName("second list missing element from first list") public void testSecondListMissingElementFromFirstList() { Relationship relationship = new RelationshipComputer<>().computeRelationship( asList('1', '2', '3'), @@ -162,30 +178,33 @@ public void testSecondListMissingElementFromFirstList() { @Disabled("Remove to run test") @Test - public void testThatListOrderingIsAccountedFor() { + @DisplayName("first list missing additional digits from second list") + public void testFirstListMissingAdditionalDigitsFromSecondList() { Relationship relationship = new RelationshipComputer<>().computeRelationship( - asList("1", "2", "3"), - asList("3", "2", "1")); + asList(1, 2), + asList(1, 22)); assertThat(relationship).isEqualTo(Relationship.UNEQUAL); } @Disabled("Remove to run test") @Test - public void testThatListsWithSameDigitsButDifferentNumbersAreUnequal() { + @DisplayName("order matters to a list") + public void testThatListOrderingIsAccountedFor() { Relationship relationship = new RelationshipComputer<>().computeRelationship( - asList(1, 0, 1), - asList(10, 1)); + asList("1", "2", "3"), + asList("3", "2", "1")); assertThat(relationship).isEqualTo(Relationship.UNEQUAL); } @Disabled("Remove to run test") @Test - public void testFirstListMissingAdditionalDigitsFromSecondList() { + @DisplayName("same digits but different numbers") + public void testThatListsWithSameDigitsButDifferentNumbersAreUnequal() { Relationship relationship = new RelationshipComputer<>().computeRelationship( - asList(1, 2), - asList(1, 22)); + asList(1, 0, 1), + asList(10, 1)); assertThat(relationship).isEqualTo(Relationship.UNEQUAL); } 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/sum-of-multiples/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/sum-of-multiples/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/sum-of-multiples/gradlew b/exercises/practice/sum-of-multiples/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/sum-of-multiples/gradlew +++ b/exercises/practice/sum-of-multiples/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/sum-of-multiples/gradlew.bat b/exercises/practice/sum-of-multiples/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/sum-of-multiples/gradlew.bat +++ b/exercises/practice/sum-of-multiples/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/sum-of-multiples/src/test/java/SumOfMultiplesTest.java b/exercises/practice/sum-of-multiples/src/test/java/SumOfMultiplesTest.java index c7f7ddfbc..b2736d8bd 100644 --- a/exercises/practice/sum-of-multiples/src/test/java/SumOfMultiplesTest.java +++ b/exercises/practice/sum-of-multiples/src/test/java/SumOfMultiplesTest.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 SumOfMultiplesTest { @Test + @DisplayName("no multiples within limit") public void testNoMultiplesWithinLimit() { int[] set = { @@ -19,6 +21,7 @@ public void testNoMultiplesWithinLimit() { @Disabled("Remove to run test") @Test + @DisplayName("one factor has multiples within limit") public void testOneFactorHasMultiplesWithinLimit() { int[] set = { @@ -32,6 +35,7 @@ public void testOneFactorHasMultiplesWithinLimit() { @Disabled("Remove to run test") @Test + @DisplayName("more than one multiple within limit") public void testMoreThanOneMultipleWithinLimit() { int[] set = { @@ -44,6 +48,7 @@ public void testMoreThanOneMultipleWithinLimit() { @Disabled("Remove to run test") @Test + @DisplayName("more than one factor with multiples within limit") public void testMoreThanOneFactorWithMultiplesWithinLimit() { int[] set = { @@ -57,6 +62,7 @@ public void testMoreThanOneFactorWithMultiplesWithinLimit() { @Disabled("Remove to run test") @Test + @DisplayName("each multiple is only counted once") public void testEachMultipleIsOnlyCountedOnce() { int[] set = { @@ -70,6 +76,7 @@ public void testEachMultipleIsOnlyCountedOnce() { @Disabled("Remove to run test") @Test + @DisplayName("a much larger limit") public void testAMuchLargerLimit() { int[] set = { @@ -83,6 +90,7 @@ public void testAMuchLargerLimit() { @Disabled("Remove to run test") @Test + @DisplayName("three factors") public void testThreeFactors() { int[] set = { @@ -97,6 +105,7 @@ public void testThreeFactors() { @Disabled("Remove to run test") @Test + @DisplayName("factors not relatively prime") public void testFactorsNotRelativelyPrime() { int[] set = { @@ -110,6 +119,7 @@ public void testFactorsNotRelativelyPrime() { @Disabled("Remove to run test") @Test + @DisplayName("some pairs of factors relatively prime and some not") public void testSomePairsOfFactorsRelativelyPrimeAndSomeNot() { int[] set = { @@ -124,6 +134,7 @@ public void testSomePairsOfFactorsRelativelyPrimeAndSomeNot() { @Disabled("Remove to run test") @Test + @DisplayName("one factor is a multiple of another") public void testOneFactorIsAMultipleOfAnother() { int[] set = { @@ -137,6 +148,7 @@ public void testOneFactorIsAMultipleOfAnother() { @Disabled("Remove to run test") @Test + @DisplayName("much larger factors") public void testMuchLargerFactors() { int[] set = { @@ -150,6 +162,7 @@ public void testMuchLargerFactors() { @Disabled("Remove to run test") @Test + @DisplayName("all numbers are multiples of 1") public void testAllNumbersAreMultiplesOf1() { int[] set = { @@ -162,6 +175,7 @@ public void testAllNumbersAreMultiplesOf1() { @Disabled("Remove to run test") @Test + @DisplayName("no factors means an empty sum") public void testNoFactorsMeanAnEmptySum() { int[] set = {}; @@ -172,6 +186,7 @@ public void testNoFactorsMeanAnEmptySum() { @Disabled("Remove to run test") @Test + @DisplayName("the only multiple of 0 is 0") public void testSumOfMultiplesOfZeroIsZero() { int[] set = { @@ -184,6 +199,7 @@ public void testSumOfMultiplesOfZeroIsZero() { @Disabled("Remove to run test") @Test + @DisplayName("the factor 0 does not affect the sum of multiples of other factors") public void testFactorZeroDoesNotAffectTheSumOfMultiplesOfOtherFactors() { int[] set = { @@ -197,6 +213,7 @@ public void testFactorZeroDoesNotAffectTheSumOfMultiplesOfOtherFactors() { @Disabled("Remove to run test") @Test + @DisplayName("solutions using include-exclude must extend to cardinality greater than 3") public void testSolutionsUsingIncludeExcludeMustExtendToCardinalityGreater3() { int[] set = { 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..2c2db5875 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "zamora-carlos" + ], + "files": { + "solution": [ + "src/main/java/SwiftScheduling.java" + ], + "test": [ + "src/test/java/SwiftSchedulingTest.java" + ], + "example": [ + ".meta/src/reference/java/SwiftScheduling.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "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 000000000..f8e1ee312 Binary files /dev/null and b/exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..4d97ea344 --- /dev/null +++ b/exercises/practice/swift-scheduling/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/exercises/practice/swift-scheduling/gradlew b/exercises/practice/swift-scheduling/gradlew new file mode 100755 index 000000000..adff685a0 --- /dev/null +++ b/exercises/practice/swift-scheduling/gradlew @@ -0,0 +1,248 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 + + + +# 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" ) + + 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, 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" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# 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..c4bdd3ab8 --- /dev/null +++ b/exercises/practice/swift-scheduling/gradlew.bat @@ -0,0 +1,93 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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 + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +: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..ffde7b7a0 --- /dev/null +++ b/exercises/practice/swift-scheduling/src/test/java/SwiftSchedulingTest.java @@ -0,0 +1,202 @@ +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 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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "NOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "ASAP"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "ASAP"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "ASAP"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "EOW"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "2M"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "11M"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "4M"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "Q1"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "Q4"); + + assertThat(actual).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @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"); + + LocalDateTime actual = SwiftScheduling.convertToDeliveryDate(meetingStart, "Q3"); + + assertThat(actual).isEqualTo(expected); + } +} 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/tournament/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/tournament/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/tournament/gradlew b/exercises/practice/tournament/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/tournament/gradlew +++ b/exercises/practice/tournament/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/tournament/gradlew.bat b/exercises/practice/tournament/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/tournament/gradlew.bat +++ b/exercises/practice/tournament/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/tournament/src/test/java/TournamentTest.java b/exercises/practice/tournament/src/test/java/TournamentTest.java index aa34a8d05..94accd8fb 100644 --- a/exercises/practice/tournament/src/test/java/TournamentTest.java +++ b/exercises/practice/tournament/src/test/java/TournamentTest.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("just the header if no input") public void justTheHeaderIfNoInput() { assertThat(tournament.printTable()) .isEqualTo("Team | MP | W | D | L | P\n"); @@ -21,6 +23,7 @@ public void justTheHeaderIfNoInput() { @Disabled("Remove to run test") @Test + @DisplayName("a win is three points, a loss is zero points") public void aWinIsThreePointsALossIsZeroPoints() { tournament.applyResults("Allegoric Alaskans;Blithering Badgers;win"); assertThat(tournament.printTable()) @@ -32,6 +35,7 @@ public void aWinIsThreePointsALossIsZeroPoints() { @Disabled("Remove to run test") @Test + @DisplayName("a win can also be expressed as a loss") public void aWinCanAlsoBeExpressedAsALoss() { tournament.applyResults("Blithering Badgers;Allegoric Alaskans;loss"); assertThat(tournament.printTable()) @@ -43,6 +47,7 @@ public void aWinCanAlsoBeExpressedAsALoss() { @Disabled("Remove to run test") @Test + @DisplayName("a different team can win") public void aDifferentTeamCanWin() { tournament.applyResults("Blithering Badgers;Allegoric Alaskans;win"); assertThat(tournament.printTable()) @@ -54,6 +59,7 @@ public void aDifferentTeamCanWin() { @Disabled("Remove to run test") @Test + @DisplayName("a draw is one point each") public void aDrawIsOnePointEach() { tournament.applyResults("Allegoric Alaskans;Blithering Badgers;draw"); assertThat(tournament.printTable()) @@ -65,6 +71,7 @@ public void aDrawIsOnePointEach() { @Disabled("Remove to run test") @Test + @DisplayName("There can be more than one match") public void thereCanBeMoreThanOneMatch() { tournament.applyResults( "Allegoric Alaskans;Blithering Badgers;win\n" + @@ -78,6 +85,7 @@ public void thereCanBeMoreThanOneMatch() { @Disabled("Remove to run test") @Test + @DisplayName("There can be more than one winner") public void thereCanBeMoreThanOneWinner() { tournament.applyResults( "Allegoric Alaskans;Blithering Badgers;loss\n" + @@ -91,6 +99,7 @@ public void thereCanBeMoreThanOneWinner() { @Disabled("Remove to run test") @Test + @DisplayName("There can be more than two teams") public void thereCanBeMoreThanTwoTeams() { tournament.applyResults( "Allegoric Alaskans;Blithering Badgers;win\n" + @@ -106,6 +115,7 @@ public void thereCanBeMoreThanTwoTeams() { @Disabled("Remove to run test") @Test + @DisplayName("typical input") public void typicalInput() { tournament.applyResults( "Allegoric Alaskans;Blithering Badgers;win\n" + @@ -125,6 +135,7 @@ public void typicalInput() { @Disabled("Remove to run test") @Test + @DisplayName("incomplete competition (not all pairs have played)") public void incompleteCompetition() { tournament.applyResults( "Allegoric Alaskans;Blithering Badgers;loss\n" + @@ -142,6 +153,7 @@ public void incompleteCompetition() { @Disabled("Remove to run test") @Test + @DisplayName("ties broken alphabetically") public void tiesBrokenAlphabetically() { tournament.applyResults( "Courageous Californians;Devastating Donkeys;win\n" + @@ -161,6 +173,7 @@ public void tiesBrokenAlphabetically() { @Disabled("Remove to run test") @Test + @DisplayName("ensure points sorted numerically") public void pointsSortedNumerically() { tournament.applyResults( "Devastating Donkeys;Blithering Badgers;win\n" + 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/transpose/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/transpose/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/transpose/gradlew b/exercises/practice/transpose/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/transpose/gradlew +++ b/exercises/practice/transpose/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/transpose/gradlew.bat b/exercises/practice/transpose/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/transpose/gradlew.bat +++ b/exercises/practice/transpose/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/transpose/src/test/java/TransposeTest.java b/exercises/practice/transpose/src/test/java/TransposeTest.java index ad8f95f29..8a4b18686 100644 --- a/exercises/practice/transpose/src/test/java/TransposeTest.java +++ b/exercises/practice/transpose/src/test/java/TransposeTest.java @@ -1,6 +1,7 @@ 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; import org.junit.jupiter.api.BeforeEach; @@ -13,12 +14,14 @@ public void setup() { } @Test + @DisplayName("empty string") public void emptyString() { assertThat(transpose.transpose("")).isEqualTo(""); } @Disabled("Remove to run test") @Test + @DisplayName("two characters in a row") public void twoCharactersInARow() { assertThat(transpose.transpose("A1")) .isEqualTo( @@ -28,6 +31,7 @@ public void twoCharactersInARow() { @Disabled("Remove to run test") @Test + @DisplayName("two characters in a column") public void twoCharactersInAColumn() { assertThat( transpose.transpose( @@ -38,6 +42,7 @@ public void twoCharactersInAColumn() { @Disabled("Remove to run test") @Test + @DisplayName("simple") public void simple() { assertThat( transpose.transpose( @@ -51,6 +56,7 @@ public void simple() { @Disabled("Remove to run test") @Test + @DisplayName("single line") public void singleLine() { assertThat(transpose.transpose("Single line.")) .isEqualTo( @@ -70,6 +76,7 @@ public void singleLine() { @Disabled("Remove to run test") @Test + @DisplayName("first line longer than second line") public void firstLineLongerThanSecondLine() { assertThat( transpose.transpose( @@ -96,6 +103,7 @@ public void firstLineLongerThanSecondLine() { @Disabled("Remove to run test") @Test + @DisplayName("second line longer than first line") public void secondLineLongerThanFirstLine() { assertThat( transpose.transpose( @@ -122,6 +130,7 @@ public void secondLineLongerThanFirstLine() { @Disabled("Remove to run test") @Test + @DisplayName("mixed line length") public void mixedLineLength() { assertThat( transpose.transpose( @@ -151,6 +160,7 @@ public void mixedLineLength() { @Disabled("Remove to run test") @Test + @DisplayName("square") public void square() { assertThat( transpose.transpose( @@ -169,6 +179,7 @@ public void square() { @Disabled("Remove to run test") @Test + @DisplayName("rectangle") public void rectangle() { assertThat( transpose.transpose( @@ -189,6 +200,7 @@ public void rectangle() { @Disabled("Remove to run test") @Test + @DisplayName("triangle") public void triangle() { assertThat( transpose.transpose( @@ -209,6 +221,7 @@ public void triangle() { @Disabled("Remove to run test") @Test + @DisplayName("jagged triangle") public void jaggedTriangle() { assertThat( transpose.transpose( 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/tree-building/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/tree-building/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/tree-building/gradlew b/exercises/practice/tree-building/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/tree-building/gradlew +++ b/exercises/practice/tree-building/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/tree-building/gradlew.bat b/exercises/practice/tree-building/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/tree-building/gradlew.bat +++ b/exercises/practice/tree-building/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/tree-building/src/test/java/BuildTreeTest.java b/exercises/practice/tree-building/src/test/java/BuildTreeTest.java index c34bb3cc9..e11bc2990 100644 --- a/exercises/practice/tree-building/src/test/java/BuildTreeTest.java +++ b/exercises/practice/tree-building/src/test/java/BuildTreeTest.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.ArrayList; @@ -9,6 +10,7 @@ public class BuildTreeTest { @Test + @DisplayName("Empty list") public void testEmptyList() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); @@ -18,6 +20,7 @@ public void testEmptyList() throws InvalidRecordsException { @Disabled("Remove to run test") @Test + @DisplayName("Single record") public void testOneRecord() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); Record record = new Record(0, 0); @@ -30,6 +33,7 @@ public void testOneRecord() throws InvalidRecordsException { @Disabled("Remove to run test") @Test + @DisplayName("Three records in order") public void testThreeRecordsInOrder() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); records.add(new Record(0, 0)); @@ -48,6 +52,7 @@ public void testThreeRecordsInOrder() throws InvalidRecordsException { @Disabled("Remove to run test") @Test + @DisplayName("Three records in reverse order") public void testThreeRecordsInReverseOrder() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); records.add(new Record(2, 0)); @@ -66,6 +71,7 @@ public void testThreeRecordsInReverseOrder() throws InvalidRecordsException { @Disabled("Remove to run test") @Test + @DisplayName("More than two children") public void testRecordsWithMoreThanTwoChildren() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); records.add(new Record(0, 0)); @@ -83,11 +89,11 @@ public void testRecordsWithMoreThanTwoChildren() throws InvalidRecordsException assertThat(root.getChildren().get(0).getNodeId()).isEqualTo(1); assertThat(root.getChildren().get(1).getNodeId()).isEqualTo(2); assertThat(root.getChildren().get(2).getNodeId()).isEqualTo(3); - } @Disabled("Remove to run test") @Test + @DisplayName("Binary tree") public void testBinaryTree() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); records.add(new Record(6, 2)); @@ -119,6 +125,7 @@ public void testBinaryTree() throws InvalidRecordsException { @Disabled("Remove to run test") @Test + @DisplayName("Unbalanced tree") public void testUnbalancedTree() throws InvalidRecordsException { ArrayList records = new ArrayList<>(); records.add(new Record(0, 0)); @@ -149,6 +156,7 @@ public void testUnbalancedTree() throws InvalidRecordsException { @Disabled("Remove to run test") @Test + @DisplayName("Root has parent") public void testRootNodeHasParent() { ArrayList records = new ArrayList<>(); records.add(new Record(0, 1)); @@ -163,6 +171,7 @@ public void testRootNodeHasParent() { @Disabled("Remove to run test") @Test + @DisplayName("No root node") public void testNoRootNode() { ArrayList records = new ArrayList<>(); records.add(new Record(1, 0)); @@ -177,6 +186,7 @@ public void testNoRootNode() { @Disabled("Remove to run test") @Test + @DisplayName("Non continuous records") public void testNonContinuousRecords() { ArrayList records = new ArrayList<>(); records.add(new Record(2, 0)); @@ -193,6 +203,7 @@ public void testNonContinuousRecords() { @Disabled("Remove to run test") @Test + @DisplayName("Cycle indirectly") public void testCycleIndirectly() { ArrayList records = new ArrayList<>(); records.add(new Record(5, 2)); diff --git a/exercises/practice/triangle/.docs/instructions.md b/exercises/practice/triangle/.docs/instructions.md index ac3900872..e9b053dcd 100644 --- a/exercises/practice/triangle/.docs/instructions.md +++ b/exercises/practice/triangle/.docs/instructions.md @@ -13,6 +13,12 @@ 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 +_Degenerate triangles_ are triangles where the sum of the length of two sides is **equal** to the length of the third side, e.g. `1, 1, 2`. +We opted to not include tests for degenerate triangles in this exercise. +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. 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/triangle/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/triangle/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/triangle/gradlew b/exercises/practice/triangle/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/triangle/gradlew +++ b/exercises/practice/triangle/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/triangle/gradlew.bat b/exercises/practice/triangle/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/triangle/gradlew.bat +++ b/exercises/practice/triangle/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/triangle/src/test/java/TriangleTest.java b/exercises/practice/triangle/src/test/java/TriangleTest.java index bd6ad663a..e7dc8f630 100644 --- a/exercises/practice/triangle/src/test/java/TriangleTest.java +++ b/exercises/practice/triangle/src/test/java/TriangleTest.java @@ -3,10 +3,12 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; public class TriangleTest { @Test + @DisplayName("equilateral triangle") public void equilateralTrianglesHaveEqualSides() throws TriangleException { Triangle triangle = new Triangle(2, 2, 2); @@ -15,6 +17,7 @@ public void equilateralTrianglesHaveEqualSides() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("any side is unequal") public void trianglesWithOneUnequalSideAreNotEquilateral() throws TriangleException { Triangle triangle = new Triangle(2, 3, 2); @@ -23,6 +26,7 @@ public void trianglesWithOneUnequalSideAreNotEquilateral() throws TriangleExcept @Disabled("Remove to run test") @Test + @DisplayName("no sides are equal") public void trianglesWithNoEqualSidesAreNotEquilateral() throws TriangleException { Triangle triangle = new Triangle(5, 4, 6); @@ -31,12 +35,14 @@ public void trianglesWithNoEqualSidesAreNotEquilateral() throws TriangleExceptio @Disabled("Remove to run test") @Test + @DisplayName("all zero sides is not a triangle") public void trianglesWithNoSizeAreIllegal() { assertThatExceptionOfType(TriangleException.class).isThrownBy(() -> new Triangle(0, 0, 0)); } @Disabled("Remove to run test") @Test + @DisplayName("sides may be floats") public void verySmallTrianglesCanBeEquilateral() throws TriangleException { Triangle triangle = new Triangle(0.5, 0.5, 0.5); @@ -45,6 +51,7 @@ public void verySmallTrianglesCanBeEquilateral() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("last two sides are equal") public void isoscelesTrianglesHaveLastTwoSidesEqual() throws TriangleException { Triangle triangle = new Triangle(3, 4, 4); @@ -53,6 +60,7 @@ public void isoscelesTrianglesHaveLastTwoSidesEqual() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("first two sides are equal") public void isoscelesTrianglesHaveTwoFirstSidesEqual() throws TriangleException { Triangle triangle = new Triangle(4, 4, 3); @@ -61,6 +69,7 @@ public void isoscelesTrianglesHaveTwoFirstSidesEqual() throws TriangleException @Disabled("Remove to run test") @Test + @DisplayName("first and last sides are equal") public void isoscelesTrianglesHaveFirstAndLastSidesEqual() throws TriangleException { Triangle triangle = new Triangle(4, 3, 4); @@ -69,6 +78,7 @@ public void isoscelesTrianglesHaveFirstAndLastSidesEqual() throws TriangleExcept @Disabled("Remove to run test") @Test + @DisplayName("equilateral triangles are also isosceles") public void equilateralTrianglesAreAlsoIsosceles() throws TriangleException { Triangle triangle = new Triangle(4, 4, 4); @@ -77,6 +87,7 @@ public void equilateralTrianglesAreAlsoIsosceles() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("no sides are equal") public void noSidesAreEqualCantBeIsoceles() throws TriangleException { Triangle triangle = new Triangle(2, 3, 4); @@ -85,24 +96,28 @@ public void noSidesAreEqualCantBeIsoceles() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("first triangle inequality violation") public void firstTriangleInequalityViolation() { assertThatExceptionOfType(TriangleException.class).isThrownBy(() -> new Triangle(1, 1, 3)); } @Disabled("Remove to run test") @Test + @DisplayName("second triangle inequality violation") public void secondTriangleInequalityViolation() { assertThatExceptionOfType(TriangleException.class).isThrownBy(() -> new Triangle(1, 3, 1)); } @Disabled("Remove to run test") @Test + @DisplayName("third triangle inequality violation") public void thirdTriangleInequalityViolation() { assertThatExceptionOfType(TriangleException.class).isThrownBy(() -> new Triangle(3, 1, 1)); } @Disabled("Remove to run test") @Test + @DisplayName("sides may be floats") public void verySmallTrianglesCanBeIsosceles() throws TriangleException { Triangle triangle = new Triangle(0.5, 0.4, 0.5); @@ -111,6 +126,7 @@ public void verySmallTrianglesCanBeIsosceles() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("no sides are equal") public void scaleneTrianglesHaveNoEqualSides() throws TriangleException { Triangle triangle = new Triangle(5, 4, 6); @@ -119,6 +135,7 @@ public void scaleneTrianglesHaveNoEqualSides() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("all sides are equal") public void allSidesEqualAreNotScalene() throws TriangleException { Triangle triangle = new Triangle(4, 4, 4); @@ -127,6 +144,7 @@ public void allSidesEqualAreNotScalene() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("first and second sides are equal") public void twoSidesEqualAreNotScalene() throws TriangleException { Triangle triangle = new Triangle(4, 4, 3); @@ -135,6 +153,7 @@ public void twoSidesEqualAreNotScalene() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("first and third sides are equal") public void firstAndThirdSidesAreEqualAreNotScalene() throws TriangleException { Triangle triangle = new Triangle(3, 4, 3); @@ -143,6 +162,7 @@ public void firstAndThirdSidesAreEqualAreNotScalene() throws TriangleException { @Disabled("Remove to run test") @Test + @DisplayName("second and third sides are equal") public void secondAndThirdSidesAreEqualAreNotScalene() throws TriangleException { Triangle triangle = new Triangle(4, 3, 3); @@ -151,12 +171,14 @@ public void secondAndThirdSidesAreEqualAreNotScalene() throws TriangleException @Disabled("Remove to run test") @Test + @DisplayName("may not violate triangle inequality") public void mayNotViolateTriangleInequality() { assertThatExceptionOfType(TriangleException.class).isThrownBy(() -> new Triangle(7, 3, 2)); } @Disabled("Remove to run test") @Test + @DisplayName("sides may be floats") public void verySmallTrianglesCanBeScalene() throws TriangleException { Triangle triangle = new Triangle(0.5, 0.4, 0.6); 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/twelve-days/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/twelve-days/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/twelve-days/gradlew b/exercises/practice/twelve-days/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/twelve-days/gradlew +++ b/exercises/practice/twelve-days/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/twelve-days/gradlew.bat b/exercises/practice/twelve-days/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/twelve-days/gradlew.bat +++ b/exercises/practice/twelve-days/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/twelve-days/src/test/java/TwelveDaysTest.java b/exercises/practice/twelve-days/src/test/java/TwelveDaysTest.java index d046235eb..a6827ee51 100644 --- a/exercises/practice/twelve-days/src/test/java/TwelveDaysTest.java +++ b/exercises/practice/twelve-days/src/test/java/TwelveDaysTest.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("first day a partridge in a pear tree") public void testVerseOne() { String expectedVerseOne = "On the first day of Christmas my true love gave to me: " + "a Partridge in a Pear Tree.\n"; @@ -21,6 +23,7 @@ public void testVerseOne() { @Disabled("Remove to run test") @Test + @DisplayName("second day two turtle doves") public void testVerseTwo() { String expectedVerseTwo = "On the second day of Christmas my true love gave to me: two Turtle Doves, " + "and a Partridge in a Pear Tree.\n"; @@ -29,6 +32,7 @@ public void testVerseTwo() { @Disabled("Remove to run test") @Test + @DisplayName("third day three french hens") public void testVerseThree() { String expectedVerseThree = "On the third day of Christmas my true love gave to me: three French Hens, " + "two Turtle Doves, and a Partridge in a Pear Tree.\n"; @@ -37,6 +41,7 @@ public void testVerseThree() { @Disabled("Remove to run test") @Test + @DisplayName("fourth day four calling birds") public void testVerseFour() { String expectedVerseFour = "On the fourth day of Christmas my true love gave to me: four Calling Birds, " + "three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n"; @@ -45,6 +50,7 @@ public void testVerseFour() { @Disabled("Remove to run test") @Test + @DisplayName("fifth day five gold rings") public void testVerseFive() { String expectedVerseFive = "On the fifth day of Christmas my true love gave to me: five Gold Rings, " + "four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n"; @@ -53,6 +59,7 @@ public void testVerseFive() { @Disabled("Remove to run test") @Test + @DisplayName("sixth day six geese-a-laying") public void testVerseSix() { String expectedVerseSix = "On the sixth day of Christmas my true love gave to me: six Geese-a-Laying, " + "five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, " + @@ -62,6 +69,7 @@ public void testVerseSix() { @Disabled("Remove to run test") @Test + @DisplayName("seventh day seven swans-a-swimming") public void testVerseSeven() { String expectedVerseSeven = "On the seventh day of Christmas my true love gave to me: " + "seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, " + @@ -71,6 +79,7 @@ public void testVerseSeven() { @Disabled("Remove to run test") @Test + @DisplayName("eighth day eight maids-a-milking") public void testVerseEight() { String expectedVerseEight = "On the eighth day of Christmas my true love gave to me: eight Maids-a-Milking," + " seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, " + @@ -80,6 +89,7 @@ public void testVerseEight() { @Disabled("Remove to run test") @Test + @DisplayName("ninth day nine ladies dancing") public void testVerseNine() { String expectedVerseNine = "On the ninth day of Christmas my true love gave to me: nine Ladies Dancing, " + "eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, " + @@ -89,6 +99,7 @@ public void testVerseNine() { @Disabled("Remove to run test") @Test + @DisplayName("tenth day ten lords-a-leaping") public void testVerseTen() { String expectedVerseTen = "On the tenth day of Christmas my true love gave to me: ten Lords-a-Leaping, " + "nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, " + @@ -99,6 +110,7 @@ public void testVerseTen() { @Disabled("Remove to run test") @Test + @DisplayName("eleventh day eleven pipers piping") public void testVerseEleven() { String expectedVerseEleven = "On the eleventh day of Christmas my true love gave to me: " + "eleven Pipers Piping, ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, " + @@ -109,6 +121,7 @@ public void testVerseEleven() { @Disabled("Remove to run test") @Test + @DisplayName("twelfth day twelve drummers drumming") public void testVerseTwelve() { String expectedVerseTwelve = "On the twelfth day of Christmas my true love gave to me: " + "twelve Drummers Drumming, eleven Pipers Piping, ten Lords-a-Leaping, nine Ladies Dancing, " + @@ -119,6 +132,7 @@ public void testVerseTwelve() { @Disabled("Remove to run test") @Test + @DisplayName("recites first three verses of the song") public void testFirstThreeVerses() { String expectedVersesOneToThree = "On the first day of Christmas my true love gave to me: " + "a Partridge in a Pear Tree.\n\n" + @@ -131,6 +145,7 @@ public void testFirstThreeVerses() { @Disabled("Remove to run test") @Test + @DisplayName("recites three verses from the middle of the song") public void testFourthToSixthVerses() { String expectedVersesFourToSix = "On the fourth day of Christmas my true love gave to me: " + "four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n\n" + @@ -143,6 +158,7 @@ public void testFourthToSixthVerses() { @Disabled("Remove to run test") @Test + @DisplayName("recites the whole song") public void testSingWholeSong() { String expectedSong = "On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree.\n" + "\n" + diff --git a/exercises/practice/two-bucket/.meta/config.json b/exercises/practice/two-bucket/.meta/config.json index 54da6bd62..57c7007b7 100644 --- a/exercises/practice/two-bucket/.meta/config.json +++ b/exercises/practice/two-bucket/.meta/config.json @@ -4,24 +4,32 @@ ], "contributors": [ "FridaTveit", + "jagdish-15", "jmrunkle", + "kahgoh", "lemoncurry", + "LoadingBG", "mirkoperillo", "msomji", "muzimuzhi", "sjwarner-bp", "SleeplessByte", - "sshine" + "sshine", + "Xinri" ], "files": { "solution": [ - "src/main/java/TwoBucket.java" + "src/main/java/TwoBucket.java", + "src/main/java/Result.java", + "src/main/java/UnreachableGoalException.java" ], "test": [ "src/test/java/TwoBucketTest.java" ], "example": [ - ".meta/src/reference/java/TwoBucket.java" + ".meta/src/reference/java/TwoBucket.java", + ".meta/src/reference/java/Result.java", + ".meta/src/reference/java/UnreachableGoalException.java" ], "invalidator": [ "build.gradle" diff --git a/exercises/practice/two-bucket/.meta/src/reference/java/Result.java b/exercises/practice/two-bucket/.meta/src/reference/java/Result.java new file mode 100644 index 000000000..b7b23ce64 --- /dev/null +++ b/exercises/practice/two-bucket/.meta/src/reference/java/Result.java @@ -0,0 +1,24 @@ +final class Result { + + private final int totalMoves; + private final String finalBucket; + private final int otherBucket; + + Result(int totalMoves, String finalBuckets, int otherBucket) { + this.totalMoves = totalMoves; + this.finalBucket = finalBuckets; + this.otherBucket = otherBucket; + } + + public int getTotalMoves() { + return totalMoves; + } + + public String getFinalBucket() { + return finalBucket; + } + + public int getOtherBucket() { + return otherBucket; + } +} diff --git a/exercises/practice/two-bucket/.meta/src/reference/java/TwoBucket.java b/exercises/practice/two-bucket/.meta/src/reference/java/TwoBucket.java index 89771ab81..3cf41ccf7 100644 --- a/exercises/practice/two-bucket/.meta/src/reference/java/TwoBucket.java +++ b/exercises/practice/two-bucket/.meta/src/reference/java/TwoBucket.java @@ -1,149 +1,124 @@ import java.util.ArrayList; -import java.util.Objects; +import java.util.List; +import java.util.Map; -class TwoBucket { +final class TwoBucket { + + private int totalMoves = Integer.MAX_VALUE; + private String finalBucket = ""; + private int otherBucket = Integer.MAX_VALUE; - private class State { - int moves; - int bucketOne; - int bucketTwo; - - State (int moves, int bucketOne, int bucketTwo) { - this.moves = moves; - this.bucketOne = bucketOne; - this.bucketTwo = bucketTwo; - } - - @Override - public boolean equals(Object o) { - State otherState = (State) o; - return this.moves == otherState.moves && - this.bucketOne == otherState.bucketOne && - this.bucketTwo == otherState.bucketTwo; - } - - @Override - public int hashCode() { - return Objects.hash(moves, bucketOne, bucketTwo); - } - } - - private State finalState; - - private int bucketOneCap; - private int bucketTwoCap; - private int desiredLiters; - private String startBucket; + private final List> statesReached = new ArrayList<>(); TwoBucket(int bucketOneCap, int bucketTwoCap, int desiredLiters, String startBucket) { - this.bucketOneCap = bucketOneCap; - this.bucketTwoCap = bucketTwoCap; - this.desiredLiters = desiredLiters; - this.startBucket = startBucket; - - finalState = computeFinalState(); + checkIfImpossible(desiredLiters, bucketOneCap, bucketTwoCap); + + fillBuckets( + startBucket.equals("one") ? bucketOneCap : 0, + bucketOneCap, + startBucket.equals("two") ? bucketTwoCap : 0, + bucketTwoCap, + desiredLiters, + 1, + startBucket + ); } - private ArrayList getAdjacentStates (State state) { - ArrayList adjacentStates = new ArrayList(); - - //Empty bucket one - adjacentStates.add(new State(state.moves + 1, 0, state.bucketTwo)); - - //Empty bucket two - adjacentStates.add(new State(state.moves + 1, state.bucketOne, 0)); - - //Fill bucket one - adjacentStates.add(new State(state.moves + 1, bucketOneCap, state.bucketTwo)); - - //Fill bucket two - adjacentStates.add(new State(state.moves + 1, state.bucketOne, bucketTwoCap)); - - //pour from bucket one to bucket two - if (state.bucketOne + state.bucketTwo > bucketTwoCap) { - adjacentStates.add(new State(state.moves + 1, - state.bucketOne - (bucketTwoCap - state.bucketTwo), - bucketTwoCap)); - } else { - adjacentStates.add(new State(state.moves + 1, 0, state.bucketOne + state.bucketTwo)); + private void fillBuckets( + int bucketOne, + int bucketOneCap, + int bucketTwo, + int bucketTwoCap, + int desiredLiters, + int actionsTaken, + String startingBucket + ) { + if (startingBucket.equals("one") && bucketOne == 0 && bucketTwo == bucketTwoCap + || startingBucket.equals("two") && bucketTwo == 0 && bucketOne == bucketOneCap + || statesReached.contains(Map.entry(bucketOne, bucketTwo)) + || bucketOne > bucketOneCap + || bucketTwo > bucketTwoCap) { + return; } - - //pour from bucket two to bucket one - if (state.bucketTwo + state.bucketOne > bucketOneCap) { - adjacentStates.add(new State(state.moves + 1, - bucketOneCap, - state.bucketTwo - (bucketOneCap - state.bucketOne))); - } else { - adjacentStates.add(new State(state.moves + 1, state.bucketTwo + state.bucketOne, 0)); + + if (bucketOne == desiredLiters) { + if (actionsTaken < totalMoves) { + this.totalMoves = actionsTaken; + this.finalBucket = "one"; + this.otherBucket = bucketTwo; + } + return; } - - return adjacentStates; - } - - private boolean isValid(State state) { - if (state.bucketOne == bucketOneCap && state.bucketTwo == 0 && startBucket.equals("two")) { - return false; - } else if (state.bucketOne == 0 && state.bucketTwo == bucketTwoCap && startBucket.equals("two")) { - return false; - } else { - return true; + if (bucketTwo == desiredLiters) { + if (actionsTaken < totalMoves) { + this.totalMoves = actionsTaken; + this.finalBucket = "two"; + this.otherBucket = bucketOne; + } + return; } - } - private State computeFinalState() { - ArrayList paths = new ArrayList(); + statesReached.add(Map.entry(bucketOne, bucketTwo)); - State initialState; - if (startBucket.equals("one")) { - initialState = new State(1, bucketOneCap, 0); - } else { - initialState = new State(1, 0, bucketTwoCap); + if (bucketOne != 0) { + fillBuckets(0, bucketOneCap, bucketTwo, bucketTwoCap, desiredLiters, actionsTaken + 1, startingBucket); } - - if (initialState.bucketOne == desiredLiters || initialState.bucketTwo == desiredLiters) { - return initialState; + if (bucketTwo != 0) { + fillBuckets(bucketOne, bucketOneCap, 0, bucketTwoCap, desiredLiters, actionsTaken + 1, startingBucket); } - paths.add(initialState); - - for (int i = 0; i < 10000; i++) { - State currentState = paths.remove(0); - ArrayList adjacentStates = getAdjacentStates(currentState); - for (State state : adjacentStates) { - if (state.bucketOne == desiredLiters || state.bucketTwo == desiredLiters) { - return state; - } - - if (!paths.contains(state) && isValid(state)) { - paths.add(state); - } - } + fillBuckets( + bucketOneCap, bucketOneCap, bucketTwo, bucketTwoCap, desiredLiters, + actionsTaken + 1, startingBucket + ); + fillBuckets( + bucketOne, bucketOneCap, bucketTwoCap, bucketTwoCap, desiredLiters, + actionsTaken + 1, startingBucket + ); + + if (bucketOne + bucketTwo <= bucketTwoCap) { + fillBuckets( + 0, bucketOneCap, bucketTwo + bucketOne, bucketTwoCap, desiredLiters, + actionsTaken + 1, startingBucket + ); + } else { + fillBuckets( + bucketOne + bucketTwo - bucketTwoCap, bucketOneCap, bucketTwoCap, bucketTwoCap, + desiredLiters, actionsTaken + 1, startingBucket + ); + } + if (bucketOne + bucketTwo <= bucketOneCap) { + fillBuckets( + bucketOne + bucketTwo, bucketOneCap, 0, bucketTwoCap, desiredLiters, + actionsTaken + 1, startingBucket + ); + } else { + fillBuckets( + bucketOneCap, bucketOneCap, bucketTwo + bucketOne - bucketOneCap, bucketTwoCap, + desiredLiters, actionsTaken + 1, startingBucket + ); } - - return null; } - int getTotalMoves() { - return finalState.moves; + private void checkIfImpossible(int desiredLiters, int bucketOneCap, int bucketTwoCap) { + boolean exceedsCapacity = desiredLiters > bucketOneCap && desiredLiters > bucketTwoCap; + boolean invalidDivision = desiredLiters % gcd(bucketOneCap, bucketTwoCap) != 0; + + if (exceedsCapacity || invalidDivision) { + throw new UnreachableGoalException(); + } } - String getFinalBucket() { - if (finalState.bucketOne == desiredLiters) { - return "one"; - } else if (finalState.bucketTwo == desiredLiters) { - return "two"; - } else { - return "No solution found in " + finalState.moves + " iterations!"; + private int gcd(int a, int b) { + while (b != 0) { + int temp = b; + b = a % b; + a = temp; } + return a; } - int getOtherBucket() { - if (getFinalBucket().equals("one")) { - return finalState.bucketTwo; - } else if (getFinalBucket().equals("two")) { - return finalState.bucketOne; - } else { - return -1; - } + Result getResult() { + return new Result(totalMoves, finalBucket, otherBucket); } } diff --git a/exercises/practice/two-bucket/.meta/src/reference/java/UnreachableGoalException.java b/exercises/practice/two-bucket/.meta/src/reference/java/UnreachableGoalException.java new file mode 100644 index 000000000..2b3ccbc49 --- /dev/null +++ b/exercises/practice/two-bucket/.meta/src/reference/java/UnreachableGoalException.java @@ -0,0 +1,14 @@ +public class UnreachableGoalException extends RuntimeException { + + public UnreachableGoalException() { + super(); + } + + public UnreachableGoalException(String message) { + super(message); + } + + public UnreachableGoalException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/exercises/practice/two-bucket/.meta/tests.toml b/exercises/practice/two-bucket/.meta/tests.toml index 27adad905..a3fe533ec 100644 --- a/exercises/practice/two-bucket/.meta/tests.toml +++ b/exercises/practice/two-bucket/.meta/tests.toml @@ -1,6 +1,13 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [a6f2b4ba-065f-4dca-b6f0-e3eee51cb661] description = "Measure using bucket one of size 3 and bucket two of size 5 - start with bucket one" @@ -19,3 +26,18 @@ description = "Measure one step using bucket one of size 1 and bucket two of siz [eb329c63-5540-4735-b30b-97f7f4df0f84] description = "Measure using bucket one of size 2 and bucket two of size 3 - start with bucket one and end with bucket two" + +[58d70152-bf2b-46bb-ad54-be58ebe94c03] +description = "Measure using bucket one much bigger than bucket two" + +[9dbe6499-caa5-4a58-b5ce-c988d71b8981] +description = "Measure using bucket one much smaller than bucket two" + +[449be72d-b10a-4f4b-a959-ca741e333b72] +description = "Not possible to reach the goal" + +[aac38b7a-77f4-4d62-9b91-8846d533b054] +description = "With the same buckets but a different goal, then it is possible" + +[74633132-0ccf-49de-8450-af4ab2e3b299] +description = "Goal larger than both buckets is impossible" 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-bucket/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/two-bucket/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/two-bucket/gradlew b/exercises/practice/two-bucket/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/two-bucket/gradlew +++ b/exercises/practice/two-bucket/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/two-bucket/gradlew.bat b/exercises/practice/two-bucket/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/two-bucket/gradlew.bat +++ b/exercises/practice/two-bucket/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/two-bucket/src/main/java/Result.java b/exercises/practice/two-bucket/src/main/java/Result.java new file mode 100644 index 000000000..2464dab5f --- /dev/null +++ b/exercises/practice/two-bucket/src/main/java/Result.java @@ -0,0 +1,24 @@ +final class Result { + + private final int totalMoves; + private final String finalBucket; + private final int otherBucket; + + Result(int totalMoves, String finalBuckets, int otherBucket) { + this.totalMoves = totalMoves; + this.finalBucket = finalBuckets; + this.otherBucket = otherBucket; + } + + public int getTotalMoves() { + return totalMoves; + } + + public String getFinalBucket() { + return finalBucket; + } + + public int getOtherBucket() { + return otherBucket; + } +} diff --git a/exercises/practice/two-bucket/src/main/java/TwoBucket.java b/exercises/practice/two-bucket/src/main/java/TwoBucket.java index ae7be705a..1bc771a94 100644 --- a/exercises/practice/two-bucket/src/main/java/TwoBucket.java +++ b/exercises/practice/two-bucket/src/main/java/TwoBucket.java @@ -1,17 +1,11 @@ class TwoBucket { - TwoBucket(int bucketOneCap, int bucketTwoCap, int desiredLiters, String startBucket) { - throw new UnsupportedOperationException("Please implement the TwoBucket(int, int, int, String) constructor."); - } - int getTotalMoves() { - throw new UnsupportedOperationException("Please implement the TwoBucket.getTotalMoves() method."); - } - - String getFinalBucket() { - throw new UnsupportedOperationException("Please implement the TwoBucket.getFinalBucket() method."); + TwoBucket(int bucketOneCap, int bucketTwoCap, int desiredLiters, String startBucket) { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } - int getOtherBucket() { - throw new UnsupportedOperationException("Please implement the TwoBucket.getOtherBucket() method."); + Result getResult() { + throw new UnsupportedOperationException("Delete this statement and write your own implementation."); } + } diff --git a/exercises/practice/two-bucket/src/main/java/UnreachableGoalException.java b/exercises/practice/two-bucket/src/main/java/UnreachableGoalException.java new file mode 100644 index 000000000..2b3ccbc49 --- /dev/null +++ b/exercises/practice/two-bucket/src/main/java/UnreachableGoalException.java @@ -0,0 +1,14 @@ +public class UnreachableGoalException extends RuntimeException { + + public UnreachableGoalException() { + super(); + } + + public UnreachableGoalException(String message) { + super(message); + } + + public UnreachableGoalException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/exercises/practice/two-bucket/src/test/java/TwoBucketTest.java b/exercises/practice/two-bucket/src/test/java/TwoBucketTest.java index cf05b67d1..80d39f0ac 100644 --- a/exercises/practice/two-bucket/src/test/java/TwoBucketTest.java +++ b/exercises/practice/two-bucket/src/test/java/TwoBucketTest.java @@ -1,78 +1,148 @@ 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 TwoBucketTest { @Test + @DisplayName("Measure using bucket one of size 3 and bucket two of size 5 - start with bucket one") public void testBucketOneSizeThreeBucketTwoSizeFiveStartWithOne() { - TwoBucket twoBucket = new TwoBucket(3, 5, 1, "one"); + Result bucketResult = new TwoBucket(3, 5, 1, "one").getResult(); - assertThat(twoBucket.getTotalMoves()).isEqualTo(4); - assertThat(twoBucket.getFinalBucket()).isEqualTo("one"); - assertThat(twoBucket.getOtherBucket()).isEqualTo(5); + assertThat(bucketResult.getTotalMoves()).isEqualTo(4); + assertThat(bucketResult.getFinalBucket()).isEqualTo("one"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(5); } @Disabled("Remove to run test") @Test + @DisplayName("Measure using bucket one of size 3 and bucket two of size 5 - start with bucket two") public void testBucketOneSizeThreeBucketTwoSizeFiveStartWithTwo() { - TwoBucket twoBucket = new TwoBucket(3, 5, 1, "two"); + Result bucketResult = new TwoBucket(3, 5, 1, "two").getResult(); - assertThat(twoBucket.getTotalMoves()).isEqualTo(8); - assertThat(twoBucket.getFinalBucket()).isEqualTo("two"); - assertThat(twoBucket.getOtherBucket()).isEqualTo(3); + assertThat(bucketResult.getTotalMoves()).isEqualTo(8); + assertThat(bucketResult.getFinalBucket()).isEqualTo("two"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("Measure using bucket one of size 7 and bucket two of size 11 - start with bucket one") public void testBucketOneSizeSevenBucketTwoSizeElevenStartWithOne() { - TwoBucket twoBucket = new TwoBucket(7, 11, 2, "one"); + Result bucketResult = new TwoBucket(7, 11, 2, "one").getResult(); - assertThat(twoBucket.getTotalMoves()).isEqualTo(14); - assertThat(twoBucket.getFinalBucket()).isEqualTo("one"); - assertThat(twoBucket.getOtherBucket()).isEqualTo(11); + assertThat(bucketResult.getTotalMoves()).isEqualTo(14); + assertThat(bucketResult.getFinalBucket()).isEqualTo("one"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(11); } @Disabled("Remove to run test") @Test + @DisplayName("Measure using bucket one of size 7 and bucket two of size 11 - start with bucket two") public void testBucketOneSizeSevenBucketTwoSizeElevenStartWithTwo() { - TwoBucket twoBucket = new TwoBucket(7, 11, 2, "two"); + Result bucketResult = new TwoBucket(7, 11, 2, "two").getResult(); - assertThat(twoBucket.getTotalMoves()).isEqualTo(18); - assertThat(twoBucket.getFinalBucket()).isEqualTo("two"); - assertThat(twoBucket.getOtherBucket()).isEqualTo(7); + assertThat(bucketResult.getTotalMoves()).isEqualTo(18); + assertThat(bucketResult.getFinalBucket()).isEqualTo("two"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(7); } @Disabled("Remove to run test") @Test + @DisplayName("Measure one step using bucket one of size 1 and bucket two of size 3 - start with bucket two") public void testBucketOneSizeOneBucketTwoSizeThreeStartWithTwo() { - TwoBucket twoBucket = new TwoBucket(1, 3, 3, "two"); + Result bucketResult = new TwoBucket(1, 3, 3, "two").getResult(); - assertThat(twoBucket.getTotalMoves()).isEqualTo(1); - assertThat(twoBucket.getFinalBucket()).isEqualTo("two"); - assertThat(twoBucket.getOtherBucket()).isEqualTo(0); + assertThat(bucketResult.getTotalMoves()).isEqualTo(1); + assertThat(bucketResult.getFinalBucket()).isEqualTo("two"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(0); } @Disabled("Remove to run test") @Test + @DisplayName( + "Measure using bucket one of size 2 and bucket two of size 3 - " + + "start with bucket one and end with bucket two" + ) public void testBucketOneSizeTwoBucketTwoSizeThreeStartWithOne() { - TwoBucket twoBucket = new TwoBucket(2, 3, 3, "one"); + Result bucketResult = new TwoBucket(2, 3, 3, "one").getResult(); - assertThat(twoBucket.getTotalMoves()).isEqualTo(2); - assertThat(twoBucket.getFinalBucket()).isEqualTo("two"); - assertThat(twoBucket.getOtherBucket()).isEqualTo(2); + assertThat(bucketResult.getTotalMoves()).isEqualTo(2); + assertThat(bucketResult.getFinalBucket()).isEqualTo("two"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(2); + + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Measure using bucket one much bigger than bucket two") + public void testBucketOneMuchBiggerThanBucketTwo() { + + Result bucketResult = new TwoBucket(5, 1, 2, "one").getResult(); + + assertThat(bucketResult.getTotalMoves()).isEqualTo(6); + assertThat(bucketResult.getFinalBucket()).isEqualTo("one"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(1); + + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Measure using bucket one much smaller than bucket two") + public void testBucketOneMuchSmallerThanBucketTwo() { + + Result bucketResult = new TwoBucket(3, 15, 9, "one").getResult(); + + assertThat(bucketResult.getTotalMoves()).isEqualTo(6); + assertThat(bucketResult.getFinalBucket()).isEqualTo("two"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(0); + + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Not possible to reach the goal") + public void testReachingGoalIsImpossible() { + + assertThatExceptionOfType(UnreachableGoalException.class) + .isThrownBy(() -> new TwoBucket(6, 15, 5, "one").getResult()); + + } + + @Disabled("Remove to run test") + @Test + @DisplayName("With the same buckets but a different goal, then it is possible") + public void testBucketOneSizeSixBucketTwoSizeFifteenStartWithOne() { + + Result bucketResult = new TwoBucket(6, 15, 9, "one").getResult(); + + assertThat(bucketResult.getTotalMoves()).isEqualTo(10); + assertThat(bucketResult.getFinalBucket()).isEqualTo("two"); + assertThat(bucketResult.getOtherBucket()).isEqualTo(0); + + } + + @Disabled("Remove to run test") + @Test + @DisplayName("Goal larger than both buckets is impossible") + public void testGoalLargerThanBothBucketsIsImpossible() { + + assertThatExceptionOfType(UnreachableGoalException.class) + .isThrownBy(() -> new TwoBucket(5, 7, 8, "one").getResult()); } } 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/two-fer/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/two-fer/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/two-fer/gradlew b/exercises/practice/two-fer/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/two-fer/gradlew +++ b/exercises/practice/two-fer/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/two-fer/gradlew.bat b/exercises/practice/two-fer/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/two-fer/gradlew.bat +++ b/exercises/practice/two-fer/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/two-fer/src/test/java/TwoferTest.java b/exercises/practice/two-fer/src/test/java/TwoferTest.java index 67dd68646..3993057d6 100644 --- a/exercises/practice/two-fer/src/test/java/TwoferTest.java +++ b/exercises/practice/two-fer/src/test/java/TwoferTest.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 name given") public void noNameGiven() { assertThat(twofer.twofer(null)) .isEqualTo("One for you, one for me."); @@ -21,6 +23,7 @@ public void noNameGiven() { @Disabled("Remove to run test") @Test + @DisplayName("a name given") public void aNameGiven() { assertThat(twofer.twofer("Alice")) .isEqualTo("One for Alice, one for me."); @@ -28,6 +31,7 @@ public void aNameGiven() { @Disabled("Remove to run test") @Test + @DisplayName("another name given") public void anotherNameGiven() { assertThat(twofer.twofer("Bob")) .isEqualTo("One for Bob, one for me."); diff --git a/exercises/practice/variable-length-quantity/.meta/config.json b/exercises/practice/variable-length-quantity/.meta/config.json index 1949d7e7b..331713ee2 100644 --- a/exercises/practice/variable-length-quantity/.meta/config.json +++ b/exercises/practice/variable-length-quantity/.meta/config.json @@ -12,7 +12,8 @@ "muzimuzhi", "SleeplessByte", "sshine", - "vpondala" + "vpondala", + "Xinri" ], "files": { "solution": [ diff --git a/exercises/practice/variable-length-quantity/.meta/tests.toml b/exercises/practice/variable-length-quantity/.meta/tests.toml index 923fa0c1a..53be789a3 100644 --- a/exercises/practice/variable-length-quantity/.meta/tests.toml +++ b/exercises/practice/variable-length-quantity/.meta/tests.toml @@ -1,81 +1,103 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [35c9db2e-f781-4c52-b73b-8e76427defd0] -description = "zero" +description = "Encode a series of integers, producing a series of bytes. -> zero" [be44d299-a151-4604-a10e-d4b867f41540] -description = "arbitrary single byte" +description = "Encode a series of integers, producing a series of bytes. -> arbitrary single byte" + +[890bc344-cb80-45af-b316-6806a6971e81] +description = "Encode a series of integers, producing a series of bytes. -> asymmetric single byte" [ea399615-d274-4af6-bbef-a1c23c9e1346] -description = "largest single byte" +description = "Encode a series of integers, producing a series of bytes. -> largest single byte" [77b07086-bd3f-4882-8476-8dcafee79b1c] -description = "smallest double byte" +description = "Encode a series of integers, producing a series of bytes. -> smallest double byte" [63955a49-2690-4e22-a556-0040648d6b2d] -description = "arbitrary double byte" +description = "Encode a series of integers, producing a series of bytes. -> arbitrary double byte" + +[4977d113-251b-4d10-a3ad-2f5a7756bb58] +description = "Encode a series of integers, producing a series of bytes. -> asymmetric double byte" [29da7031-0067-43d3-83a7-4f14b29ed97a] -description = "largest double byte" +description = "Encode a series of integers, producing a series of bytes. -> largest double byte" [3345d2e3-79a9-4999-869e-d4856e3a8e01] -description = "smallest triple byte" +description = "Encode a series of integers, producing a series of bytes. -> smallest triple byte" [5df0bc2d-2a57-4300-a653-a75ee4bd0bee] -description = "arbitrary triple byte" +description = "Encode a series of integers, producing a series of bytes. -> arbitrary triple byte" + +[6731045f-1e00-4192-b5ae-98b22e17e9f7] +description = "Encode a series of integers, producing a series of bytes. -> asymmetric triple byte" [f51d8539-312d-4db1-945c-250222c6aa22] -description = "largest triple byte" +description = "Encode a series of integers, producing a series of bytes. -> largest triple byte" [da78228b-544f-47b7-8bfe-d16b35bbe570] -description = "smallest quadruple byte" +description = "Encode a series of integers, producing a series of bytes. -> smallest quadruple byte" [11ed3469-a933-46f1-996f-2231e05d7bb6] -description = "arbitrary quadruple byte" +description = "Encode a series of integers, producing a series of bytes. -> arbitrary quadruple byte" + +[b45ef770-cbba-48c2-bd3c-c6362679516e] +description = "Encode a series of integers, producing a series of bytes. -> asymmetric quadruple byte" [d5f3f3c3-e0f1-4e7f-aad0-18a44f223d1c] -description = "largest quadruple byte" +description = "Encode a series of integers, producing a series of bytes. -> largest quadruple byte" [91a18b33-24e7-4bfb-bbca-eca78ff4fc47] -description = "smallest quintuple byte" +description = "Encode a series of integers, producing a series of bytes. -> smallest quintuple byte" [5f34ff12-2952-4669-95fe-2d11b693d331] -description = "arbitrary quintuple byte" +description = "Encode a series of integers, producing a series of bytes. -> arbitrary quintuple byte" + +[9be46731-7cd5-415c-b960-48061cbc1154] +description = "Encode a series of integers, producing a series of bytes. -> asymmetric quintuple byte" [7489694b-88c3-4078-9864-6fe802411009] -description = "maximum 32-bit integer input" +description = "Encode a series of integers, producing a series of bytes. -> maximum 32-bit integer input" [f9b91821-cada-4a73-9421-3c81d6ff3661] -description = "two single-byte values" +description = "Encode a series of integers, producing a series of bytes. -> two single-byte values" [68694449-25d2-4974-ba75-fa7bb36db212] -description = "two multi-byte values" +description = "Encode a series of integers, producing a series of bytes. -> two multi-byte values" [51a06b5c-de1b-4487-9a50-9db1b8930d85] -description = "many multi-byte values" +description = "Encode a series of integers, producing a series of bytes. -> many multi-byte values" [baa73993-4514-4915-bac0-f7f585e0e59a] -description = "one byte" +description = "Decode a series of bytes, producing a series of integers. -> one byte" [72e94369-29f9-46f2-8c95-6c5b7a595aee] -description = "two bytes" +description = "Decode a series of bytes, producing a series of integers. -> two bytes" [df5a44c4-56f7-464e-a997-1db5f63ce691] -description = "three bytes" +description = "Decode a series of bytes, producing a series of integers. -> three bytes" [1bb58684-f2dc-450a-8406-1f3452aa1947] -description = "four bytes" +description = "Decode a series of bytes, producing a series of integers. -> four bytes" [cecd5233-49f1-4dd1-a41a-9840a40f09cd] -description = "maximum 32-bit integer" +description = "Decode a series of bytes, producing a series of integers. -> maximum 32-bit integer" [e7d74ba3-8b8e-4bcb-858d-d08302e15695] -description = "incomplete sequence causes error" +description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error" [aa378291-9043-4724-bc53-aca1b4a3fcb6] -description = "incomplete sequence causes error, even if value is zero" +description = "Decode a series of bytes, producing a series of integers. -> incomplete sequence causes error, even if value is zero" [a91e6f5a-c64a-48e3-8a75-ce1a81e0ebee] -description = "multiple values" +description = "Decode a series of bytes, producing a series of integers. -> multiple values" 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/variable-length-quantity/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/variable-length-quantity/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/variable-length-quantity/gradlew b/exercises/practice/variable-length-quantity/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/variable-length-quantity/gradlew +++ b/exercises/practice/variable-length-quantity/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/variable-length-quantity/gradlew.bat b/exercises/practice/variable-length-quantity/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/variable-length-quantity/gradlew.bat +++ b/exercises/practice/variable-length-quantity/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/variable-length-quantity/src/test/java/VariableLengthQuantityTest.java b/exercises/practice/variable-length-quantity/src/test/java/VariableLengthQuantityTest.java index 5b25441e7..5e3059937 100644 --- a/exercises/practice/variable-length-quantity/src/test/java/VariableLengthQuantityTest.java +++ b/exercises/practice/variable-length-quantity/src/test/java/VariableLengthQuantityTest.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 VariableLengthQuantityTest { new VariableLengthQuantity(); @Test + @DisplayName("zero") public void testZero() { List expected = Arrays.asList("0x0"); List numbers = Arrays.asList(0x0L); @@ -22,6 +24,7 @@ public void testZero() { @Disabled("Remove to run test") @Test + @DisplayName("arbitrary single byte") public void testArbitrarySingleByte() { List expected = Arrays.asList("0x40"); List numbers = Arrays.asList(0x40L); @@ -31,6 +34,17 @@ public void testArbitrarySingleByte() { @Disabled("Remove to run test") @Test + @DisplayName("asymmetric single byte") + public void testAsymmetricSingleByte() { + List expected = Arrays.asList("0x53"); + List numbers = Arrays.asList(0x53L); + + assertThat(variableLengthQuantity.encode(numbers)).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("largest single byte") public void testLargestSingleByte() { List expected = Arrays.asList("0x7f"); List numbers = Arrays.asList(0x7fL); @@ -40,6 +54,7 @@ public void testLargestSingleByte() { @Disabled("Remove to run test") @Test + @DisplayName("smallest double byte") public void testSmallestDoubleByte() { List expected = Arrays.asList("0x81", "0x0"); List numbers = Arrays.asList(0x80L); @@ -49,6 +64,7 @@ public void testSmallestDoubleByte() { @Disabled("Remove to run test") @Test + @DisplayName("arbitrary double byte") public void testArbitraryDoubleByte() { List expected = Arrays.asList("0xc0", "0x0"); List numbers = Arrays.asList(0x2000L); @@ -58,6 +74,17 @@ public void testArbitraryDoubleByte() { @Disabled("Remove to run test") @Test + @DisplayName("asymmetric double byte") + public void testAsymmetricDoubleByte() { + List expected = Arrays.asList("0x81", "0x2d"); + List numbers = Arrays.asList(0xadL); + + assertThat(variableLengthQuantity.encode(numbers)).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("largest double byte") public void testLargestDoubleByte() { List expected = Arrays.asList("0xff", "0x7f"); List numbers = Arrays.asList(0x3fffL); @@ -67,6 +94,7 @@ public void testLargestDoubleByte() { @Disabled("Remove to run test") @Test + @DisplayName("smallest triple byte") public void testSmallestTripleByte() { List expected = Arrays.asList("0x81", "0x80", "0x0"); List numbers = Arrays.asList(0x4000L); @@ -76,6 +104,7 @@ public void testSmallestTripleByte() { @Disabled("Remove to run test") @Test + @DisplayName("arbitrary triple byte") public void testArbitraryTripleByte() { List expected = Arrays.asList("0xc0", "0x80", "0x0"); List numbers = Arrays.asList(0x100000L); @@ -85,6 +114,17 @@ public void testArbitraryTripleByte() { @Disabled("Remove to run test") @Test + @DisplayName("asymmetric triple byte") + public void testAsymmetricTripleByte() { + List expected = Arrays.asList("0x87", "0xab", "0x1c"); + List numbers = Arrays.asList(0x1d59cL); + + assertThat(variableLengthQuantity.encode(numbers)).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("largest triple byte") public void testLargestTripleByte() { List expected = Arrays.asList("0xff", "0xff", "0x7f"); List numbers = Arrays.asList(0x1fffffL); @@ -94,6 +134,7 @@ public void testLargestTripleByte() { @Disabled("Remove to run test") @Test + @DisplayName("smallest quadruple byte") public void testSmallestQuadrupleByte() { List expected = Arrays.asList("0x81", "0x80", "0x80", "0x0"); List numbers = Arrays.asList(0x200000L); @@ -103,6 +144,7 @@ public void testSmallestQuadrupleByte() { @Disabled("Remove to run test") @Test + @DisplayName("arbitrary quadruple byte") public void testArbitraryQuadrupleByte() { List expected = Arrays.asList("0xc0", "0x80", "0x80", "0x0"); List numbers = Arrays.asList(0x8000000L); @@ -112,6 +154,17 @@ public void testArbitraryQuadrupleByte() { @Disabled("Remove to run test") @Test + @DisplayName("asymmetric quadruple byte") + public void testAsymmetricQuadrupleByte() { + List expected = Arrays.asList("0x81", "0xd5", "0xee", "0x4"); + List numbers = Arrays.asList(0x357704L); + + assertThat(variableLengthQuantity.encode(numbers)).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("largest quadruple byte") public void testLargestQuadrupleByte() { List expected = Arrays.asList("0xff", "0xff", "0xff", "0x7f"); List numbers = Arrays.asList(0xfffffffL); @@ -121,6 +174,7 @@ public void testLargestQuadrupleByte() { @Disabled("Remove to run test") @Test + @DisplayName("smallest quintuple byte") public void testSmallestQuintupleByte() { List expected = Arrays.asList("0x81", "0x80", "0x80", "0x80", "0x0"); List numbers = Arrays.asList(0x10000000L); @@ -130,6 +184,7 @@ public void testSmallestQuintupleByte() { @Disabled("Remove to run test") @Test + @DisplayName("arbitrary quintuple byte") public void testArbitraryQuintupleByte() { List expected = Arrays.asList("0x8f", "0xf8", "0x80", "0x80", "0x0"); List numbers = Arrays.asList(0xff000000L); @@ -139,6 +194,17 @@ public void testArbitraryQuintupleByte() { @Disabled("Remove to run test") @Test + @DisplayName("asymmetric quintuple byte") + public void testAsymmetricQuintupleByte() { + List expected = Arrays.asList("0x88", "0xb3", "0x95", "0xc2", "0x5"); + List numbers = Arrays.asList(0x86656105L); + + assertThat(variableLengthQuantity.encode(numbers)).isEqualTo(expected); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("maximum 32-bit integer input") public void testMaximum32BitIntegerInput() { List expected = Arrays.asList("0x8f", "0xff", "0xff", "0xff", "0x7f"); List numbers = Arrays.asList(0xffffffffL); @@ -148,6 +214,7 @@ public void testMaximum32BitIntegerInput() { @Disabled("Remove to run test") @Test + @DisplayName("two single-byte values") public void testTwoSingleByteValues() { List expected = Arrays.asList("0x40", "0x7f"); List numbers = Arrays.asList(0x40L, 0x7fL); @@ -157,6 +224,7 @@ public void testTwoSingleByteValues() { @Disabled("Remove to run test") @Test + @DisplayName("two multi-byte values") public void testTwoMultiByteValues() { List expected = Arrays.asList("0x81", "0x80", "0x0", "0xc8", "0xe8", "0x56"); List numbers = Arrays.asList(0x4000L, 0x123456L); @@ -166,6 +234,7 @@ public void testTwoMultiByteValues() { @Disabled("Remove to run test") @Test + @DisplayName("many multi-byte values") public void testManyMultiByteValues() { List expected = Arrays.asList("0xc0", "0x0", "0xc8", "0xe8", "0x56", "0xff", "0xff", "0xff", @@ -179,6 +248,7 @@ public void testManyMultiByteValues() { @Disabled("Remove to run test") @Test + @DisplayName("one byte") public void testDecodeOneByte() { List expected = Arrays.asList("0x7f"); List bytes = Arrays.asList(0x7fL); @@ -188,6 +258,7 @@ public void testDecodeOneByte() { @Disabled("Remove to run test") @Test + @DisplayName("two bytes") public void testDecodeTwoBytes() { List expected = Arrays.asList("0x2000"); List bytes = Arrays.asList(0xc0L, 0x0L); @@ -197,6 +268,7 @@ public void testDecodeTwoBytes() { @Disabled("Remove to run test") @Test + @DisplayName("three bytes") public void testDecodeThreeBytes() { List expected = Arrays.asList("0x1fffff"); List bytes = Arrays.asList(0xffL, 0xffL, 0x7fL); @@ -206,6 +278,7 @@ public void testDecodeThreeBytes() { @Disabled("Remove to run test") @Test + @DisplayName("four bytes") public void testDecodeFourBytes() { List expected = Arrays.asList("0x200000"); List bytes = Arrays.asList(0x81L, 0x80L, 0x80L, 0x0L); @@ -215,6 +288,7 @@ public void testDecodeFourBytes() { @Disabled("Remove to run test") @Test + @DisplayName("maximum 32-bit integer") public void testDecodeMaximum32BitInteger() { List expected = Arrays.asList("0xffffffff"); List bytes = Arrays.asList(0x8fL, 0xffL, 0xffL, 0xffL, 0x7fL); @@ -224,6 +298,7 @@ public void testDecodeMaximum32BitInteger() { @Disabled("Remove to run test") @Test + @DisplayName("incomplete sequence causes error") public void testCannotDecodeIncompleteSequence() { List bytes = Arrays.asList(0xffL); @@ -234,6 +309,7 @@ public void testCannotDecodeIncompleteSequence() { @Disabled("Remove to run test") @Test + @DisplayName("incomplete sequence causes error, even if value is zero") public void testCannotDecodeIncompleteSequenceEvenIfValueIsZero() { List bytes = Arrays.asList(0x80L); @@ -244,6 +320,7 @@ public void testCannotDecodeIncompleteSequenceEvenIfValueIsZero() { @Disabled("Remove to run test") @Test + @DisplayName("multiple values") public void testDecodeMultipleBytes() { List expected = Arrays.asList("0x2000", "0x123456", "0xfffffff", "0x0", "0x3fff", 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-count/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/word-count/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/word-count/gradlew b/exercises/practice/word-count/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/word-count/gradlew +++ b/exercises/practice/word-count/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/word-count/gradlew.bat b/exercises/practice/word-count/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/word-count/gradlew.bat +++ b/exercises/practice/word-count/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/word-count/src/test/java/WordCountTest.java b/exercises/practice/word-count/src/test/java/WordCountTest.java index bd5f70fdb..0c2cd4e01 100644 --- a/exercises/practice/word-count/src/test/java/WordCountTest.java +++ b/exercises/practice/word-count/src/test/java/WordCountTest.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.HashMap; @@ -21,6 +22,7 @@ public void setup() { @Test + @DisplayName("count one word") public void countOneWord() { expectedWordCount.put("word", 1); @@ -30,6 +32,7 @@ public void countOneWord() { @Disabled("Remove to run test") @Test + @DisplayName("count one of each word") public void countOneOfEachWord() { expectedWordCount.put("one", 1); expectedWordCount.put("of", 1); @@ -41,6 +44,7 @@ public void countOneOfEachWord() { @Disabled("Remove to run test") @Test + @DisplayName("multiple occurrences of a word") public void multipleOccurrencesOfAWord() { expectedWordCount.put("one", 1); expectedWordCount.put("fish", 4); @@ -54,6 +58,7 @@ public void multipleOccurrencesOfAWord() { @Disabled("Remove to run test") @Test + @DisplayName("handles cramped lists") public void handlesCrampedLists() { expectedWordCount.put("one", 1); expectedWordCount.put("two", 1); @@ -65,6 +70,7 @@ public void handlesCrampedLists() { @Disabled("Remove to run test") @Test + @DisplayName("handles expanded lists") public void handlesExpandedLists() { expectedWordCount.put("one", 1); expectedWordCount.put("two", 1); @@ -76,6 +82,7 @@ public void handlesExpandedLists() { @Disabled("Remove to run test") @Test + @DisplayName("ignore punctuation") public void ignorePunctuation() { expectedWordCount.put("car", 1); expectedWordCount.put("carpet", 1); @@ -90,6 +97,7 @@ public void ignorePunctuation() { @Disabled("Remove to run test") @Test + @DisplayName("include numbers") public void includeNumbers() { expectedWordCount.put("testing", 2); expectedWordCount.put("1", 1); @@ -101,6 +109,7 @@ public void includeNumbers() { @Disabled("Remove to run test") @Test + @DisplayName("normalize case") public void normalizeCase() { expectedWordCount.put("go", 3); expectedWordCount.put("stop", 2); @@ -111,6 +120,7 @@ public void normalizeCase() { @Disabled("Remove to run test") @Test + @DisplayName("with apostrophes") public void withApostrophes() { expectedWordCount.put("first", 1); expectedWordCount.put("don't", 2); @@ -127,36 +137,39 @@ public void withApostrophes() { @Disabled("Remove to run test") @Test - public void substringsFromTheBeginning() { + @DisplayName("with quotations") + public void withQuotations() { expectedWordCount.put("joe", 1); expectedWordCount.put("can't", 1); expectedWordCount.put("tell", 1); expectedWordCount.put("between", 1); - expectedWordCount.put("app", 1); - expectedWordCount.put("apple", 1); + expectedWordCount.put("large", 2); expectedWordCount.put("and", 1); - expectedWordCount.put("a", 1); - actualWordCount = wordCount.phrase("Joe can't tell between app, apple and a."); + actualWordCount = wordCount.phrase("Joe can't tell between 'large' and large."); assertThat(actualWordCount).isEqualTo(expectedWordCount); } @Disabled("Remove to run test") @Test - public void withQuotations() { + @DisplayName("substrings from the beginning") + public void substringsFromTheBeginning() { expectedWordCount.put("joe", 1); expectedWordCount.put("can't", 1); expectedWordCount.put("tell", 1); expectedWordCount.put("between", 1); - expectedWordCount.put("large", 2); + expectedWordCount.put("app", 1); + expectedWordCount.put("apple", 1); expectedWordCount.put("and", 1); + expectedWordCount.put("a", 1); - actualWordCount = wordCount.phrase("Joe can't tell between 'large' and large."); + actualWordCount = wordCount.phrase("Joe can't tell between app, apple and a."); assertThat(actualWordCount).isEqualTo(expectedWordCount); } @Disabled("Remove to run test") @Test + @DisplayName("multiple spaces not detected as a word") public void multipleSpacesNotDetectedAsAWord() { expectedWordCount.put("multiple", 1); expectedWordCount.put("whitespaces", 1); @@ -167,6 +180,7 @@ public void multipleSpacesNotDetectedAsAWord() { @Disabled("Remove to run test") @Test + @DisplayName("alternating word separators not detected as a word") public void alternatingWordSeperatorsNotDetectedAsAWord() { expectedWordCount.put("one", 1); expectedWordCount.put("two", 1); @@ -178,6 +192,7 @@ public void alternatingWordSeperatorsNotDetectedAsAWord() { @Disabled("Remove to run test") @Test + @DisplayName("quotation for word with apostrophe") public void quotationForWordWithApostrophe() { expectedWordCount.put("can", 1); expectedWordCount.put("can't", 2); 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/word-search/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/word-search/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/word-search/gradlew b/exercises/practice/word-search/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/word-search/gradlew +++ b/exercises/practice/word-search/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/word-search/gradlew.bat b/exercises/practice/word-search/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/word-search/gradlew.bat +++ b/exercises/practice/word-search/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/word-search/src/test/java/WordSearcherTest.java b/exercises/practice/word-search/src/test/java/WordSearcherTest.java index 8460366c5..3a7d9291e 100644 --- a/exercises/practice/word-search/src/test/java/WordSearcherTest.java +++ b/exercises/practice/word-search/src/test/java/WordSearcherTest.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.HashMap; @@ -19,6 +20,7 @@ public void setUp() { } @Test + @DisplayName("Should accept an initial game grid and a target search word") public void testAcceptsInitialGridAndTargetWord() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.empty()); @@ -37,6 +39,7 @@ public void testAcceptsInitialGridAndTargetWord() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate one word written left to right") public void testLocatesOneWordWrittenLeftToRight() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 1), new Pair(7, 1)))); @@ -55,6 +58,7 @@ public void testLocatesOneWordWrittenLeftToRight() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate the same word written left to right in a different position") public void testShouldLocateTheSameWordLeftToRightInDifferentPosition() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(3, 1), new Pair(9, 1)))); @@ -73,6 +77,7 @@ public void testShouldLocateTheSameWordLeftToRightInDifferentPosition() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate a different left to right word") public void testShouldLocateADifferentLeftToRightWord() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("coffee", Optional.of(new WordLocation(new Pair(1, 1), new Pair(6, 1)))); @@ -91,6 +96,7 @@ public void testShouldLocateADifferentLeftToRightWord() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate that different left to right word in a different position") public void testShouldLocateThatDifferentLeftToRightWordInADifferentPosition() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("coffee", Optional.of(new WordLocation(new Pair(2, 1), new Pair(7, 1)))); @@ -109,6 +115,7 @@ public void testShouldLocateThatDifferentLeftToRightWordInADifferentPosition() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate a left to right word in two line grid") public void testShouldLocateLeftToRightWordInTwoLineGrid() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(2, 2), new Pair(8, 2)))); @@ -128,6 +135,7 @@ public void testShouldLocateLeftToRightWordInTwoLineGrid() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate a left to right word in three line grid") public void testShouldLocateLeftToRightWordInThreeLineGrid() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 3), new Pair(7, 3)))); @@ -148,6 +156,7 @@ public void testShouldLocateLeftToRightWordInThreeLineGrid() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate a left to right word in ten line grid") public void testLocatesWordWrittenLeftToRightInTenLineGrid() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -175,6 +184,7 @@ public void testLocatesWordWrittenLeftToRightInTenLineGrid() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate that left to right word in a different position in a ten line grid") public void testLocatesSameWordWrittenLeftToRightInDifferentTenLineGrid() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 9), new Pair(7, 9)))); @@ -202,6 +212,7 @@ public void testLocatesSameWordWrittenLeftToRightInDifferentTenLineGrid() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate a different left to right word in a ten line grid") public void testLocatesDifferentWordWrittenLeftToRightInTenLineGrid() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("fortran", Optional.of(new WordLocation(new Pair(1, 7), new Pair(7, 7)))); @@ -229,6 +240,7 @@ public void testLocatesDifferentWordWrittenLeftToRightInTenLineGrid() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate multiple words") public void testShouldLocateMultipleWords() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("fortran", Optional.of(new WordLocation(new Pair(1, 7), new Pair(7, 7)))); @@ -257,6 +269,7 @@ public void testShouldLocateMultipleWords() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate a single word written right to left") public void testShouldLocateASingleWordRightToLeft() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("elixir", Optional.of(new WordLocation(new Pair(6, 1), new Pair(1, 1)))); @@ -275,6 +288,7 @@ public void testShouldLocateASingleWordRightToLeft() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate multiple words written in different horizontal directions") public void testShouldLocateMultipleWordsWrittenInDifferentHorizontalDirections() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("elixir", Optional.of(new WordLocation(new Pair(6, 5), new Pair(1, 5)))); @@ -303,6 +317,7 @@ public void testShouldLocateMultipleWordsWrittenInDifferentHorizontalDirections( @Disabled("Remove to run test") @Test + @DisplayName("Should locate words written top to bottom") public void testLocatesWordsWrittenTopToBottom() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -332,6 +347,7 @@ public void testLocatesWordsWrittenTopToBottom() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate words written bottom to top") public void testLocatesWordsWrittenBottomToTop() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -360,6 +376,7 @@ public void testLocatesWordsWrittenBottomToTop() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate words written top left to bottom right") public void testLocatesWordsWrittenTopLeftToBottomRight() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -389,6 +406,7 @@ public void testLocatesWordsWrittenTopLeftToBottomRight() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate words written bottom right to top left") public void testLocatesWordsWrittenBottomRightToTopLeft() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -419,6 +437,7 @@ public void testLocatesWordsWrittenBottomRightToTopLeft() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate words written bottom left to top right") public void testLocatesWordsWrittenBottomLeftToTopRight() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -450,6 +469,7 @@ public void testLocatesWordsWrittenBottomLeftToTopRight() { @Disabled("Remove to run test") @Test + @DisplayName("Should locate words written top right to bottom left") public void testLocatesWordsWrittenTopRightToBottomLeft() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -482,6 +502,7 @@ public void testLocatesWordsWrittenTopRightToBottomLeft() { @Disabled("Remove to run test") @Test + @DisplayName("Should fail to locate a word that is not in the puzzle") public void testFailsToLocateAWordsThatIsNotInThePuzzle() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("clojure", Optional.of(new WordLocation(new Pair(1, 10), new Pair(7, 10)))); @@ -515,6 +536,7 @@ public void testFailsToLocateAWordsThatIsNotInThePuzzle() { @Disabled("Remove to run test") @Test + @DisplayName("Should fail to locate words that are not on horizontal, vertical, or diagonal lines") public void testFailToLocateWordsThatAreNotOnHorizontalVerticalOrDiagonalLines() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("aef", Optional.empty()); @@ -535,6 +557,7 @@ public void testFailToLocateWordsThatAreNotOnHorizontalVerticalOrDiagonalLines() @Disabled("Remove to run test") @Test + @DisplayName("Should not concatenate different lines to find a horizontal word") public void testNotConcatenateDifferentLinesToFindAHorizontalWord() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("elixir", Optional.empty()); @@ -552,6 +575,7 @@ public void testNotConcatenateDifferentLinesToFindAHorizontalWord() { @Disabled("Remove to run test") @Test + @DisplayName("Should not wrap around horizontally to find a word") public void testNotWrapAroundHorizontallyToFindAWord() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("lisp", Optional.empty()); @@ -570,6 +594,7 @@ public void testNotWrapAroundHorizontallyToFindAWord() { @Disabled("Remove to run test") @Test + @DisplayName("Should not wrap around vertically to find a word") public void testNotWrapAroundVerticallyToFindAWord() { Map> expectedLocations = new HashMap<>(); expectedLocations.put("rust", Optional.empty()); diff --git a/exercises/practice/wordy/.meta/config.json b/exercises/practice/wordy/.meta/config.json index 2e6e383fd..78f4c3f91 100644 --- a/exercises/practice/wordy/.meta/config.json +++ b/exercises/practice/wordy/.meta/config.json @@ -21,6 +21,7 @@ "vasouv", "vivshaw", "vpondala", + "Xinri", "Zaldrick" ], "files": { diff --git a/exercises/practice/wordy/.meta/tests.toml b/exercises/practice/wordy/.meta/tests.toml index 912d57600..a0a83ed0b 100644 --- a/exercises/practice/wordy/.meta/tests.toml +++ b/exercises/practice/wordy/.meta/tests.toml @@ -1,13 +1,32 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [88bf4b28-0de3-4883-93c7-db1b14aa806e] description = "just a number" +[18983214-1dfc-4ebd-ac77-c110dde699ce] +description = "just a zero" + +[607c08ee-2241-4288-916d-dae5455c87e6] +description = "just a negative number" + [bb8c655c-cf42-4dfc-90e0-152fcfd8d4e0] description = "addition" +[bb9f2082-171c-46ad-ad4e-c3f72087c1b5] +description = "addition with a left hand zero" + +[6fa05f17-405a-4742-80ae-5d1a8edb0d5d] +description = "addition with a right hand zero" + [79e49e06-c5ae-40aa-a352-7a3a01f70015] description = "more addition" 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/wordy/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/wordy/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/wordy/gradlew b/exercises/practice/wordy/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/wordy/gradlew +++ b/exercises/practice/wordy/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/wordy/gradlew.bat b/exercises/practice/wordy/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/wordy/gradlew.bat +++ b/exercises/practice/wordy/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/wordy/src/test/java/WordProblemSolverTest.java b/exercises/practice/wordy/src/test/java/WordProblemSolverTest.java index 70f0a1c30..79c4a1d41 100644 --- a/exercises/practice/wordy/src/test/java/WordProblemSolverTest.java +++ b/exercises/practice/wordy/src/test/java/WordProblemSolverTest.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,129 +10,175 @@ public class WordProblemSolverTest { WordProblemSolver solver = new WordProblemSolver(); @Test + @DisplayName("just a number") public void testJustANumber() { assertThat(solver.solve("What is 5?")).isEqualTo(5); } + @Test + @DisplayName("just a zero") + public void testJustAZero() { + assertThat(solver.solve("What is 0?")).isEqualTo(0); + } + + @Test + @DisplayName("just a negative number") + public void testJustANegativeNumber() { + assertThat(solver.solve("What is -123?")).isEqualTo(-123); + } + @Disabled("Remove to run test") @Test + @DisplayName("addition") public void testSingleAddition1() { assertThat(solver.solve("What is 1 plus 1?")).isEqualTo(2); } @Disabled("Remove to run test") @Test + @DisplayName("addition with a left hand zero") + public void testAdditionWithALeftHandZero() { + assertThat(solver.solve("What is 0 plus 2?")).isEqualTo(2); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("addition with a right hand zero") + public void testAdditionWithARightHandZero() { + assertThat(solver.solve("What is 3 plus 0?")).isEqualTo(3); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("more addition") public void testSingleAddition2() { assertThat(solver.solve("What is 53 plus 2?")).isEqualTo(55); } @Disabled("Remove to run test") @Test + @DisplayName("addition with negative numbers") public void testSingleAdditionWithNegativeNumbers() { assertThat(solver.solve("What is -1 plus -10?")).isEqualTo(-11); } @Disabled("Remove to run test") @Test + @DisplayName("large addition") public void testSingleAdditionOfLargeNumbers() { assertThat(solver.solve("What is 123 plus 45678?")).isEqualTo(45801); } @Disabled("Remove to run test") @Test + @DisplayName("subtraction") public void testSingleSubtraction() { assertThat(solver.solve("What is 4 minus -12?")).isEqualTo(16); } @Disabled("Remove to run test") @Test + @DisplayName("multiplication") public void testSingleMultiplication() { assertThat(solver.solve("What is -3 multiplied by 25?")).isEqualTo(-75); } @Disabled("Remove to run test") @Test + @DisplayName("division") public void testSingleDivision() { assertThat(solver.solve("What is 33 divided by -3?")).isEqualTo(-11); } @Disabled("Remove to run test") @Test + @DisplayName("multiple additions") public void testMultipleAdditions() { assertThat(solver.solve("What is 1 plus 1 plus 1?")).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("addition and subtraction") public void testAdditionThenSubtraction() { assertThat(solver.solve("What is 1 plus 5 minus -2?")).isEqualTo(8); } @Disabled("Remove to run test") @Test + @DisplayName("multiple subtraction") public void testMultipleSubtractions() { assertThat(solver.solve("What is 20 minus 4 minus 13?")).isEqualTo(3); } @Disabled("Remove to run test") @Test + @DisplayName("subtraction then addition") public void testSubtractionThenAddition() { assertThat(solver.solve("What is 17 minus 6 plus 3?")).isEqualTo(14); } @Disabled("Remove to run test") @Test + @DisplayName("multiple multiplication") public void testMultipleMultiplications() { assertThat(solver.solve("What is 2 multiplied by -2 multiplied by 3?")).isEqualTo(-12); } @Disabled("Remove to run test") @Test + @DisplayName("addition and multiplication") public void testAdditionThenMultiplication() { assertThat(solver.solve("What is -3 plus 7 multiplied by -2?")).isEqualTo(-8); } @Disabled("Remove to run test") @Test + @DisplayName("multiple division") public void testMultipleDivisions() { assertThat(solver.solve("What is -12 divided by 2 divided by -3?")).isEqualTo(2); } @Disabled("Remove to run test") @Test + @DisplayName("unknown operation") public void testUnknownOperation() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> solver.solve("What is 52 cubed?")) - .withMessage("I'm sorry, I don't understand the question!"); + .isThrownBy(() -> solver.solve("What is 52 cubed?")) + .withMessage("I'm sorry, I don't understand the question!"); } @Disabled("Remove to run test") @Test + @DisplayName("Non math question") public void testNonMathQuestion() { // See https://en.wikipedia.org/wiki/President_of_the_United_States if you really need to know! assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> solver.solve("Who is the President of the United States?")) - .withMessage("I'm sorry, I don't understand the question!"); + .isThrownBy(() -> solver.solve("Who is the President of the United States?")) + .withMessage("I'm sorry, I don't understand the question!"); } @Disabled("Remove to run test") @Test + @DisplayName("reject problem missing an operand") public void testMissingAnOperand() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> solver.solve("What is 1 plus?")) - .withMessage("I'm sorry, I don't understand the question!"); + .isThrownBy(() -> solver.solve("What is 1 plus?")) + .withMessage("I'm sorry, I don't understand the question!"); } @Disabled("Remove to run test") @Test + @DisplayName("reject problem with no operands or operator") public void testNoOperandsOrOperators() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> solver.solve("What is?")) - .withMessage("I'm sorry, I don't understand the question!"); + .isThrownBy(() -> solver.solve("What is?")) + .withMessage("I'm sorry, I don't understand the question!"); } @Disabled("Remove to run test") @Test + @DisplayName("reject two operations in a row") public void testTwoOperationsInARow() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> solver.solve("What is 1 plus plus 2?")) @@ -140,6 +187,7 @@ public void testTwoOperationsInARow() { @Disabled("Remove to run test") @Test + @DisplayName("reject two numbers in a row") public void testTwoNumbersAfterOperation() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> solver.solve("What is 1 plus 2 1?")) @@ -148,6 +196,7 @@ public void testTwoNumbersAfterOperation() { @Disabled("Remove to run test") @Test + @DisplayName("reject postfix notation") public void testPostfixNotation() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> solver.solve("What is 1 2 plus?")) @@ -156,6 +205,7 @@ public void testPostfixNotation() { @Disabled("Remove to run test") @Test + @DisplayName("reject prefix notation") public void testPrefixNotation() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> solver.solve("What is plus 1 2?")) diff --git a/exercises/practice/yacht/.meta/config.json b/exercises/practice/yacht/.meta/config.json index 122b450c0..971e3587d 100644 --- a/exercises/practice/yacht/.meta/config.json +++ b/exercises/practice/yacht/.meta/config.json @@ -29,6 +29,6 @@ ] }, "blurb": "Score a single throw of dice in the game Yacht.", - "source": "James Kilfiger, using wikipedia", + "source": "James Kilfiger, using Wikipedia", "source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)" } 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/yacht/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/yacht/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/yacht/gradlew b/exercises/practice/yacht/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/yacht/gradlew +++ b/exercises/practice/yacht/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/yacht/gradlew.bat b/exercises/practice/yacht/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/yacht/gradlew.bat +++ b/exercises/practice/yacht/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/yacht/src/test/java/YachtTest.java b/exercises/practice/yacht/src/test/java/YachtTest.java index b2792817b..2a5e873e6 100644 --- a/exercises/practice/yacht/src/test/java/YachtTest.java +++ b/exercises/practice/yacht/src/test/java/YachtTest.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 YachtTest { @Test + @DisplayName("Yacht") public void yacht() { Yacht yacht = new Yacht(new int[]{ 5, 5, 5, 5, 5 }, YachtCategory.YACHT); assertThat(yacht.score()).isEqualTo(50); @@ -13,6 +15,7 @@ public void yacht() { @Disabled("Remove to run test") @Test + @DisplayName("Not Yacht") public void notYacht() { Yacht yacht = new Yacht(new int[]{ 1, 3, 3, 2, 5 }, YachtCategory.YACHT); assertThat(yacht.score()).isEqualTo(0); @@ -20,6 +23,7 @@ public void notYacht() { @Disabled("Remove to run test") @Test + @DisplayName("Ones") public void ones() { Yacht yacht = new Yacht(new int[]{ 1, 1, 1, 3, 5 }, YachtCategory.ONES); assertThat(yacht.score()).isEqualTo(3); @@ -27,6 +31,7 @@ public void ones() { @Disabled("Remove to run test") @Test + @DisplayName("Ones, out of order") public void onesOutOfOrder() { Yacht yacht = new Yacht(new int[]{ 3, 1, 1, 5, 1 }, YachtCategory.ONES); assertThat(yacht.score()).isEqualTo(3); @@ -34,6 +39,7 @@ public void onesOutOfOrder() { @Disabled("Remove to run test") @Test + @DisplayName("No ones") public void noOnes() { Yacht yacht = new Yacht(new int[]{ 4, 3, 6, 5, 5 }, YachtCategory.ONES); assertThat(yacht.score()).isEqualTo(0); @@ -41,6 +47,7 @@ public void noOnes() { @Disabled("Remove to run test") @Test + @DisplayName("Twos") public void twos() { Yacht yacht = new Yacht(new int[]{ 2, 3, 4, 5, 6 }, YachtCategory.TWOS); assertThat(yacht.score()).isEqualTo(2); @@ -48,6 +55,7 @@ public void twos() { @Disabled("Remove to run test") @Test + @DisplayName("Fours") public void fours() { Yacht yacht = new Yacht(new int[]{ 1, 4, 1, 4, 1 }, YachtCategory.FOURS); assertThat(yacht.score()).isEqualTo(8); @@ -55,6 +63,7 @@ public void fours() { @Disabled("Remove to run test") @Test + @DisplayName("Yacht counted as threes") public void yachtCountedAsThrees() { Yacht yacht = new Yacht(new int[]{ 3, 3, 3, 3, 3 }, YachtCategory.THREES); assertThat(yacht.score()).isEqualTo(15); @@ -62,6 +71,7 @@ public void yachtCountedAsThrees() { @Disabled("Remove to run test") @Test + @DisplayName("Yacht of 3s counted as fives") public void yachtOfThreesCountedAsFives() { Yacht yacht = new Yacht(new int[]{ 3, 3, 3, 3, 3 }, YachtCategory.FIVES); assertThat(yacht.score()).isEqualTo(0); @@ -69,6 +79,7 @@ public void yachtOfThreesCountedAsFives() { @Disabled("Remove to run test") @Test + @DisplayName("Fives") public void fives() { Yacht yacht = new Yacht(new int[]{ 1, 5, 3, 5, 3 }, YachtCategory.FIVES); assertThat(yacht.score()).isEqualTo(10); @@ -76,6 +87,7 @@ public void fives() { @Disabled("Remove to run test") @Test + @DisplayName("Sixes") public void sixes() { Yacht yacht = new Yacht(new int[]{ 2, 3, 4, 5, 6 }, YachtCategory.SIXES); assertThat(yacht.score()).isEqualTo(6); @@ -83,6 +95,7 @@ public void sixes() { @Disabled("Remove to run test") @Test + @DisplayName("Full house two small, three big") public void fullHouseTwoSmallThreeBig() { Yacht yacht = new Yacht(new int[]{ 2, 2, 4, 4, 4 }, YachtCategory.FULL_HOUSE); assertThat(yacht.score()).isEqualTo(16); @@ -90,6 +103,7 @@ public void fullHouseTwoSmallThreeBig() { @Disabled("Remove to run test") @Test + @DisplayName("Full house three small, two big") public void fullHouseThreeSmallTwoBig() { Yacht yacht = new Yacht(new int[]{ 5, 3, 3, 5, 3 }, YachtCategory.FULL_HOUSE); assertThat(yacht.score()).isEqualTo(19); @@ -97,6 +111,7 @@ public void fullHouseThreeSmallTwoBig() { @Disabled("Remove to run test") @Test + @DisplayName("Two pair is not a full house") public void twoPairIsNotAFullHouse() { Yacht yacht = new Yacht(new int[]{ 2, 2, 4, 4, 5 }, YachtCategory.FULL_HOUSE); assertThat(yacht.score()).isEqualTo(0); @@ -104,6 +119,7 @@ public void twoPairIsNotAFullHouse() { @Disabled("Remove to run test") @Test + @DisplayName("Four of a kind is not a full house") public void fourOfAKindIsNotAFullHouse() { Yacht yacht = new Yacht(new int[]{ 1, 4, 4, 4, 4 }, YachtCategory.FULL_HOUSE); assertThat(yacht.score()).isEqualTo(0); @@ -111,6 +127,7 @@ public void fourOfAKindIsNotAFullHouse() { @Disabled("Remove to run test") @Test + @DisplayName("Yacht is not a full house") public void yachtIsNotAFullHouse() { Yacht yacht = new Yacht(new int[]{ 2, 2, 2, 2, 2 }, YachtCategory.FULL_HOUSE); assertThat(yacht.score()).isEqualTo(0); @@ -118,6 +135,7 @@ public void yachtIsNotAFullHouse() { @Disabled("Remove to run test") @Test + @DisplayName("Four of a Kind") public void fourOfAKind() { Yacht yacht = new Yacht(new int[]{ 6, 6, 4, 6, 6 }, YachtCategory.FOUR_OF_A_KIND); assertThat(yacht.score()).isEqualTo(24); @@ -125,6 +143,7 @@ public void fourOfAKind() { @Disabled("Remove to run test") @Test + @DisplayName("Yacht can be scored as Four of a Kind") public void yachtCanBeScoredAsFourOfAKind() { Yacht yacht = new Yacht(new int[]{ 3, 3, 3, 3, 3 }, YachtCategory.FOUR_OF_A_KIND); assertThat(yacht.score()).isEqualTo(12); @@ -132,6 +151,7 @@ public void yachtCanBeScoredAsFourOfAKind() { @Disabled("Remove to run test") @Test + @DisplayName("Full house is not Four of a Kind") public void fullHouseIsNotFourOfAKind() { Yacht yacht = new Yacht(new int[]{ 3, 3, 3, 5, 5 }, YachtCategory.FOUR_OF_A_KIND); assertThat(yacht.score()).isEqualTo(0); @@ -139,6 +159,7 @@ public void fullHouseIsNotFourOfAKind() { @Disabled("Remove to run test") @Test + @DisplayName("Little Straight") public void littleStraight() { Yacht yacht = new Yacht(new int[]{ 3, 5, 4, 1, 2 }, YachtCategory.LITTLE_STRAIGHT); assertThat(yacht.score()).isEqualTo(30); @@ -146,6 +167,7 @@ public void littleStraight() { @Disabled("Remove to run test") @Test + @DisplayName("Little Straight as Big Straight") public void littleStraightAsBigStraight() { Yacht yacht = new Yacht(new int[]{ 1, 2, 3, 4, 5 }, YachtCategory.BIG_STRAIGHT); assertThat(yacht.score()).isEqualTo(0); @@ -153,6 +175,7 @@ public void littleStraightAsBigStraight() { @Disabled("Remove to run test") @Test + @DisplayName("Four in order but not a little straight") public void fourInOrderButNotALittleStraight() { Yacht yacht = new Yacht(new int[]{ 1, 1, 2, 3, 4 }, YachtCategory.LITTLE_STRAIGHT); assertThat(yacht.score()).isEqualTo(0); @@ -160,6 +183,7 @@ public void fourInOrderButNotALittleStraight() { @Disabled("Remove to run test") @Test + @DisplayName("No pairs but not a little straight") public void noPairsButNotALittleStraight() { Yacht yacht = new Yacht(new int[]{ 1, 2, 3, 4, 6 }, YachtCategory.LITTLE_STRAIGHT); assertThat(yacht.score()).isEqualTo(0); @@ -167,6 +191,7 @@ public void noPairsButNotALittleStraight() { @Disabled("Remove to run test") @Test + @DisplayName("Minimum is 1, maximum is 5, but not a little straight") public void minimumIs1MaximumIs5ButNotALittleStraight() { Yacht yacht = new Yacht(new int[]{ 1, 1, 3, 4, 5 }, YachtCategory.LITTLE_STRAIGHT); assertThat(yacht.score()).isEqualTo(0); @@ -174,6 +199,7 @@ public void minimumIs1MaximumIs5ButNotALittleStraight() { @Disabled("Remove to run test") @Test + @DisplayName("Big Straight") public void bigStraight() { Yacht yacht = new Yacht(new int[]{ 4, 6, 2, 5, 3 }, YachtCategory.BIG_STRAIGHT); assertThat(yacht.score()).isEqualTo(30); @@ -181,6 +207,7 @@ public void bigStraight() { @Disabled("Remove to run test") @Test + @DisplayName("Big Straight as little straight") public void bigStraightAsLittleStraight() { Yacht yacht = new Yacht(new int[]{ 6, 5, 4, 3, 2 }, YachtCategory.LITTLE_STRAIGHT); assertThat(yacht.score()).isEqualTo(0); @@ -188,6 +215,7 @@ public void bigStraightAsLittleStraight() { @Disabled("Remove to run test") @Test + @DisplayName("No pairs but not a big straight") public void noPairsButNotABigStraight() { Yacht yacht = new Yacht(new int[]{ 6, 5, 4, 3, 1 }, YachtCategory.BIG_STRAIGHT); assertThat(yacht.score()).isEqualTo(0); @@ -195,6 +223,7 @@ public void noPairsButNotABigStraight() { @Disabled("Remove to run test") @Test + @DisplayName("Choice") public void choice() { Yacht yacht = new Yacht(new int[]{ 3, 3, 5, 6, 6 }, YachtCategory.CHOICE); assertThat(yacht.score()).isEqualTo(23); @@ -202,6 +231,7 @@ public void choice() { @Disabled("Remove to run test") @Test + @DisplayName("Yacht as choice") public void yachtAsChoice() { Yacht yacht = new Yacht(new int[]{ 2, 2, 2, 2, 2 }, YachtCategory.CHOICE); assertThat(yacht.score()).isEqualTo(10); 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/zebra-puzzle/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/zebra-puzzle/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/zebra-puzzle/gradlew b/exercises/practice/zebra-puzzle/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/zebra-puzzle/gradlew +++ b/exercises/practice/zebra-puzzle/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/zebra-puzzle/gradlew.bat b/exercises/practice/zebra-puzzle/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/zebra-puzzle/gradlew.bat +++ b/exercises/practice/zebra-puzzle/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/zebra-puzzle/src/test/java/ZebraPuzzleTest.java b/exercises/practice/zebra-puzzle/src/test/java/ZebraPuzzleTest.java index 911ebae5c..b0bb118fe 100644 --- a/exercises/practice/zebra-puzzle/src/test/java/ZebraPuzzleTest.java +++ b/exercises/practice/zebra-puzzle/src/test/java/ZebraPuzzleTest.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 ZebraPuzzleTest { @Test + @DisplayName("resident who drinks water") public void residentWhoDrinksWater() { ZebraPuzzle zebraPuzzle = new ZebraPuzzle(); assertThat(zebraPuzzle.getWaterDrinker()).isEqualTo("Norwegian"); @@ -13,6 +15,7 @@ public void residentWhoDrinksWater() { @Disabled("Remove to run test") @Test + @DisplayName("resident who owns zebra") public void residentWhoOwnsZebra() { ZebraPuzzle zebraPuzzle = new ZebraPuzzle(); assertThat(zebraPuzzle.getZebraOwner()).isEqualTo("Japanese"); diff --git a/exercises/practice/zipper/.meta/config.json b/exercises/practice/zipper/.meta/config.json index 17e31b240..d5d633cc3 100644 --- a/exercises/practice/zipper/.meta/config.json +++ b/exercises/practice/zipper/.meta/config.json @@ -4,6 +4,7 @@ ], "contributors": [ "FridaTveit", + "jagdish-15", "jmrunkle", "lemoncurry", "msomji", diff --git a/exercises/practice/zipper/.meta/tests.toml b/exercises/practice/zipper/.meta/tests.toml index d7b089816..e93932b17 100644 --- a/exercises/practice/zipper/.meta/tests.toml +++ b/exercises/practice/zipper/.meta/tests.toml @@ -1,6 +1,13 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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. [771c652e-0754-4ef0-945c-0675d12ef1f5] description = "data is retained" @@ -20,6 +27,9 @@ description = "traversing up from top" [b8505f6a-aed4-4c2e-824f-a0ed8570d74b] description = "left, right, and up" +[b9aa8d54-07b7-4bfd-ab6b-7ff7f35930b6] +description = "test ability to descend multiple levels and return" + [47df1a27-b709-496e-b381-63a03b82ea5f] description = "set_value" 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/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.jar b/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.jar index e6441136f..f8e1ee312 100644 Binary files a/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.jar and b/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.jar differ diff --git a/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.properties b/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.properties index 2deab89d5..4d97ea344 100644 --- a/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.properties +++ b/exercises/practice/zipper/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/exercises/practice/zipper/gradlew b/exercises/practice/zipper/gradlew index 1aa94a426..adff685a0 100755 --- a/exercises/practice/zipper/gradlew +++ b/exercises/practice/zipper/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,7 @@ done # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # 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" ) @@ -203,15 +203,14 @@ fi 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, +# * DEFAULT_JVM_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 \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/exercises/practice/zipper/gradlew.bat b/exercises/practice/zipper/gradlew.bat index 25da30dbd..c4bdd3ab8 100644 --- a/exercises/practice/zipper/gradlew.bat +++ b/exercises/practice/zipper/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,10 @@ 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 %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/exercises/practice/zipper/src/test/java/ZipperTest.java b/exercises/practice/zipper/src/test/java/ZipperTest.java index d9f6fd505..d8797814f 100644 --- a/exercises/practice/zipper/src/test/java/ZipperTest.java +++ b/exercises/practice/zipper/src/test/java/ZipperTest.java @@ -1,6 +1,7 @@ +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 org.junit.jupiter.api.BeforeEach; import static org.assertj.core.api.Assertions.assertThat; @@ -21,12 +22,14 @@ public void setup() { } @Test + @DisplayName("data is retained") public void testToTree() { assertThat(zipper.toTree()).isEqualTo(binaryTree); } @Disabled("Remove to run test") @Test + @DisplayName("left, right and value") public void testLeftRightAndValue() { zipper = binaryTree.getRoot(); assertThat(zipper.left.right.getValue()).isEqualTo(3); @@ -34,6 +37,7 @@ public void testLeftRightAndValue() { @Disabled("Remove to run test") @Test + @DisplayName("dead end") public void testDeadEnd() { zipper = binaryTree.getRoot(); assertThat(zipper.left.left).isNull(); @@ -41,6 +45,7 @@ public void testDeadEnd() { @Disabled("Remove to run test") @Test + @DisplayName("tree from deep focus") public void testToTreeFromDeepFocus() { zipper = binaryTree.getRoot(); assertThat(zipper.left.right.toTree()).isEqualTo(binaryTree); @@ -48,6 +53,7 @@ public void testToTreeFromDeepFocus() { @Disabled("Remove to run test") @Test + @DisplayName("traversing up from top") public void testTraversingUpFromTop() { zipper = binaryTree.getRoot(); assertThat(zipper.up).isNull(); @@ -55,6 +61,7 @@ public void testTraversingUpFromTop() { @Disabled("Remove to run test") @Test + @DisplayName("left, right, and up") public void testLeftRightAndUp() { zipper = binaryTree.getRoot(); assertThat(zipper.left.up.right.up.left.right.getValue()).isEqualTo(3); @@ -62,6 +69,15 @@ public void testLeftRightAndUp() { @Disabled("Remove to run test") @Test + @DisplayName("test ability to descend multiple levels and return") + public void testAbilityToReturnAfterMultipleLevelDescend() { + zipper = binaryTree.getRoot(); + assertThat(zipper.left.right.up.up.getValue()).isEqualTo(1); + } + + @Disabled("Remove to run test") + @Test + @DisplayName("set_value") public void testSetValue() { zipper = binaryTree.getRoot(); zipper = zipper.left; @@ -84,6 +100,7 @@ public void testSetValue() { @Disabled("Remove to run test") @Test + @DisplayName("set_value after traversing up") public void testSetValueAfterTraversingUp() { zipper = binaryTree.getRoot(); zipper = zipper.left.right.up; @@ -106,6 +123,7 @@ public void testSetValueAfterTraversingUp() { @Disabled("Remove to run test") @Test + @DisplayName("set_left with leaf") public void testSetLeftWithLeaf() { zipper = binaryTree.getRoot(); zipper = zipper.left; @@ -131,6 +149,7 @@ public void testSetLeftWithLeaf() { @Disabled("Remove to run test") @Test + @DisplayName("set_right with null") public void testSetRightWithNull() { zipper = binaryTree.getRoot(); zipper = zipper.left; @@ -144,6 +163,7 @@ public void testSetRightWithNull() { @Disabled("Remove to run test") @Test + @DisplayName("set_right with subtree") public void testSetRightWithSubtree() { BinaryTree subtree = new BinaryTree(6); subtree.getRoot().setLeft(new Zipper(7)); @@ -174,6 +194,7 @@ public void testSetRightWithSubtree() { @Disabled("Remove to run test") @Test + @DisplayName("set_value on deep focus") public void testSetValueOnDeepFocus() { zipper = binaryTree.getRoot(); zipper = zipper.left.right; @@ -196,6 +217,7 @@ public void testSetValueOnDeepFocus() { @Disabled("Remove to run test") @Test + @DisplayName("different paths to same zipper") public void differentPathToSameZipper() { Zipper zipper1 = binaryTree.getRoot().left.up.right; Zipper zipper2 = binaryTree.getRoot().right; diff --git a/exercises/settings.gradle b/exercises/settings.gradle index c991919dc..1119f4b98 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -8,9 +8,10 @@ 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' include 'concept:karls-languages' include 'concept:lasagna' include 'concept:log-levels' @@ -57,9 +58,11 @@ include 'practice:difference-of-squares' // include 'practice:diffie-hellman' // deprecated include 'practice:dnd-character' include 'practice:dominoes' +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' @@ -74,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' @@ -91,7 +95,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' @@ -102,6 +106,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' @@ -112,10 +117,12 @@ include 'practice:proverb' include 'practice:pythagorean-triplet' include 'practice:queen-attack' include 'practice:rail-fence-cipher' +include 'practice:rate-limiter' 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' @@ -139,11 +146,13 @@ 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 include 'practice:sublist' include 'practice:sum-of-multiples' +include 'practice:swift-scheduling' include 'practice:tournament' include 'practice:transpose' include 'practice:tree-building' diff --git a/reference/contributing-to-practice-exercises.md b/reference/contributing-to-practice-exercises.md index 327c68635..e5fbb2c78 100644 --- a/reference/contributing-to-practice-exercises.md +++ b/reference/contributing-to-practice-exercises.md @@ -1,25 +1,34 @@ # 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`. - You can find details about what should be in that entry [here][docs-building-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. - Please add an entry for your exercise to `settings.gradle`. @@ -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 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 {