From 5a6b18582888ec9277ed4c661710353cb603d8cd Mon Sep 17 00:00:00 2001 From: BereKanters Date: Tue, 11 Jun 2024 13:22:39 +0200 Subject: [PATCH 01/15] ###feature/Integrate random matrix generation and merge Ubuntu and Windows workflows - Added a new CI workflow using vlsi/github-actions-random-matrix to generate a dynamic matrix for testing on both Ubuntu and Windows with JDK 8 and JDK 11. - Merged existing build steps for Ubuntu and Windows into a single workflow file (ci.yml). - Moved common steps, such as caching the Android jar and compiling, into a separate setup job to improve CI efficiency. - Updated README.md to reflect the new combined CI workflow badge. - Improved test coverage and reduced CI resource usage by leveraging the matrix strategy. --- .github/workflows/ci.yml | 115 +++++++++++++++++++++++++++++++++++++++ README.md | 3 +- 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..4785b82986 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,115 @@ +name: CI + +on: [ push, pull_request ] + +jobs: + # Job to generate the matrix + generate-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Generate matrix + id: set-matrix + uses: vlsi/github-actions-random-matrix@v1 + with: + include: | + [ + {"os": "ubuntu-latest", "java_version": "11"}, + {"os": "ubuntu-latest", "java_version": "17"}, + {"os": "windows-latest", "java_version": "11"}, + {"os": "windows-latest", "java_version": "17"} + ] + + # Setup job to run common steps + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache Android jar + id: android-cache + uses: actions/cache@v4 + with: + path: android-jar-cache + key: ${{ runner.os }}-android-version-26 + + - name: Download Android SDK's android.jar (or use cached file) + if: steps.android-cache.outputs.cache-hit != 'true' + run: | + mkdir android-jar-cache + cd android-jar-cache + curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" + echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 + if ! sha256sum -c android.sha256; then + echo >&2 "wrong sha256 for android.jar, expected:" + cat >&2 android.sha256 + echo >&2 "actual:" + sha256sum android.jar + exit 1; + fi + + # Main build and test job using the generated matrix + build-and-test: + needs: [generate-matrix, setup] + runs-on: ${{ matrix.os }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java_version }} + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: ${{ matrix.java_version }} + + - name: Setup Gradle + uses: gradle/gradle-build-action@v3.3.2 + + - name: Build using JDK ${{ matrix.java_version }} + run: ./gradlew build + + - name: Upload build code coverage + uses: codecov/codecov-action@v4.4.1 + if: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ matrix.os == 'windows-latest' && 'current_windows' || 'current' }} + env_vars: JAVA_VERSION + + - name: Check Atrium's -jvm.jar can be dexed + run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer + + - name: Composite build atrium-samples-test + run: ./gradlew build + working-directory: misc/tools/atrium-samples-test + + forwardCompatibility: + needs: setup + env: + JAVA_VERSION: 17 + KOTLIN_VERSION: ${{ matrix.kotlin_version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + kotlin_version: [ '1.5', '1.6', '1.7', '1.8', '1.9', '2.0' ] + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: 17 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v3.3.2 + + - name: Build using Kotlin ${{ matrix.kotlin_version }} + run: ./gradlew build diff --git a/README.md b/README.md index 476f74c9a8..4cf431630c 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ [![Download](https://img.shields.io/badge/Download-1.2.0-%23007ec6)](https://central.sonatype.com/artifact/ch.tutteli.atrium/atrium-fluent/1.2.0) [![EUPL](https://img.shields.io/badge/%E2%9A%96-EUPL%201.2-%230b45a6)](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12 "License") [![atrium @ kotlinlang.slack.com](https://img.shields.io/static/v1?label=kotlinlang&message=atrium&color=blue&logo=slack)](https://kotlinlang.slack.com/messages/atrium "See invitation link under section FAQ") -[![Build Status Ubuntu](https://github.com/robstoll/atrium/workflows/Ubuntu/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3AUbuntu+branch%3Amain) -[![Build Status Windows](https://github.com/robstoll/atrium/workflows/Windows/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3AWindows+branch%3Amain) +[![Build Status](https://github.com/robstoll/atrium/actions/workflows/ci.yml/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3Aci.yml+branch%3Amain) [![Coverage](https://codecov.io/gh/robstoll/atrium/branch/main/graph/badge.svg)](https://app.codecov.io/github/robstoll/atrium/branch/main) [![Newcomers Welcome](https://img.shields.io/badge/%F0%9F%91%8B-Newcomers%20Welcome-blueviolet)](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 "Ask in slack for help") From 076c1361f13a033027f670d2a2b78589a4906c46 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Wed, 12 Jun 2024 11:39:03 +0200 Subject: [PATCH 02/15] ###feature/Added feature-random-matrix-ci.patch file - Added a new patch file --- feature-random-matrix-ci.patch | Bin 0 -> 13280 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 feature-random-matrix-ci.patch diff --git a/feature-random-matrix-ci.patch b/feature-random-matrix-ci.patch new file mode 100644 index 0000000000000000000000000000000000000000..80aa9a0f1efcaa92cd92c2bcac3216d70206a0e4 GIT binary patch literal 13280 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 E18{2X+5i9m literal 0 HcmV?d00001 From 2f35e51a42118ae4959c73376ea3a19b69398840 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Fri, 14 Jun 2024 22:28:42 +0200 Subject: [PATCH 03/15] ###feat/Integrate custom random matrix generation and merge Ubuntu and Windows workflows updated based on feedback - Added custom matrix generation scripts (`matrix_builder.js` and `matrix.js`) to dynamically generate the matrix for CI jobs. - Configured the GitHub Actions workflow (`ci.yml`) to use the custom matrix generator. - Renamed the `setup` job to `dexer` and included the step to check if Atrium's -jvm.jar can be dexed. --- .github/workflows/ci.yml | 88 +++++------ .github/workflows/matrix.js | 29 ++++ .github/workflows/matrix_builder.js | 236 ++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+), 47 deletions(-) create mode 100644 .github/workflows/matrix.js create mode 100644 .github/workflows/matrix_builder.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4785b82986..0cee643fd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,8 @@ name: CI -on: [ push, pull_request ] +on: [push, pull_request] jobs: - # Job to generate the matrix generate-matrix: runs-on: ubuntu-latest outputs: @@ -12,50 +11,17 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Generate matrix - id: set-matrix - uses: vlsi/github-actions-random-matrix@v1 - with: - include: | - [ - {"os": "ubuntu-latest", "java_version": "11"}, - {"os": "ubuntu-latest", "java_version": "17"}, - {"os": "windows-latest", "java_version": "11"}, - {"os": "windows-latest", "java_version": "17"} - ] - - # Setup job to run common steps - setup: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Cache Android jar - id: android-cache - uses: actions/cache@v4 + - name: Set up Node.js + uses: actions/setup-node@v2 with: - path: android-jar-cache - key: ${{ runner.os }}-android-version-26 + node-version: '14' - - name: Download Android SDK's android.jar (or use cached file) - if: steps.android-cache.outputs.cache-hit != 'true' - run: | - mkdir android-jar-cache - cd android-jar-cache - curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" - echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 - if ! sha256sum -c android.sha256; then - echo >&2 "wrong sha256 for android.jar, expected:" - cat >&2 android.sha256 - echo >&2 "actual:" - sha256sum android.jar - exit 1; - fi + - name: Generate matrix + id: set-matrix + run: node .github/workflows/matrix.js - # Main build and test job using the generated matrix build-and-test: - needs: [generate-matrix, setup] + needs: generate-matrix runs-on: ${{ matrix.os }} strategy: matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} @@ -83,15 +49,43 @@ jobs: flags: ${{ matrix.os == 'windows-latest' && 'current_windows' || 'current' }} env_vars: JAVA_VERSION - - name: Check Atrium's -jvm.jar can be dexed - run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer - - name: Composite build atrium-samples-test run: ./gradlew build working-directory: misc/tools/atrium-samples-test + dexer: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache Android jar + id: android-cache + uses: actions/cache@v4 + with: + path: android-jar-cache + key: ${{ runner.os }}-android-version-26 + + - name: Download Android SDK's android.jar (or use cached file) + if: steps.android-cache.outputs.cache-hit != 'true' + run: | + mkdir android-jar-cache + cd android-jar-cache + curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" + echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 + if ! sha256sum -c android.sha256; then + echo >&2 "wrong sha256 for android.jar, expected:" + cat >&2 android.sha256 + echo >&2 "actual:" + sha256sum android.jar + exit 1; + fi + + - name: Check Atrium's -jvm.jar can be dexed + run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer + forwardCompatibility: - needs: setup + needs: dexer env: JAVA_VERSION: 17 KOTLIN_VERSION: ${{ matrix.kotlin_version }} @@ -99,7 +93,7 @@ jobs: strategy: fail-fast: false matrix: - kotlin_version: [ '1.5', '1.6', '1.7', '1.8', '1.9', '2.0' ] + kotlin_version: ['1.5', '1.6', '1.7', '1.8', '1.9', '2.0'] steps: - uses: actions/checkout@v4 - name: Set up JDK 17 diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js new file mode 100644 index 0000000000..21f414c3b9 --- /dev/null +++ b/.github/workflows/matrix.js @@ -0,0 +1,29 @@ +let { MatrixBuilder } = require('./matrix_builder'); +const matrix = new MatrixBuilder(); + +// Add axes for the matrix +matrix.addAxis({ + name: 'os', + title: x => x.replace('-latest', ''), + values: ['ubuntu-latest', 'windows-latest'] +}); +matrix.addAxis({ + name: 'java_version', + values: ['11', '17'] +}); + +// Ensure at least one windows and at least one linux job is present (macos is almost the same as linux) +matrix.generateRow({ os: 'windows-latest' }); +matrix.generateRow({ os: 'ubuntu-latest' }); + +// Generate more rows, no duplicates would be generated +const include = matrix.generateRows(process.env.MATRIX_JOBS || 5); +if (include.length === 0) { + throw new Error('Matrix list is empty'); +} +// Sort jobs by name, however, numeric parts are sorted appropriately +// For instance, 'windows 8' would come before 'windows 11' +include.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true })); + +console.log(include); +console.log('::set-output name=matrix::' + JSON.stringify({ include })); diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js new file mode 100644 index 0000000000..4cf2b8e5cb --- /dev/null +++ b/.github/workflows/matrix_builder.js @@ -0,0 +1,236 @@ +// License: Apache-2.0 +// Copyright Vladimir Sitnikov, 2021 +// See https://github.com/vlsi/github-actions-random-matrix + +class Axis { + constructor({name, title, values}) { + this.name = name; + this.title = title; + this.values = values; + // If all entries have same weight, the axis has uniform distribution + this.uniform = values.reduce((a, b) => a === (b.weight || 1) ? a : 0, values[0].weight || 1) !== 0 + this.totalWeight = this.uniform ? values.length : values.reduce((a, b) => a + (b.weight || 1), 0); + } + + static matches(row, filter) { + if (typeof filter === 'function') { + return filter(row); + } + if (Array.isArray(filter)) { + // e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}] + return filter.find(v => Axis.matches(row, v)); + } + if (typeof filter === 'object') { + // e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}} + for (const [key, value] of Object.entries(filter)) { + if (!row.hasOwnProperty(key) || !Axis.matches(row[key], value)) { + return false; + } + } + return true; + } + return row === filter; + } + + pickValue(filter) { + let values = this.values; + if (filter) { + values = values.filter(v => Axis.matches(v, filter)); + } + if (values.length === 0) { + const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); + throw Error(`No values produces for axis '${this.name}' from ${JSON.stringify(this.values)}, filter=${filterStr}`); + } + if (values.length === 1) { + return values[0]; + } + if (this.uniform) { + return values[Math.floor(Math.random() * values.length)]; + } + const totalWeight = !filter ? this.totalWeight : values.reduce((a, b) => a + (b.weight || 1), 0); + let weight = Math.random() * totalWeight; + for (let i = 0; i < values.length; i++) { + const value = values[i]; + weight -= value.weight || 1; + if (weight <= 0) { + return value; + } + } + return values[values.length - 1]; + } +} + +class MatrixBuilder { + constructor() { + this.axes = []; + this.axisByName = {}; + this.rows = []; + this.duplicates = {}; + this.excludes = []; + this.includes = []; + this.failOnUnsatisfiableFilters = false; + } + + /** + * Specifies include filter (all the generated rows would comply with all the include filters) + * @param filter + */ + include(filter) { + this.includes.push(filter); + } + + /** + * Specifies exclude filter (e.g. exclude a forbidden combination) + * @param filter + */ + exclude(filter) { + this.excludes.push(filter); + } + + addAxis({name, title, values}) { + const axis = new Axis({name, title, values}); + this.axes.push(axis); + this.axisByName[name] = axis; + return axis; + } + + setNamePattern(names) { + this.namePattern = names; + } + + /** + * Returns true if the row matches the include and exclude filters. + * @param row input row + * @returns {boolean} + */ + matches(row) { + return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) && + (this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f))); + } + + failOnUnsatisfiableFilters(value) { + this.failOnUnsatisfiableFilters = value; + } + + /** + * Adds a row that matches the given filter to the resulting matrix. + * filter values could be + * - literal values: filter={os: 'windows-latest'} + * - arrays: filter={os: ['windows-latest', 'linux-latest']} + * - functions: filter={os: x => x!='windows-latest'} + * @param filter object with keys matching axes names + * @returns {*} + */ + generateRow(filter) { + let res; + if (filter) { + // If matching row already exists, no need to generate more + res = this.rows.find(v => Axis.matches(v, filter)); + if (res) { + return res; + } + } + for (let i = 0; i < 142; i++) { + res = this.axes.reduce( + (prev, next) => + Object.assign(prev, { + [next.name]: next.pickValue(filter ? filter[next.name] : undefined) + }), + {} + ); + if (!this.matches(res)) { + continue; + } + const key = JSON.stringify(res); + if (!this.duplicates.hasOwnProperty(key)) { + this.duplicates[key] = true; + res.name = + this.namePattern.map(axisName => { + let value = res[axisName]; + const title = value.title; + if (typeof title != 'undefined') { + return title; + } + const computeTitle = this.axisByName[axisName].title; + return computeTitle ? computeTitle(value) : value; + }).filter(Boolean).join(", "); + this.rows.push(res); + return res; + } + } + const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); + const msg = `Unable to generate row for ${filterStr}. Please check include and exclude filters`; + if (this.failOnUnsatisfiableFilters) { + throw Error(msg); + } else { + console.warn(msg); + } + } + + generateRows(maxRows, filter) { + for (let i = 0; this.rows.length < maxRows && i < maxRows; i++) { + this.generateRow(filter); + } + return this.rows; + } + + /** + * Computes the number of all the possible combinations. + * @returns {{bad: number, good: number}} + */ + summary() { + let position = -1; + let indices = []; + let values = {}; + const axes = this.axes; + function resetValuesUpTo(nextPosition) { + for(let i=0; i Date: Sat, 15 Jun 2024 00:42:00 +0200 Subject: [PATCH 04/15] ###feat/feature extractors and test samples for SQLException - Introduced feature extractors for SQLException: - errorCode: Extracts the error code from an SQLException instance. - sqlState: Extracts the SQL state from an SQLException instance. - Added test samples to validate the new feature extractors: - errorCodeFeature: Tests the extraction and validation of the error code. - sqlStateFeature: Tests the extraction and validation of the SQL state. - errorCode: Tests grouped assertions for the error code. - sqlState: Tests grouped assertions for the SQL state. --- .../api/fluent/en_GB/SQLExceptionFeatures.kt | 16 ++++ .../samples/SQLExceptionFeaturesSamples.kt | 80 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt create mode 100644 apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt diff --git a/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt new file mode 100644 index 0000000000..6de92d5acf --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt @@ -0,0 +1,16 @@ +import ch.tutteli.atrium.api.fluent.en_GB.feature +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.creating.FeatureExpect +import java.sql.SQLException + +val Expect.errorCode: FeatureExpect + get() = feature("errorCode", SQLException::getErrorCode) + +fun Expect.errorCode(assertionCreator: Expect.() -> Unit) = + feature("errorCode", SQLException::getErrorCode, assertionCreator) + +val Expect.sqlState: FeatureExpect + get() = feature("sqlState", SQLException::getSQLState) + +fun Expect.sqlState(assertionCreator: Expect.() -> Unit) = + feature("sqlState", SQLException::getSQLState, assertionCreator) diff --git a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt new file mode 100644 index 0000000000..0beb2c2d91 --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt @@ -0,0 +1,80 @@ +package ch.tutteli.atrium.api.fluent.en_GB.samples + +import ch.tutteli.atrium.api.fluent.en_GB.* +import ch.tutteli.atrium.api.verbs.expect +import ch.tutteli.atrium.creating.Expect +import errorCode +import sqlState +import java.sql.SQLException +import kotlin.test.Test + +class SQLExceptionFeaturesSamples { + + @Test + fun errorCodeFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .errorCode // subject is now of type Int + .toEqual(1001) + + fails { + expect(exception) + .errorCode // subject is now of type Int + .toEqual(9999) // fails + .toBeGreaterThan(1000) // not evaluated/reported because `toEqual` already fails + // use `.errorCode { ... }` if you want all assertions evaluated + } + } + + @Test + fun sqlStateFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .sqlState // subject is now of type String + .toEqual("42000") + + fails { + expect(exception) + .sqlState // subject is now of type String + .toEqual("00000") // fails + .toContain("42") // not evaluated/reported because `toEqual` already fails + // use `.sqlState { ... }` if you want all assertions evaluated + } + } + + @Test + fun errorCode() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).errorCode { // subject within this expectation-group is of type Int + toBeGreaterThan(1000) + toEqual(1001) + } // subject here is back to type SQLException + + fails { + expect(exception).errorCode { + toEqual(9999) // fails + toBeGreaterThan(1000) // still evaluated even though `toEqual` already fails + } + } + } + + @Test + fun sqlState() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).sqlState { // subject within this expectation-group is of type String + toEqual("42000") + toContain("42") + } // subject here is back to type SQLException + + fails { + expect(exception).sqlState { + toEqual("00000") // fails + toContain("42") // still evaluated even though `toEqual` already fails + } + } + } +} From c491978cca3c46633a8410f9a0a022814d8de7d3 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Sun, 16 Jun 2024 01:33:44 +0200 Subject: [PATCH 05/15] ###feat/Add feature extractors and test samples for SQLException - Added KDoc comments with @since 1.3.0 and links to the sample file. - Changed file naming to `sqlExceptionFeatureExtractors` and `SQLExceptionFeaturesSamples` - Removed accidentally commited files - Aligned comments and ordered fun --- .github/workflows/ci.yml | 109 -------- .github/workflows/matrix.js | 29 --- .github/workflows/matrix_builder.js | 236 ------------------ README.md | 3 +- .../api/fluent/en_GB/SQLExceptionFeatures.kt | 16 -- .../en_GB/sqlExceptionFeatureExtractors.kt | 41 +++ .../SQLExceptionFeatureExtractorSamples.kt | 79 ++++++ .../samples/SQLExceptionFeaturesSamples.kt | 80 ------ feature-random-matrix-ci.patch | Bin 13280 -> 0 bytes 9 files changed, 122 insertions(+), 471 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/matrix.js delete mode 100644 .github/workflows/matrix_builder.js delete mode 100644 apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt create mode 100644 apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt create mode 100644 apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt delete mode 100644 apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt delete mode 100644 feature-random-matrix-ci.patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 0cee643fd3..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - generate-matrix: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: '14' - - - name: Generate matrix - id: set-matrix - run: node .github/workflows/matrix.js - - build-and-test: - needs: generate-matrix - runs-on: ${{ matrix.os }} - strategy: - matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up JDK ${{ matrix.java_version }} - uses: actions/setup-java@v4 - with: - distribution: 'adopt' - java-version: ${{ matrix.java_version }} - - - name: Setup Gradle - uses: gradle/gradle-build-action@v3.3.2 - - - name: Build using JDK ${{ matrix.java_version }} - run: ./gradlew build - - - name: Upload build code coverage - uses: codecov/codecov-action@v4.4.1 - if: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }} - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: ${{ matrix.os == 'windows-latest' && 'current_windows' || 'current' }} - env_vars: JAVA_VERSION - - - name: Composite build atrium-samples-test - run: ./gradlew build - working-directory: misc/tools/atrium-samples-test - - dexer: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Cache Android jar - id: android-cache - uses: actions/cache@v4 - with: - path: android-jar-cache - key: ${{ runner.os }}-android-version-26 - - - name: Download Android SDK's android.jar (or use cached file) - if: steps.android-cache.outputs.cache-hit != 'true' - run: | - mkdir android-jar-cache - cd android-jar-cache - curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" - echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 - if ! sha256sum -c android.sha256; then - echo >&2 "wrong sha256 for android.jar, expected:" - cat >&2 android.sha256 - echo >&2 "actual:" - sha256sum android.jar - exit 1; - fi - - - name: Check Atrium's -jvm.jar can be dexed - run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer - - forwardCompatibility: - needs: dexer - env: - JAVA_VERSION: 17 - KOTLIN_VERSION: ${{ matrix.kotlin_version }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - kotlin_version: ['1.5', '1.6', '1.7', '1.8', '1.9', '2.0'] - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'adopt' - java-version: 17 - - - name: Setup Gradle - uses: gradle/gradle-build-action@v3.3.2 - - - name: Build using Kotlin ${{ matrix.kotlin_version }} - run: ./gradlew build diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js deleted file mode 100644 index 21f414c3b9..0000000000 --- a/.github/workflows/matrix.js +++ /dev/null @@ -1,29 +0,0 @@ -let { MatrixBuilder } = require('./matrix_builder'); -const matrix = new MatrixBuilder(); - -// Add axes for the matrix -matrix.addAxis({ - name: 'os', - title: x => x.replace('-latest', ''), - values: ['ubuntu-latest', 'windows-latest'] -}); -matrix.addAxis({ - name: 'java_version', - values: ['11', '17'] -}); - -// Ensure at least one windows and at least one linux job is present (macos is almost the same as linux) -matrix.generateRow({ os: 'windows-latest' }); -matrix.generateRow({ os: 'ubuntu-latest' }); - -// Generate more rows, no duplicates would be generated -const include = matrix.generateRows(process.env.MATRIX_JOBS || 5); -if (include.length === 0) { - throw new Error('Matrix list is empty'); -} -// Sort jobs by name, however, numeric parts are sorted appropriately -// For instance, 'windows 8' would come before 'windows 11' -include.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true })); - -console.log(include); -console.log('::set-output name=matrix::' + JSON.stringify({ include })); diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js deleted file mode 100644 index 4cf2b8e5cb..0000000000 --- a/.github/workflows/matrix_builder.js +++ /dev/null @@ -1,236 +0,0 @@ -// License: Apache-2.0 -// Copyright Vladimir Sitnikov, 2021 -// See https://github.com/vlsi/github-actions-random-matrix - -class Axis { - constructor({name, title, values}) { - this.name = name; - this.title = title; - this.values = values; - // If all entries have same weight, the axis has uniform distribution - this.uniform = values.reduce((a, b) => a === (b.weight || 1) ? a : 0, values[0].weight || 1) !== 0 - this.totalWeight = this.uniform ? values.length : values.reduce((a, b) => a + (b.weight || 1), 0); - } - - static matches(row, filter) { - if (typeof filter === 'function') { - return filter(row); - } - if (Array.isArray(filter)) { - // e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}] - return filter.find(v => Axis.matches(row, v)); - } - if (typeof filter === 'object') { - // e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}} - for (const [key, value] of Object.entries(filter)) { - if (!row.hasOwnProperty(key) || !Axis.matches(row[key], value)) { - return false; - } - } - return true; - } - return row === filter; - } - - pickValue(filter) { - let values = this.values; - if (filter) { - values = values.filter(v => Axis.matches(v, filter)); - } - if (values.length === 0) { - const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); - throw Error(`No values produces for axis '${this.name}' from ${JSON.stringify(this.values)}, filter=${filterStr}`); - } - if (values.length === 1) { - return values[0]; - } - if (this.uniform) { - return values[Math.floor(Math.random() * values.length)]; - } - const totalWeight = !filter ? this.totalWeight : values.reduce((a, b) => a + (b.weight || 1), 0); - let weight = Math.random() * totalWeight; - for (let i = 0; i < values.length; i++) { - const value = values[i]; - weight -= value.weight || 1; - if (weight <= 0) { - return value; - } - } - return values[values.length - 1]; - } -} - -class MatrixBuilder { - constructor() { - this.axes = []; - this.axisByName = {}; - this.rows = []; - this.duplicates = {}; - this.excludes = []; - this.includes = []; - this.failOnUnsatisfiableFilters = false; - } - - /** - * Specifies include filter (all the generated rows would comply with all the include filters) - * @param filter - */ - include(filter) { - this.includes.push(filter); - } - - /** - * Specifies exclude filter (e.g. exclude a forbidden combination) - * @param filter - */ - exclude(filter) { - this.excludes.push(filter); - } - - addAxis({name, title, values}) { - const axis = new Axis({name, title, values}); - this.axes.push(axis); - this.axisByName[name] = axis; - return axis; - } - - setNamePattern(names) { - this.namePattern = names; - } - - /** - * Returns true if the row matches the include and exclude filters. - * @param row input row - * @returns {boolean} - */ - matches(row) { - return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) && - (this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f))); - } - - failOnUnsatisfiableFilters(value) { - this.failOnUnsatisfiableFilters = value; - } - - /** - * Adds a row that matches the given filter to the resulting matrix. - * filter values could be - * - literal values: filter={os: 'windows-latest'} - * - arrays: filter={os: ['windows-latest', 'linux-latest']} - * - functions: filter={os: x => x!='windows-latest'} - * @param filter object with keys matching axes names - * @returns {*} - */ - generateRow(filter) { - let res; - if (filter) { - // If matching row already exists, no need to generate more - res = this.rows.find(v => Axis.matches(v, filter)); - if (res) { - return res; - } - } - for (let i = 0; i < 142; i++) { - res = this.axes.reduce( - (prev, next) => - Object.assign(prev, { - [next.name]: next.pickValue(filter ? filter[next.name] : undefined) - }), - {} - ); - if (!this.matches(res)) { - continue; - } - const key = JSON.stringify(res); - if (!this.duplicates.hasOwnProperty(key)) { - this.duplicates[key] = true; - res.name = - this.namePattern.map(axisName => { - let value = res[axisName]; - const title = value.title; - if (typeof title != 'undefined') { - return title; - } - const computeTitle = this.axisByName[axisName].title; - return computeTitle ? computeTitle(value) : value; - }).filter(Boolean).join(", "); - this.rows.push(res); - return res; - } - } - const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); - const msg = `Unable to generate row for ${filterStr}. Please check include and exclude filters`; - if (this.failOnUnsatisfiableFilters) { - throw Error(msg); - } else { - console.warn(msg); - } - } - - generateRows(maxRows, filter) { - for (let i = 0; this.rows.length < maxRows && i < maxRows; i++) { - this.generateRow(filter); - } - return this.rows; - } - - /** - * Computes the number of all the possible combinations. - * @returns {{bad: number, good: number}} - */ - summary() { - let position = -1; - let indices = []; - let values = {}; - const axes = this.axes; - function resetValuesUpTo(nextPosition) { - for(let i=0; i.errorCode: FeatureExpect - get() = feature("errorCode", SQLException::getErrorCode) - -fun Expect.errorCode(assertionCreator: Expect.() -> Unit) = - feature("errorCode", SQLException::getErrorCode, assertionCreator) - -val Expect.sqlState: FeatureExpect - get() = feature("sqlState", SQLException::getSQLState) - -fun Expect.sqlState(assertionCreator: Expect.() -> Unit) = - feature("sqlState", SQLException::getSQLState, assertionCreator) diff --git a/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt new file mode 100644 index 0000000000..da2c3214ed --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt @@ -0,0 +1,41 @@ +package ch.tutteli.atrium.api.fluent.en_GB + +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.creating.FeatureExpect +import java.sql.SQLException + +/** + * Extracts the [errorCode][SQLException.getErrorCode] property from the subject of the assertion. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.errorCodeFeature + */ +val Expect.errorCode: FeatureExpect + get() = feature("errorCode", SQLException::getErrorCode) + +/** + * Extracts the [errorCode][SQLException.getErrorCode] property from the subject of the assertion and makes it the new subject. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.sqlStateFeature + */ +fun Expect.errorCode(assertionCreator: Expect.() -> Unit) = + feature("errorCode", SQLException::getErrorCode, assertionCreator) + +/** + * Extracts the [sqlState][SQLException.getSQLState] property from the subject of the assertion. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.errorCode + */ +val Expect.sqlState: FeatureExpect + get() = feature("sqlState", SQLException::getSQLState) + +/** + * Extracts the [sqlState][SQLException.getSQLState] property from the subject of the assertion and makes it the new subject. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.sqlState + */ +fun Expect.sqlState(assertionCreator: Expect.() -> Unit) = + feature("sqlState", SQLException::getSQLState, assertionCreator) diff --git a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt new file mode 100644 index 0000000000..658dc042cb --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt @@ -0,0 +1,79 @@ +package ch.tutteli.atrium.api.fluent.en_GB.samples + +import ch.tutteli.atrium.api.fluent.en_GB.* +import ch.tutteli.atrium.api.verbs.expect +import java.sql.SQLException +import kotlin.test.Test + +class SQLExceptionFeaturesSamples { + + @Test + fun errorCodeFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .errorCode // subject is now of type Int + .toEqual(1001) + + fails { + expect(exception) + .errorCode // subject is now of type Int + .toEqual(9999) // fails + .toBeGreaterThan(1000) // not evaluated/reported because `toEqual` already fails + // use `.errorCode { ... }` if you want all assertions evaluated + } + } + + @Test + fun errorCode() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).errorCode { // subject within this expectation-group is of type Int + toBeGreaterThan(1000) + toEqual(1001) + } // subject here is back to type SQLException + + fails { + expect(exception).errorCode { + toEqual(9999) // fails + toBeGreaterThan(1000) // still evaluated even though `toEqual` already fails + } + } + } + + @Test + fun sqlStateFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .sqlState // subject is now of type String + .toEqual("42000") + + fails { + expect(exception) + .sqlState // subject is now of type String + .toEqual("00000") // fails + .toContain("42") // not evaluated/reported because `toEqual` already fails + // use `.sqlState { ... }` if you want all assertions evaluated + } + } + + + + @Test + fun sqlState() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).sqlState { // subject within this expectation-group is of type String + toEqual("42000") + toContain("42") + } // subject here is back to type SQLException + + fails { + expect(exception).sqlState { + toEqual("00000") // fails + toContain("42") // still evaluated even though `toEqual` already fails + } + } + } +} diff --git a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt deleted file mode 100644 index 0beb2c2d91..0000000000 --- a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt +++ /dev/null @@ -1,80 +0,0 @@ -package ch.tutteli.atrium.api.fluent.en_GB.samples - -import ch.tutteli.atrium.api.fluent.en_GB.* -import ch.tutteli.atrium.api.verbs.expect -import ch.tutteli.atrium.creating.Expect -import errorCode -import sqlState -import java.sql.SQLException -import kotlin.test.Test - -class SQLExceptionFeaturesSamples { - - @Test - fun errorCodeFeature() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception) - .errorCode // subject is now of type Int - .toEqual(1001) - - fails { - expect(exception) - .errorCode // subject is now of type Int - .toEqual(9999) // fails - .toBeGreaterThan(1000) // not evaluated/reported because `toEqual` already fails - // use `.errorCode { ... }` if you want all assertions evaluated - } - } - - @Test - fun sqlStateFeature() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception) - .sqlState // subject is now of type String - .toEqual("42000") - - fails { - expect(exception) - .sqlState // subject is now of type String - .toEqual("00000") // fails - .toContain("42") // not evaluated/reported because `toEqual` already fails - // use `.sqlState { ... }` if you want all assertions evaluated - } - } - - @Test - fun errorCode() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception).errorCode { // subject within this expectation-group is of type Int - toBeGreaterThan(1000) - toEqual(1001) - } // subject here is back to type SQLException - - fails { - expect(exception).errorCode { - toEqual(9999) // fails - toBeGreaterThan(1000) // still evaluated even though `toEqual` already fails - } - } - } - - @Test - fun sqlState() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception).sqlState { // subject within this expectation-group is of type String - toEqual("42000") - toContain("42") - } // subject here is back to type SQLException - - fails { - expect(exception).sqlState { - toEqual("00000") // fails - toContain("42") // still evaluated even though `toEqual` already fails - } - } - } -} diff --git a/feature-random-matrix-ci.patch b/feature-random-matrix-ci.patch deleted file mode 100644 index 80aa9a0f1efcaa92cd92c2bcac3216d70206a0e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13280 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 E18{2X+5i9m From 99ec8e8ab738f42791642b07c84e348a7dc3caa2 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Tue, 11 Jun 2024 13:22:39 +0200 Subject: [PATCH 06/15] ###feature/Integrate random matrix generation and merge Ubuntu and Windows workflows - Added a new CI workflow using vlsi/github-actions-random-matrix to generate a dynamic matrix for testing on both Ubuntu and Windows with JDK 8 and JDK 11. - Merged existing build steps for Ubuntu and Windows into a single workflow file (ci.yml). - Moved common steps, such as caching the Android jar and compiling, into a separate setup job to improve CI efficiency. - Updated README.md to reflect the new combined CI workflow badge. - Improved test coverage and reduced CI resource usage by leveraging the matrix strategy. --- .github/workflows/ci.yml | 115 +++++++++++++++++++++++++++++++++++++++ README.md | 3 +- 2 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..4785b82986 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,115 @@ +name: CI + +on: [ push, pull_request ] + +jobs: + # Job to generate the matrix + generate-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Generate matrix + id: set-matrix + uses: vlsi/github-actions-random-matrix@v1 + with: + include: | + [ + {"os": "ubuntu-latest", "java_version": "11"}, + {"os": "ubuntu-latest", "java_version": "17"}, + {"os": "windows-latest", "java_version": "11"}, + {"os": "windows-latest", "java_version": "17"} + ] + + # Setup job to run common steps + setup: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache Android jar + id: android-cache + uses: actions/cache@v4 + with: + path: android-jar-cache + key: ${{ runner.os }}-android-version-26 + + - name: Download Android SDK's android.jar (or use cached file) + if: steps.android-cache.outputs.cache-hit != 'true' + run: | + mkdir android-jar-cache + cd android-jar-cache + curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" + echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 + if ! sha256sum -c android.sha256; then + echo >&2 "wrong sha256 for android.jar, expected:" + cat >&2 android.sha256 + echo >&2 "actual:" + sha256sum android.jar + exit 1; + fi + + # Main build and test job using the generated matrix + build-and-test: + needs: [generate-matrix, setup] + runs-on: ${{ matrix.os }} + strategy: + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java_version }} + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: ${{ matrix.java_version }} + + - name: Setup Gradle + uses: gradle/gradle-build-action@v3.3.2 + + - name: Build using JDK ${{ matrix.java_version }} + run: ./gradlew build + + - name: Upload build code coverage + uses: codecov/codecov-action@v4.4.1 + if: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }} + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ matrix.os == 'windows-latest' && 'current_windows' || 'current' }} + env_vars: JAVA_VERSION + + - name: Check Atrium's -jvm.jar can be dexed + run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer + + - name: Composite build atrium-samples-test + run: ./gradlew build + working-directory: misc/tools/atrium-samples-test + + forwardCompatibility: + needs: setup + env: + JAVA_VERSION: 17 + KOTLIN_VERSION: ${{ matrix.kotlin_version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + kotlin_version: [ '1.5', '1.6', '1.7', '1.8', '1.9', '2.0' ] + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: 17 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v3.3.2 + + - name: Build using Kotlin ${{ matrix.kotlin_version }} + run: ./gradlew build diff --git a/README.md b/README.md index 476f74c9a8..4cf431630c 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ [![Download](https://img.shields.io/badge/Download-1.2.0-%23007ec6)](https://central.sonatype.com/artifact/ch.tutteli.atrium/atrium-fluent/1.2.0) [![EUPL](https://img.shields.io/badge/%E2%9A%96-EUPL%201.2-%230b45a6)](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12 "License") [![atrium @ kotlinlang.slack.com](https://img.shields.io/static/v1?label=kotlinlang&message=atrium&color=blue&logo=slack)](https://kotlinlang.slack.com/messages/atrium "See invitation link under section FAQ") -[![Build Status Ubuntu](https://github.com/robstoll/atrium/workflows/Ubuntu/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3AUbuntu+branch%3Amain) -[![Build Status Windows](https://github.com/robstoll/atrium/workflows/Windows/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3AWindows+branch%3Amain) +[![Build Status](https://github.com/robstoll/atrium/actions/workflows/ci.yml/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3Aci.yml+branch%3Amain) [![Coverage](https://codecov.io/gh/robstoll/atrium/branch/main/graph/badge.svg)](https://app.codecov.io/github/robstoll/atrium/branch/main) [![Newcomers Welcome](https://img.shields.io/badge/%F0%9F%91%8B-Newcomers%20Welcome-blueviolet)](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 "Ask in slack for help") From fcdcf8c9ed07f7cd4a231d65f89efe3ba82ac1e1 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Wed, 12 Jun 2024 11:39:03 +0200 Subject: [PATCH 07/15] ###feature/Added feature-random-matrix-ci.patch file - Added a new patch file --- feature-random-matrix-ci.patch | Bin 0 -> 13280 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 feature-random-matrix-ci.patch diff --git a/feature-random-matrix-ci.patch b/feature-random-matrix-ci.patch new file mode 100644 index 0000000000000000000000000000000000000000..80aa9a0f1efcaa92cd92c2bcac3216d70206a0e4 GIT binary patch literal 13280 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 E18{2X+5i9m literal 0 HcmV?d00001 From d021ec60b31f9ccbb41407ad3b4f50cd5801f76c Mon Sep 17 00:00:00 2001 From: BereKanters Date: Fri, 14 Jun 2024 22:28:42 +0200 Subject: [PATCH 08/15] ###feat/Integrate custom random matrix generation and merge Ubuntu and Windows workflows updated based on feedback - Added custom matrix generation scripts (`matrix_builder.js` and `matrix.js`) to dynamically generate the matrix for CI jobs. - Configured the GitHub Actions workflow (`ci.yml`) to use the custom matrix generator. - Renamed the `setup` job to `dexer` and included the step to check if Atrium's -jvm.jar can be dexed. --- .github/workflows/ci.yml | 88 +++++------ .github/workflows/matrix.js | 29 ++++ .github/workflows/matrix_builder.js | 236 ++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+), 47 deletions(-) create mode 100644 .github/workflows/matrix.js create mode 100644 .github/workflows/matrix_builder.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4785b82986..0cee643fd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,8 @@ name: CI -on: [ push, pull_request ] +on: [push, pull_request] jobs: - # Job to generate the matrix generate-matrix: runs-on: ubuntu-latest outputs: @@ -12,50 +11,17 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Generate matrix - id: set-matrix - uses: vlsi/github-actions-random-matrix@v1 - with: - include: | - [ - {"os": "ubuntu-latest", "java_version": "11"}, - {"os": "ubuntu-latest", "java_version": "17"}, - {"os": "windows-latest", "java_version": "11"}, - {"os": "windows-latest", "java_version": "17"} - ] - - # Setup job to run common steps - setup: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Cache Android jar - id: android-cache - uses: actions/cache@v4 + - name: Set up Node.js + uses: actions/setup-node@v2 with: - path: android-jar-cache - key: ${{ runner.os }}-android-version-26 + node-version: '14' - - name: Download Android SDK's android.jar (or use cached file) - if: steps.android-cache.outputs.cache-hit != 'true' - run: | - mkdir android-jar-cache - cd android-jar-cache - curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" - echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 - if ! sha256sum -c android.sha256; then - echo >&2 "wrong sha256 for android.jar, expected:" - cat >&2 android.sha256 - echo >&2 "actual:" - sha256sum android.jar - exit 1; - fi + - name: Generate matrix + id: set-matrix + run: node .github/workflows/matrix.js - # Main build and test job using the generated matrix build-and-test: - needs: [generate-matrix, setup] + needs: generate-matrix runs-on: ${{ matrix.os }} strategy: matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} @@ -83,15 +49,43 @@ jobs: flags: ${{ matrix.os == 'windows-latest' && 'current_windows' || 'current' }} env_vars: JAVA_VERSION - - name: Check Atrium's -jvm.jar can be dexed - run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer - - name: Composite build atrium-samples-test run: ./gradlew build working-directory: misc/tools/atrium-samples-test + dexer: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Cache Android jar + id: android-cache + uses: actions/cache@v4 + with: + path: android-jar-cache + key: ${{ runner.os }}-android-version-26 + + - name: Download Android SDK's android.jar (or use cached file) + if: steps.android-cache.outputs.cache-hit != 'true' + run: | + mkdir android-jar-cache + cd android-jar-cache + curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" + echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 + if ! sha256sum -c android.sha256; then + echo >&2 "wrong sha256 for android.jar, expected:" + cat >&2 android.sha256 + echo >&2 "actual:" + sha256sum android.jar + exit 1; + fi + + - name: Check Atrium's -jvm.jar can be dexed + run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer + forwardCompatibility: - needs: setup + needs: dexer env: JAVA_VERSION: 17 KOTLIN_VERSION: ${{ matrix.kotlin_version }} @@ -99,7 +93,7 @@ jobs: strategy: fail-fast: false matrix: - kotlin_version: [ '1.5', '1.6', '1.7', '1.8', '1.9', '2.0' ] + kotlin_version: ['1.5', '1.6', '1.7', '1.8', '1.9', '2.0'] steps: - uses: actions/checkout@v4 - name: Set up JDK 17 diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js new file mode 100644 index 0000000000..21f414c3b9 --- /dev/null +++ b/.github/workflows/matrix.js @@ -0,0 +1,29 @@ +let { MatrixBuilder } = require('./matrix_builder'); +const matrix = new MatrixBuilder(); + +// Add axes for the matrix +matrix.addAxis({ + name: 'os', + title: x => x.replace('-latest', ''), + values: ['ubuntu-latest', 'windows-latest'] +}); +matrix.addAxis({ + name: 'java_version', + values: ['11', '17'] +}); + +// Ensure at least one windows and at least one linux job is present (macos is almost the same as linux) +matrix.generateRow({ os: 'windows-latest' }); +matrix.generateRow({ os: 'ubuntu-latest' }); + +// Generate more rows, no duplicates would be generated +const include = matrix.generateRows(process.env.MATRIX_JOBS || 5); +if (include.length === 0) { + throw new Error('Matrix list is empty'); +} +// Sort jobs by name, however, numeric parts are sorted appropriately +// For instance, 'windows 8' would come before 'windows 11' +include.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true })); + +console.log(include); +console.log('::set-output name=matrix::' + JSON.stringify({ include })); diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js new file mode 100644 index 0000000000..4cf2b8e5cb --- /dev/null +++ b/.github/workflows/matrix_builder.js @@ -0,0 +1,236 @@ +// License: Apache-2.0 +// Copyright Vladimir Sitnikov, 2021 +// See https://github.com/vlsi/github-actions-random-matrix + +class Axis { + constructor({name, title, values}) { + this.name = name; + this.title = title; + this.values = values; + // If all entries have same weight, the axis has uniform distribution + this.uniform = values.reduce((a, b) => a === (b.weight || 1) ? a : 0, values[0].weight || 1) !== 0 + this.totalWeight = this.uniform ? values.length : values.reduce((a, b) => a + (b.weight || 1), 0); + } + + static matches(row, filter) { + if (typeof filter === 'function') { + return filter(row); + } + if (Array.isArray(filter)) { + // e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}] + return filter.find(v => Axis.matches(row, v)); + } + if (typeof filter === 'object') { + // e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}} + for (const [key, value] of Object.entries(filter)) { + if (!row.hasOwnProperty(key) || !Axis.matches(row[key], value)) { + return false; + } + } + return true; + } + return row === filter; + } + + pickValue(filter) { + let values = this.values; + if (filter) { + values = values.filter(v => Axis.matches(v, filter)); + } + if (values.length === 0) { + const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); + throw Error(`No values produces for axis '${this.name}' from ${JSON.stringify(this.values)}, filter=${filterStr}`); + } + if (values.length === 1) { + return values[0]; + } + if (this.uniform) { + return values[Math.floor(Math.random() * values.length)]; + } + const totalWeight = !filter ? this.totalWeight : values.reduce((a, b) => a + (b.weight || 1), 0); + let weight = Math.random() * totalWeight; + for (let i = 0; i < values.length; i++) { + const value = values[i]; + weight -= value.weight || 1; + if (weight <= 0) { + return value; + } + } + return values[values.length - 1]; + } +} + +class MatrixBuilder { + constructor() { + this.axes = []; + this.axisByName = {}; + this.rows = []; + this.duplicates = {}; + this.excludes = []; + this.includes = []; + this.failOnUnsatisfiableFilters = false; + } + + /** + * Specifies include filter (all the generated rows would comply with all the include filters) + * @param filter + */ + include(filter) { + this.includes.push(filter); + } + + /** + * Specifies exclude filter (e.g. exclude a forbidden combination) + * @param filter + */ + exclude(filter) { + this.excludes.push(filter); + } + + addAxis({name, title, values}) { + const axis = new Axis({name, title, values}); + this.axes.push(axis); + this.axisByName[name] = axis; + return axis; + } + + setNamePattern(names) { + this.namePattern = names; + } + + /** + * Returns true if the row matches the include and exclude filters. + * @param row input row + * @returns {boolean} + */ + matches(row) { + return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) && + (this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f))); + } + + failOnUnsatisfiableFilters(value) { + this.failOnUnsatisfiableFilters = value; + } + + /** + * Adds a row that matches the given filter to the resulting matrix. + * filter values could be + * - literal values: filter={os: 'windows-latest'} + * - arrays: filter={os: ['windows-latest', 'linux-latest']} + * - functions: filter={os: x => x!='windows-latest'} + * @param filter object with keys matching axes names + * @returns {*} + */ + generateRow(filter) { + let res; + if (filter) { + // If matching row already exists, no need to generate more + res = this.rows.find(v => Axis.matches(v, filter)); + if (res) { + return res; + } + } + for (let i = 0; i < 142; i++) { + res = this.axes.reduce( + (prev, next) => + Object.assign(prev, { + [next.name]: next.pickValue(filter ? filter[next.name] : undefined) + }), + {} + ); + if (!this.matches(res)) { + continue; + } + const key = JSON.stringify(res); + if (!this.duplicates.hasOwnProperty(key)) { + this.duplicates[key] = true; + res.name = + this.namePattern.map(axisName => { + let value = res[axisName]; + const title = value.title; + if (typeof title != 'undefined') { + return title; + } + const computeTitle = this.axisByName[axisName].title; + return computeTitle ? computeTitle(value) : value; + }).filter(Boolean).join(", "); + this.rows.push(res); + return res; + } + } + const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); + const msg = `Unable to generate row for ${filterStr}. Please check include and exclude filters`; + if (this.failOnUnsatisfiableFilters) { + throw Error(msg); + } else { + console.warn(msg); + } + } + + generateRows(maxRows, filter) { + for (let i = 0; this.rows.length < maxRows && i < maxRows; i++) { + this.generateRow(filter); + } + return this.rows; + } + + /** + * Computes the number of all the possible combinations. + * @returns {{bad: number, good: number}} + */ + summary() { + let position = -1; + let indices = []; + let values = {}; + const axes = this.axes; + function resetValuesUpTo(nextPosition) { + for(let i=0; i Date: Sat, 15 Jun 2024 00:42:00 +0200 Subject: [PATCH 09/15] ###feat/feature extractors and test samples for SQLException - Introduced feature extractors for SQLException: - errorCode: Extracts the error code from an SQLException instance. - sqlState: Extracts the SQL state from an SQLException instance. - Added test samples to validate the new feature extractors: - errorCodeFeature: Tests the extraction and validation of the error code. - sqlStateFeature: Tests the extraction and validation of the SQL state. - errorCode: Tests grouped assertions for the error code. - sqlState: Tests grouped assertions for the SQL state. --- .../api/fluent/en_GB/SQLExceptionFeatures.kt | 16 ++++ .../samples/SQLExceptionFeaturesSamples.kt | 80 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt create mode 100644 apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt diff --git a/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt new file mode 100644 index 0000000000..6de92d5acf --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt @@ -0,0 +1,16 @@ +import ch.tutteli.atrium.api.fluent.en_GB.feature +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.creating.FeatureExpect +import java.sql.SQLException + +val Expect.errorCode: FeatureExpect + get() = feature("errorCode", SQLException::getErrorCode) + +fun Expect.errorCode(assertionCreator: Expect.() -> Unit) = + feature("errorCode", SQLException::getErrorCode, assertionCreator) + +val Expect.sqlState: FeatureExpect + get() = feature("sqlState", SQLException::getSQLState) + +fun Expect.sqlState(assertionCreator: Expect.() -> Unit) = + feature("sqlState", SQLException::getSQLState, assertionCreator) diff --git a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt new file mode 100644 index 0000000000..0beb2c2d91 --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt @@ -0,0 +1,80 @@ +package ch.tutteli.atrium.api.fluent.en_GB.samples + +import ch.tutteli.atrium.api.fluent.en_GB.* +import ch.tutteli.atrium.api.verbs.expect +import ch.tutteli.atrium.creating.Expect +import errorCode +import sqlState +import java.sql.SQLException +import kotlin.test.Test + +class SQLExceptionFeaturesSamples { + + @Test + fun errorCodeFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .errorCode // subject is now of type Int + .toEqual(1001) + + fails { + expect(exception) + .errorCode // subject is now of type Int + .toEqual(9999) // fails + .toBeGreaterThan(1000) // not evaluated/reported because `toEqual` already fails + // use `.errorCode { ... }` if you want all assertions evaluated + } + } + + @Test + fun sqlStateFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .sqlState // subject is now of type String + .toEqual("42000") + + fails { + expect(exception) + .sqlState // subject is now of type String + .toEqual("00000") // fails + .toContain("42") // not evaluated/reported because `toEqual` already fails + // use `.sqlState { ... }` if you want all assertions evaluated + } + } + + @Test + fun errorCode() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).errorCode { // subject within this expectation-group is of type Int + toBeGreaterThan(1000) + toEqual(1001) + } // subject here is back to type SQLException + + fails { + expect(exception).errorCode { + toEqual(9999) // fails + toBeGreaterThan(1000) // still evaluated even though `toEqual` already fails + } + } + } + + @Test + fun sqlState() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).sqlState { // subject within this expectation-group is of type String + toEqual("42000") + toContain("42") + } // subject here is back to type SQLException + + fails { + expect(exception).sqlState { + toEqual("00000") // fails + toContain("42") // still evaluated even though `toEqual` already fails + } + } + } +} From 016ac330c2c195dc6ce1ba8aaff9cc3b456ea967 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Sun, 16 Jun 2024 01:33:44 +0200 Subject: [PATCH 10/15] ###feat/Add feature extractors and test samples for SQLException - Added KDoc comments with @since 1.3.0 and links to the sample file. - Changed file naming to `sqlExceptionFeatureExtractors` and `SQLExceptionFeaturesSamples` - Removed accidentally commited files - Aligned comments and ordered fun --- .github/workflows/ci.yml | 109 -------- .github/workflows/matrix.js | 29 --- .github/workflows/matrix_builder.js | 236 ------------------ README.md | 3 +- .../api/fluent/en_GB/SQLExceptionFeatures.kt | 16 -- .../en_GB/sqlExceptionFeatureExtractors.kt | 41 +++ .../SQLExceptionFeatureExtractorSamples.kt | 79 ++++++ .../samples/SQLExceptionFeaturesSamples.kt | 80 ------ feature-random-matrix-ci.patch | Bin 13280 -> 0 bytes 9 files changed, 122 insertions(+), 471 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/matrix.js delete mode 100644 .github/workflows/matrix_builder.js delete mode 100644 apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/SQLExceptionFeatures.kt create mode 100644 apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt create mode 100644 apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt delete mode 100644 apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt delete mode 100644 feature-random-matrix-ci.patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 0cee643fd3..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - generate-matrix: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: '14' - - - name: Generate matrix - id: set-matrix - run: node .github/workflows/matrix.js - - build-and-test: - needs: generate-matrix - runs-on: ${{ matrix.os }} - strategy: - matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up JDK ${{ matrix.java_version }} - uses: actions/setup-java@v4 - with: - distribution: 'adopt' - java-version: ${{ matrix.java_version }} - - - name: Setup Gradle - uses: gradle/gradle-build-action@v3.3.2 - - - name: Build using JDK ${{ matrix.java_version }} - run: ./gradlew build - - - name: Upload build code coverage - uses: codecov/codecov-action@v4.4.1 - if: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }} - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: ${{ matrix.os == 'windows-latest' && 'current_windows' || 'current' }} - env_vars: JAVA_VERSION - - - name: Composite build atrium-samples-test - run: ./gradlew build - working-directory: misc/tools/atrium-samples-test - - dexer: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Cache Android jar - id: android-cache - uses: actions/cache@v4 - with: - path: android-jar-cache - key: ${{ runner.os }}-android-version-26 - - - name: Download Android SDK's android.jar (or use cached file) - if: steps.android-cache.outputs.cache-hit != 'true' - run: | - mkdir android-jar-cache - cd android-jar-cache - curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" - echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 - if ! sha256sum -c android.sha256; then - echo >&2 "wrong sha256 for android.jar, expected:" - cat >&2 android.sha256 - echo >&2 "actual:" - sha256sum android.jar - exit 1; - fi - - - name: Check Atrium's -jvm.jar can be dexed - run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer - - forwardCompatibility: - needs: dexer - env: - JAVA_VERSION: 17 - KOTLIN_VERSION: ${{ matrix.kotlin_version }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - kotlin_version: ['1.5', '1.6', '1.7', '1.8', '1.9', '2.0'] - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'adopt' - java-version: 17 - - - name: Setup Gradle - uses: gradle/gradle-build-action@v3.3.2 - - - name: Build using Kotlin ${{ matrix.kotlin_version }} - run: ./gradlew build diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js deleted file mode 100644 index 21f414c3b9..0000000000 --- a/.github/workflows/matrix.js +++ /dev/null @@ -1,29 +0,0 @@ -let { MatrixBuilder } = require('./matrix_builder'); -const matrix = new MatrixBuilder(); - -// Add axes for the matrix -matrix.addAxis({ - name: 'os', - title: x => x.replace('-latest', ''), - values: ['ubuntu-latest', 'windows-latest'] -}); -matrix.addAxis({ - name: 'java_version', - values: ['11', '17'] -}); - -// Ensure at least one windows and at least one linux job is present (macos is almost the same as linux) -matrix.generateRow({ os: 'windows-latest' }); -matrix.generateRow({ os: 'ubuntu-latest' }); - -// Generate more rows, no duplicates would be generated -const include = matrix.generateRows(process.env.MATRIX_JOBS || 5); -if (include.length === 0) { - throw new Error('Matrix list is empty'); -} -// Sort jobs by name, however, numeric parts are sorted appropriately -// For instance, 'windows 8' would come before 'windows 11' -include.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true })); - -console.log(include); -console.log('::set-output name=matrix::' + JSON.stringify({ include })); diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js deleted file mode 100644 index 4cf2b8e5cb..0000000000 --- a/.github/workflows/matrix_builder.js +++ /dev/null @@ -1,236 +0,0 @@ -// License: Apache-2.0 -// Copyright Vladimir Sitnikov, 2021 -// See https://github.com/vlsi/github-actions-random-matrix - -class Axis { - constructor({name, title, values}) { - this.name = name; - this.title = title; - this.values = values; - // If all entries have same weight, the axis has uniform distribution - this.uniform = values.reduce((a, b) => a === (b.weight || 1) ? a : 0, values[0].weight || 1) !== 0 - this.totalWeight = this.uniform ? values.length : values.reduce((a, b) => a + (b.weight || 1), 0); - } - - static matches(row, filter) { - if (typeof filter === 'function') { - return filter(row); - } - if (Array.isArray(filter)) { - // e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}] - return filter.find(v => Axis.matches(row, v)); - } - if (typeof filter === 'object') { - // e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}} - for (const [key, value] of Object.entries(filter)) { - if (!row.hasOwnProperty(key) || !Axis.matches(row[key], value)) { - return false; - } - } - return true; - } - return row === filter; - } - - pickValue(filter) { - let values = this.values; - if (filter) { - values = values.filter(v => Axis.matches(v, filter)); - } - if (values.length === 0) { - const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); - throw Error(`No values produces for axis '${this.name}' from ${JSON.stringify(this.values)}, filter=${filterStr}`); - } - if (values.length === 1) { - return values[0]; - } - if (this.uniform) { - return values[Math.floor(Math.random() * values.length)]; - } - const totalWeight = !filter ? this.totalWeight : values.reduce((a, b) => a + (b.weight || 1), 0); - let weight = Math.random() * totalWeight; - for (let i = 0; i < values.length; i++) { - const value = values[i]; - weight -= value.weight || 1; - if (weight <= 0) { - return value; - } - } - return values[values.length - 1]; - } -} - -class MatrixBuilder { - constructor() { - this.axes = []; - this.axisByName = {}; - this.rows = []; - this.duplicates = {}; - this.excludes = []; - this.includes = []; - this.failOnUnsatisfiableFilters = false; - } - - /** - * Specifies include filter (all the generated rows would comply with all the include filters) - * @param filter - */ - include(filter) { - this.includes.push(filter); - } - - /** - * Specifies exclude filter (e.g. exclude a forbidden combination) - * @param filter - */ - exclude(filter) { - this.excludes.push(filter); - } - - addAxis({name, title, values}) { - const axis = new Axis({name, title, values}); - this.axes.push(axis); - this.axisByName[name] = axis; - return axis; - } - - setNamePattern(names) { - this.namePattern = names; - } - - /** - * Returns true if the row matches the include and exclude filters. - * @param row input row - * @returns {boolean} - */ - matches(row) { - return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) && - (this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f))); - } - - failOnUnsatisfiableFilters(value) { - this.failOnUnsatisfiableFilters = value; - } - - /** - * Adds a row that matches the given filter to the resulting matrix. - * filter values could be - * - literal values: filter={os: 'windows-latest'} - * - arrays: filter={os: ['windows-latest', 'linux-latest']} - * - functions: filter={os: x => x!='windows-latest'} - * @param filter object with keys matching axes names - * @returns {*} - */ - generateRow(filter) { - let res; - if (filter) { - // If matching row already exists, no need to generate more - res = this.rows.find(v => Axis.matches(v, filter)); - if (res) { - return res; - } - } - for (let i = 0; i < 142; i++) { - res = this.axes.reduce( - (prev, next) => - Object.assign(prev, { - [next.name]: next.pickValue(filter ? filter[next.name] : undefined) - }), - {} - ); - if (!this.matches(res)) { - continue; - } - const key = JSON.stringify(res); - if (!this.duplicates.hasOwnProperty(key)) { - this.duplicates[key] = true; - res.name = - this.namePattern.map(axisName => { - let value = res[axisName]; - const title = value.title; - if (typeof title != 'undefined') { - return title; - } - const computeTitle = this.axisByName[axisName].title; - return computeTitle ? computeTitle(value) : value; - }).filter(Boolean).join(", "); - this.rows.push(res); - return res; - } - } - const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); - const msg = `Unable to generate row for ${filterStr}. Please check include and exclude filters`; - if (this.failOnUnsatisfiableFilters) { - throw Error(msg); - } else { - console.warn(msg); - } - } - - generateRows(maxRows, filter) { - for (let i = 0; this.rows.length < maxRows && i < maxRows; i++) { - this.generateRow(filter); - } - return this.rows; - } - - /** - * Computes the number of all the possible combinations. - * @returns {{bad: number, good: number}} - */ - summary() { - let position = -1; - let indices = []; - let values = {}; - const axes = this.axes; - function resetValuesUpTo(nextPosition) { - for(let i=0; i.errorCode: FeatureExpect - get() = feature("errorCode", SQLException::getErrorCode) - -fun Expect.errorCode(assertionCreator: Expect.() -> Unit) = - feature("errorCode", SQLException::getErrorCode, assertionCreator) - -val Expect.sqlState: FeatureExpect - get() = feature("sqlState", SQLException::getSQLState) - -fun Expect.sqlState(assertionCreator: Expect.() -> Unit) = - feature("sqlState", SQLException::getSQLState, assertionCreator) diff --git a/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt new file mode 100644 index 0000000000..da2c3214ed --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmMain/kotlin/ch/tutteli/atrium/api/fluent/en_GB/sqlExceptionFeatureExtractors.kt @@ -0,0 +1,41 @@ +package ch.tutteli.atrium.api.fluent.en_GB + +import ch.tutteli.atrium.creating.Expect +import ch.tutteli.atrium.creating.FeatureExpect +import java.sql.SQLException + +/** + * Extracts the [errorCode][SQLException.getErrorCode] property from the subject of the assertion. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.errorCodeFeature + */ +val Expect.errorCode: FeatureExpect + get() = feature("errorCode", SQLException::getErrorCode) + +/** + * Extracts the [errorCode][SQLException.getErrorCode] property from the subject of the assertion and makes it the new subject. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.sqlStateFeature + */ +fun Expect.errorCode(assertionCreator: Expect.() -> Unit) = + feature("errorCode", SQLException::getErrorCode, assertionCreator) + +/** + * Extracts the [sqlState][SQLException.getSQLState] property from the subject of the assertion. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.errorCode + */ +val Expect.sqlState: FeatureExpect + get() = feature("sqlState", SQLException::getSQLState) + +/** + * Extracts the [sqlState][SQLException.getSQLState] property from the subject of the assertion and makes it the new subject. + * + * @since 1.3.0 + * @sample ch.tutteli.atrium.api.fluent.en_GB.samples.SQLExceptionFeaturesSamples.sqlState + */ +fun Expect.sqlState(assertionCreator: Expect.() -> Unit) = + feature("sqlState", SQLException::getSQLState, assertionCreator) diff --git a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt new file mode 100644 index 0000000000..658dc042cb --- /dev/null +++ b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeatureExtractorSamples.kt @@ -0,0 +1,79 @@ +package ch.tutteli.atrium.api.fluent.en_GB.samples + +import ch.tutteli.atrium.api.fluent.en_GB.* +import ch.tutteli.atrium.api.verbs.expect +import java.sql.SQLException +import kotlin.test.Test + +class SQLExceptionFeaturesSamples { + + @Test + fun errorCodeFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .errorCode // subject is now of type Int + .toEqual(1001) + + fails { + expect(exception) + .errorCode // subject is now of type Int + .toEqual(9999) // fails + .toBeGreaterThan(1000) // not evaluated/reported because `toEqual` already fails + // use `.errorCode { ... }` if you want all assertions evaluated + } + } + + @Test + fun errorCode() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).errorCode { // subject within this expectation-group is of type Int + toBeGreaterThan(1000) + toEqual(1001) + } // subject here is back to type SQLException + + fails { + expect(exception).errorCode { + toEqual(9999) // fails + toBeGreaterThan(1000) // still evaluated even though `toEqual` already fails + } + } + } + + @Test + fun sqlStateFeature() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception) + .sqlState // subject is now of type String + .toEqual("42000") + + fails { + expect(exception) + .sqlState // subject is now of type String + .toEqual("00000") // fails + .toContain("42") // not evaluated/reported because `toEqual` already fails + // use `.sqlState { ... }` if you want all assertions evaluated + } + } + + + + @Test + fun sqlState() { + val exception = SQLException("Test exception", "42000", 1001) + + expect(exception).sqlState { // subject within this expectation-group is of type String + toEqual("42000") + toContain("42") + } // subject here is back to type SQLException + + fails { + expect(exception).sqlState { + toEqual("00000") // fails + toContain("42") // still evaluated even though `toEqual` already fails + } + } + } +} diff --git a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt b/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt deleted file mode 100644 index 0beb2c2d91..0000000000 --- a/apis/fluent/atrium-api-fluent/src/jvmTest/kotlin/ch/tutteli/atrium/api/fluent/en_GB/samples/SQLExceptionFeaturesSamples.kt +++ /dev/null @@ -1,80 +0,0 @@ -package ch.tutteli.atrium.api.fluent.en_GB.samples - -import ch.tutteli.atrium.api.fluent.en_GB.* -import ch.tutteli.atrium.api.verbs.expect -import ch.tutteli.atrium.creating.Expect -import errorCode -import sqlState -import java.sql.SQLException -import kotlin.test.Test - -class SQLExceptionFeaturesSamples { - - @Test - fun errorCodeFeature() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception) - .errorCode // subject is now of type Int - .toEqual(1001) - - fails { - expect(exception) - .errorCode // subject is now of type Int - .toEqual(9999) // fails - .toBeGreaterThan(1000) // not evaluated/reported because `toEqual` already fails - // use `.errorCode { ... }` if you want all assertions evaluated - } - } - - @Test - fun sqlStateFeature() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception) - .sqlState // subject is now of type String - .toEqual("42000") - - fails { - expect(exception) - .sqlState // subject is now of type String - .toEqual("00000") // fails - .toContain("42") // not evaluated/reported because `toEqual` already fails - // use `.sqlState { ... }` if you want all assertions evaluated - } - } - - @Test - fun errorCode() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception).errorCode { // subject within this expectation-group is of type Int - toBeGreaterThan(1000) - toEqual(1001) - } // subject here is back to type SQLException - - fails { - expect(exception).errorCode { - toEqual(9999) // fails - toBeGreaterThan(1000) // still evaluated even though `toEqual` already fails - } - } - } - - @Test - fun sqlState() { - val exception = SQLException("Test exception", "42000", 1001) - - expect(exception).sqlState { // subject within this expectation-group is of type String - toEqual("42000") - toContain("42") - } // subject here is back to type SQLException - - fails { - expect(exception).sqlState { - toEqual("00000") // fails - toContain("42") // still evaluated even though `toEqual` already fails - } - } - } -} diff --git a/feature-random-matrix-ci.patch b/feature-random-matrix-ci.patch deleted file mode 100644 index 80aa9a0f1efcaa92cd92c2bcac3216d70206a0e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13280 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 E18{2X+5i9m From e33fedcd25c23b6b1d3b60db94ab0af3156ad621 Mon Sep 17 00:00:00 2001 From: ScratchFiveK <60172985+BereKanters@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:42:04 +0200 Subject: [PATCH 11/15] Integrate Custom Random Matrix Generation and Merge Ubuntu and Windows Workflows (#1776) * ###feature/Integrate random matrix generation and merge Ubuntu and Windows workflows - Added a new CI workflow using vlsi/github-actions-random-matrix to generate a dynamic matrix for testing on both Ubuntu and Windows with JDK 11 and JDK 17 - Merged existing build steps for Ubuntu and Windows into a single workflow file. --- .github/workflows/build-windows.yml | 50 ---- .github/workflows/matrix.js | 46 ++++ .github/workflows/matrix_builder.js | 237 ++++++++++++++++++ ...build-ubuntu.yml => quality-assurance.yml} | 87 ++++--- README.md | 3 +- 5 files changed, 331 insertions(+), 92 deletions(-) delete mode 100644 .github/workflows/build-windows.yml create mode 100644 .github/workflows/matrix.js create mode 100644 .github/workflows/matrix_builder.js rename .github/workflows/{build-ubuntu.yml => quality-assurance.yml} (57%) diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml deleted file mode 100644 index d0d5831cbd..0000000000 --- a/.github/workflows/build-windows.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Windows - -on: [ push, pull_request ] - -jobs: - build: - env: - JAVA_VERSION: ${{ matrix.java_version }} - runs-on: windows-latest - strategy: - fail-fast: true - matrix: - java_version: [ 11, 17 ] - - steps: - - uses: actions/checkout@v4 - - name: "Set up JDK ${{ matrix.java_version }}" - uses: actions/setup-java@v4 - with: - distribution: 'adopt' - java-version: ${{ matrix.java_version }} - - name: Setup Gradle - uses: gradle/gradle-build-action@v3.4.1 - - - name: "build using jdk ${{ matrix.java_version }}" - run: ./gradlew build - - - name: Upload windows build code coverage - uses: codecov/codecov-action@v4.5.0 - if: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }} - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: current_windows - env_vars: JAVA_VERSION - # too many timeout errors, let's not fail at the moment - #fail_ci_if_error: true - - # TODO 3.0.0 re-activate scala API - # - name: composite build atrium-scala2 - # run: ./gradlew build - # working-directory: misc\tools\atrium-scala2-test - - - name: composite build atrium-samples-test - run: ./gradlew build - working-directory: misc\tools\atrium-samples-test - - # TODO 1.3.0 activate again once we use JS IR -# - name: composite build atrium-js-sample-test -# run: ./gradlew build -# working-directory: misc\tools\atrium-js-sample-test diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js new file mode 100644 index 0000000000..bf5fcdef0a --- /dev/null +++ b/.github/workflows/matrix.js @@ -0,0 +1,46 @@ +let { MatrixBuilder } = require('./matrix_builder'); +const matrix = new MatrixBuilder(); + +// Add axes for the matrix +matrix.addAxis({ + name: 'os', + title: x => x.replace('-latest', ''), + values: [ + 'ubuntu-latest', + 'windows-latest' + ] +}); +matrix.addAxis({ + name: 'java_version', + values: ['11', '17'] +}); +matrix.addAxis({ + name: 'java_distribution', + title: x => x, + values: [ + 'corretto', + 'liberica', + 'microsoft', + 'temurin', + 'zulu' + ] +}); + +// Configure the order of the fields in job name +matrix.setNamePattern(['os', 'java_version', 'java_distribution']); + +// Ensure at least one windows and at least one linux job is present (macos is almost the same as linux) +matrix.generateRow({ os: 'windows-latest' }); +matrix.generateRow({ os: 'ubuntu-latest' }); + +// Generate more rows, no duplicates would be generated +const include = matrix.generateRows(process.env.MATRIX_JOBS || 5); +if (include.length === 0) { + throw new Error('Matrix list is empty'); +} +// Sort jobs by name, however, numeric parts are sorted appropriately +// For instance, 'windows 8' would come before 'windows 11' +include.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true })); + +console.log(include); +console.log('::set-output name=matrix::' + JSON.stringify({ include })); diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js new file mode 100644 index 0000000000..a98ede8e6c --- /dev/null +++ b/.github/workflows/matrix_builder.js @@ -0,0 +1,237 @@ +// License: Apache-2.0 +// Copyright Vladimir Sitnikov, 2021 +// See https://github.com/vlsi/github-actions-random-matrix + +class Axis { + constructor({name, title, values}) { + this.name = name; + this.title = title; + this.values = values; + // If all entries have same weight, the axis has uniform distribution + this.uniform = values.reduce((a, b) => a === (b.weight || 1) ? a : 0, values[0].weight || 1) !== 0 + this.totalWeight = this.uniform ? values.length : values.reduce((a, b) => a + (b.weight || 1), 0); + } + + static matches(row, filter) { + if (typeof filter === 'function') { + return filter(row); + } + if (Array.isArray(filter)) { + // e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}] + return filter.find(v => Axis.matches(row, v)); + } + if (typeof filter === 'object') { + // e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}} + for (const [key, value] of Object.entries(filter)) { + if (!row.hasOwnProperty(key) || !Axis.matches(row[key], value)) { + return false; + } + } + return true; + } + return row === filter; + } + + pickValue(filter) { + let values = this.values; + if (filter) { + values = values.filter(v => Axis.matches(v, filter)); + } + if (values.length === 0) { + const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); + throw Error(`No values produces for axis '${this.name}' from ${JSON.stringify(this.values)}, filter=${filterStr}`); + } + if (values.length === 1) { + return values[0]; + } + if (this.uniform) { + return values[Math.floor(Math.random() * values.length)]; + } + const totalWeight = !filter ? this.totalWeight : values.reduce((a, b) => a + (b.weight || 1), 0); + let weight = Math.random() * totalWeight; + for (let i = 0; i < values.length; i++) { + const value = values[i]; + weight -= value.weight || 1; + if (weight <= 0) { + return value; + } + } + return values[values.length - 1]; + } +} + +class MatrixBuilder { + constructor() { + this.axes = []; + this.axisByName = {}; + this.rows = []; + this.duplicates = {}; + this.excludes = []; + this.includes = []; + this.failOnUnsatisfiableFilters = false; + // this.namePattern = []; // Initialize as an empty array + } + + /** + * Specifies include filter (all the generated rows would comply with all the include filters) + * @param filter + */ + include(filter) { + this.includes.push(filter); + } + + /** + * Specifies exclude filter (e.g. exclude a forbidden combination) + * @param filter + */ + exclude(filter) { + this.excludes.push(filter); + } + + addAxis({name, title, values}) { + const axis = new Axis({name, title, values}); + this.axes.push(axis); + this.axisByName[name] = axis; + return axis; + } + + setNamePattern(names) { + this.namePattern = names; + } + + /** + * Returns true if the row matches the include and exclude filters. + * @param row input row + * @returns {boolean} + */ + matches(row) { + return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) && + (this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f))); + } + + failOnUnsatisfiableFilters(value) { + this.failOnUnsatisfiableFilters = value; + } + + /** + * Adds a row that matches the given filter to the resulting matrix. + * filter values could be + * - literal values: filter={os: 'windows-latest'} + * - arrays: filter={os: ['windows-latest', 'linux-latest']} + * - functions: filter={os: x => x!='windows-latest'} + * @param filter object with keys matching axes names + * @returns {*} + */ + generateRow(filter) { + let res; + if (filter) { + // If matching row already exists, no need to generate more + res = this.rows.find(v => Axis.matches(v, filter)); + if (res) { + return res; + } + } + for (let i = 0; i < 142; i++) { + res = this.axes.reduce( + (prev, next) => + Object.assign(prev, { + [next.name]: next.pickValue(filter ? filter[next.name] : undefined) + }), + {} + ); + if (!this.matches(res)) { + continue; + } + const key = JSON.stringify(res); + if (!this.duplicates.hasOwnProperty(key)) { + this.duplicates[key] = true; + res.name = + this.namePattern.map(axisName => { + let value = res[axisName]; + const title = value.title; + if (typeof title != 'undefined') { + return title; + } + const computeTitle = this.axisByName[axisName].title; + return computeTitle ? computeTitle(value) : value; + }).filter(Boolean).join(", "); + this.rows.push(res); + return res; + } + } + const filterStr = typeof filter === 'string' ? filter.toString() : JSON.stringify(filter); + const msg = `Unable to generate row for ${filterStr}. Please check include and exclude filters`; + if (this.failOnUnsatisfiableFilters) { + throw Error(msg); + } else { + console.warn(msg); + } + } + + generateRows(maxRows, filter) { + for (let i = 0; this.rows.length < maxRows && i < maxRows; i++) { + this.generateRow(filter); + } + return this.rows; + } + + /** + * Computes the number of all the possible combinations. + * @returns {{bad: number, good: number}} + */ + summary() { + let position = -1; + let indices = []; + let values = {}; + const axes = this.axes; + function resetValuesUpTo(nextPosition) { + for(let i=0; iandroid.sha256 + curl -o android.jar -L "https://github.com/Sable/android-platforms/blob/master/android-26/android.jar?raw=true" + echo "cdc1846376a14b0370cc63454a129606b4a52cc50ada75ef0d4cf956b1ad2daa android.jar" > android.sha256 if ! sha256sum -c android.sha256; then echo >&2 "wrong sha256 for android.jar, expected:" cat >&2 android.sha256 @@ -59,24 +80,9 @@ jobs: exit 1; fi - - name: check Atrium's -jvm.jar can be dexed + - name: Check Atrium's -jvm.jar can be dexed run: ATRIUM_ANDROID_JAR="$PWD/android-jar-cache/android.jar" ./gradlew checkDexer - # TODO 3.0.0 re-activate scala API - # - name: composite build atrium-scala2 - # run: ./gradlew build - # working-directory: misc/tools/atrium-scala2-test - - - - name: composite build atrium-samples-test - run: ./gradlew build - working-directory: misc/tools/atrium-samples-test - - # TODO 1.3.0 activate again once we use JS IR - # - name: composite build atrium-js-sample-test - # run: ./gradlew build - # working-directory: misc/tools/atrium-js-sample-test - forwardCompatibility: env: JAVA_VERSION: 17 @@ -88,13 +94,14 @@ jobs: kotlin_version: [ '1.5', '1.6', '1.7', '1.8', '1.9', '2.0' ] steps: - uses: actions/checkout@v4 - - name: "Set up JDK 17" + - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: 'adopt' java-version: 17 + - name: Setup Gradle uses: gradle/gradle-build-action@v3.4.1 - - name: "build using kotlin ${{ matrix.kotlin_version }}" + - name: Build using Kotlin ${{ matrix.kotlin_version }} run: ./gradlew build diff --git a/README.md b/README.md index 476f74c9a8..1d0d76a55d 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ [![Download](https://img.shields.io/badge/Download-1.2.0-%23007ec6)](https://central.sonatype.com/artifact/ch.tutteli.atrium/atrium-fluent/1.2.0) [![EUPL](https://img.shields.io/badge/%E2%9A%96-EUPL%201.2-%230b45a6)](https://joinup.ec.europa.eu/collection/eupl/eupl-text-11-12 "License") [![atrium @ kotlinlang.slack.com](https://img.shields.io/static/v1?label=kotlinlang&message=atrium&color=blue&logo=slack)](https://kotlinlang.slack.com/messages/atrium "See invitation link under section FAQ") -[![Build Status Ubuntu](https://github.com/robstoll/atrium/workflows/Ubuntu/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3AUbuntu+branch%3Amain) -[![Build Status Windows](https://github.com/robstoll/atrium/workflows/Windows/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions?query=workflow%3AWindows+branch%3Amain) +[![Quality Assurance](https://github.com/robstoll/atrium/actions/workflows/quality-assurance.yml/badge.svg?event=push&branch=main)](https://github.com/robstoll/atrium/actions/workflows/quality-assurance.yml?query=branch%3Amain) [![Coverage](https://codecov.io/gh/robstoll/atrium/branch/main/graph/badge.svg)](https://app.codecov.io/github/robstoll/atrium/branch/main) [![Newcomers Welcome](https://img.shields.io/badge/%F0%9F%91%8B-Newcomers%20Welcome-blueviolet)](https://github.com/robstoll/atrium/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22 "Ask in slack for help") From 904b2e121cb347e16163b357a70242cef4bb404d Mon Sep 17 00:00:00 2001 From: BereKanters Date: Sun, 16 Jun 2024 18:24:02 +0200 Subject: [PATCH 12/15] ###Fix/Removed non needed comment - Removed comment from constructor --- .github/workflows/matrix_builder.js | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js index a98ede8e6c..4cf2b8e5cb 100644 --- a/.github/workflows/matrix_builder.js +++ b/.github/workflows/matrix_builder.js @@ -69,7 +69,6 @@ class MatrixBuilder { this.excludes = []; this.includes = []; this.failOnUnsatisfiableFilters = false; - // this.namePattern = []; // Initialize as an empty array } /** From 4521ddb29e30ae6cafbf9e6318619a160eba3e2e Mon Sep 17 00:00:00 2001 From: BereKanters Date: Sun, 16 Jun 2024 18:28:57 +0200 Subject: [PATCH 13/15] ###Fix/Re-added comment - Added comment to constructor - Added this.namePattern = []; --- .github/workflows/matrix_builder.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js index 4cf2b8e5cb..a98ede8e6c 100644 --- a/.github/workflows/matrix_builder.js +++ b/.github/workflows/matrix_builder.js @@ -69,6 +69,7 @@ class MatrixBuilder { this.excludes = []; this.includes = []; this.failOnUnsatisfiableFilters = false; + // this.namePattern = []; // Initialize as an empty array } /** From d400a45f9636ad7c880cf0e060ef6fe87bbd1d36 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Sun, 16 Jun 2024 23:19:00 +0200 Subject: [PATCH 14/15] ###Fix/Added Requires java to module for sqlExceptionFeatureExtractors - Added Requires java to module-info.java --- apis/fluent/atrium-api-fluent/src/jvmMain/java/module-info.java | 1 + 1 file changed, 1 insertion(+) diff --git a/apis/fluent/atrium-api-fluent/src/jvmMain/java/module-info.java b/apis/fluent/atrium-api-fluent/src/jvmMain/java/module-info.java index 35fa46e0f3..c76a373cf9 100644 --- a/apis/fluent/atrium-api-fluent/src/jvmMain/java/module-info.java +++ b/apis/fluent/atrium-api-fluent/src/jvmMain/java/module-info.java @@ -3,6 +3,7 @@ requires ch.tutteli.atrium.logic; requires kotlin.stdlib; requires ch.tutteli.kbox; + requires java.sql; exports ch.tutteli.atrium.api.fluent.en_GB; } From 552f710f5c24b71e75343734e90dac029231fc22 Mon Sep 17 00:00:00 2001 From: BereKanters Date: Sun, 16 Jun 2024 23:32:41 +0200 Subject: [PATCH 15/15] ###Feat/Updated API declarations for new SQLException feature extractors - Introduced feature extractors for SQLException: - errorCode: Extracts the error code from an SQLException instance. - sqlState: Extracts the SQL state from an SQLException instance. --- .../atrium-api-fluent/api/main/atrium-api-fluent.api | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apis/fluent/atrium-api-fluent/api/main/atrium-api-fluent.api b/apis/fluent/atrium-api-fluent/api/main/atrium-api-fluent.api index 197702e69b..bad036054c 100644 --- a/apis/fluent/atrium-api-fluent/api/main/atrium-api-fluent.api +++ b/apis/fluent/atrium-api-fluent/api/main/atrium-api-fluent.api @@ -518,6 +518,13 @@ public final class ch/tutteli/atrium/api/fluent/en_GB/SequenceSubjectChangersKt public static final fun asList (Lch/tutteli/atrium/creating/Expect;Lkotlin/jvm/functions/Function1;)Lch/tutteli/atrium/creating/Expect; } +public final class ch/tutteli/atrium/api/fluent/en_GB/SqlExceptionFeatureExtractorsKt { + public static final fun errorCode (Lch/tutteli/atrium/creating/Expect;Lkotlin/jvm/functions/Function1;)Lch/tutteli/atrium/creating/Expect; + public static final fun getErrorCode (Lch/tutteli/atrium/creating/Expect;)Lch/tutteli/atrium/creating/FeatureExpect; + public static final fun getSqlState (Lch/tutteli/atrium/creating/Expect;)Lch/tutteli/atrium/creating/FeatureExpect; + public static final fun sqlState (Lch/tutteli/atrium/creating/Expect;Lkotlin/jvm/functions/Function1;)Lch/tutteli/atrium/creating/Expect; +} + public final class ch/tutteli/atrium/api/fluent/en_GB/ThirdPartyExpectationsKt { public static final fun toHoldThirdPartyExpectation (Lch/tutteli/atrium/creating/Expect;Ljava/lang/String;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Lch/tutteli/atrium/creating/Expect; }