├── .editorconfig
├── .github
├── config.yml
├── stale.yml
└── workflows
│ ├── PR.yml
│ ├── master.yml
│ └── release.yml
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle.kts
├── buildSrc
├── build.gradle.kts
├── gradle.properties
├── settings.gradle.kts
└── src
│ └── main
│ └── kotlin
│ ├── Ci.kt
│ ├── kotest-conventions.gradle.kts
│ └── versionCatalog.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── kotest-assertions-arrow-fx-coroutines
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── io
│ │ └── kotest
│ │ └── assertions
│ │ └── arrow
│ │ └── fx
│ │ └── coroutines
│ │ ├── ExitCase.kt
│ │ ├── ProjectResource.kt
│ │ ├── Resource.kt
│ │ ├── ResourceExtension.kt
│ │ └── shouldBe.kt
│ └── commonTest
│ └── kotlin
│ └── io
│ └── kotest
│ └── assertions
│ └── arrow
│ └── fx
│ └── coroutines
│ ├── ExitCaseTest.kt
│ ├── ResourceExtensionLeafTest.kt
│ ├── ResourceExtensionRootTest.kt
│ ├── ResourceExtensionSpecTest.kt
│ └── ResourceUnderTest.kt
├── kotest-assertions-arrow
├── build.gradle.kts
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── io
│ │ └── kotest
│ │ └── assertions
│ │ └── arrow
│ │ ├── Utils.kt
│ │ └── core
│ │ ├── Either.kt
│ │ ├── Ior.kt
│ │ ├── Nel.kt
│ │ ├── Nelnspectors.kt
│ │ ├── Option.kt
│ │ └── Raise.kt
│ └── commonTest
│ └── kotlin
│ └── io
│ └── kotest
│ └── assertions
│ └── arrow
│ └── core
│ ├── EitherMatchers.kt
│ ├── IorMatchers.kt
│ ├── NelMatchers.kt
│ ├── OptionMatchers.kt
│ └── RaiseMatchers.kt
├── kotest-property-arrow-optics
├── build.gradle.kts
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── io
│ │ └── kotest
│ │ └── property
│ │ └── arrow
│ │ └── optics
│ │ ├── IsoLaws.kt
│ │ ├── LensLaws.kt
│ │ ├── OptionalLaws.kt
│ │ ├── PrismLaws.kt
│ │ └── TraversalLaws.kt
│ └── commonTest
│ └── kotlin
│ └── io
│ └── kotest
│ └── property
│ └── arrow
│ └── optics
│ └── TraversalTests.kt
├── kotest-property-arrow
├── build.gradle.kts
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── io
│ │ └── kotest
│ │ └── property
│ │ └── arrow
│ │ ├── core
│ │ ├── Either.kt
│ │ ├── Function.kt
│ │ ├── Ior.kt
│ │ ├── Nel.kt
│ │ ├── Option.kt
│ │ ├── SemiringLaws.kt
│ │ └── Tuple.kt
│ │ └── laws
│ │ ├── Law.kt
│ │ └── Util.kt
│ └── commonTest
│ └── kotlin
│ └── io
│ └── kotest
│ └── property
│ └── arrow
│ └── core
│ ├── EitherTests.kt
│ └── IorTests.kt
├── kotlin-js-store
└── yarn.lock
├── publish-mpp.gradle.kts
├── renovate.json
├── settings.gradle.kts
└── signing-pom-details.gradle.kts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 |
5 | indent_style = space
6 | indent_size = 2
7 |
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.gradle.kts]
12 | indent_size = 2
13 |
--------------------------------------------------------------------------------
/.github/config.yml:
--------------------------------------------------------------------------------
1 | todo:
2 | keyword: "// todo"
3 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.github/workflows/PR.yml:
--------------------------------------------------------------------------------
1 | name: PR-Test
2 |
3 | on:
4 | pull_request:
5 | paths-ignore:
6 | - '*.md'
7 | - '*.yml'
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout the repo
15 | uses: actions/checkout@v4
16 |
17 | - name: Setup JDK
18 | uses: actions/setup-java@v4
19 | with:
20 | java-version: '8'
21 | distribution: 'temurin'
22 |
23 | - name: Run tests
24 | run: ./gradlew check
25 |
26 | - name: Bundle the build report
27 | if: failure()
28 | run: find . -type d -name 'reports' | zip -@ -r build-reports.zip
29 |
30 | - name: Upload the build report
31 | if: failure()
32 | uses: actions/upload-artifact@master
33 | with:
34 | name: error-report
35 | path: build-reports.zip
36 |
37 | env:
38 | GRADLE_OPTS: -Dorg.gradle.configureondemand=true -Dorg.gradle.parallel=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8"
39 |
--------------------------------------------------------------------------------
/.github/workflows/master.yml:
--------------------------------------------------------------------------------
1 | name: master
2 |
3 | on:
4 | push:
5 | paths-ignore:
6 | - '*.md'
7 | - '*.yml'
8 | branches:
9 | - master
10 |
11 | env:
12 | GRADLE_OPTS: -Dorg.gradle.configureondemand=true -Dorg.gradle.parallel=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8"
13 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
14 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
15 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
16 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
17 |
18 | jobs:
19 | linux:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Checkout the repo
23 | uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 |
27 | - name: Setup JDK
28 | uses: actions/setup-java@v4
29 | with:
30 | java-version: '11'
31 | distribution: 'temurin'
32 |
33 | - name: Run tests
34 | run: ./gradlew check --scan
35 |
36 | - name: publish snapshots
37 | run: ./gradlew publish
38 |
39 | - name: Bundle the build report
40 | if: failure()
41 | run: find . -type d -name 'reports' | zip -@ -r build-reports.zip
42 |
43 | - name: Upload the build report
44 | if: failure()
45 | uses: actions/upload-artifact@master
46 | with:
47 | name: error-report
48 | path: build-reports.zips
49 |
50 | macos:
51 | runs-on: macos-latest
52 | steps:
53 | - name: Checkout the repo
54 | uses: actions/checkout@v4
55 | with:
56 | fetch-depth: 0
57 |
58 | - name: Setup JDK
59 | uses: actions/setup-java@v4
60 | with:
61 | java-version: '11'
62 | distribution: 'temurin'
63 |
64 | - name: Run macos tests
65 | run: ./gradlew macosX64Test macosArm64Test iosX64Test iosSimulatorArm64Test tvosX64Test tvosSimulatorArm64Test watchosX64Test watchosSimulatorArm64Test --scan
66 |
67 | - name: publish macos snapshots
68 | run: |
69 | ./gradlew publishMacosX64PublicationToDeployRepository publishMacosArm64PublicationToDeployRepository publishIosArm64PublicationToDeployRepository publishIosX64PublicationToDeployRepository \
70 | publishTvosArm64PublicationToDeployRepository publishTvosX64PublicationToDeployRepository publishTvosSimulatorArm64PublicationToDeployRepository publishWatchosArm32PublicationToDeployRepository publishIosSimulatorArm64PublicationToDeployRepository \
71 | publishWatchosArm64PublicationToDeployRepository publishWatchosX64PublicationToDeployRepository publishWatchosSimulatorArm64PublicationToDeployRepository
72 |
73 | - name: Bundle the build report
74 | if: failure()
75 | run: find . -type d -name 'reports' | zip -@ -r build-reports.zip
76 |
77 | - name: Upload the build report
78 | if: failure()
79 | uses: actions/upload-artifact@master
80 | with:
81 | name: error-report
82 | path: build-reports.zip
83 |
84 | windows:
85 | runs-on: windows-latest
86 | steps:
87 | - name: Checkout the repo
88 | uses: actions/checkout@v4
89 | with:
90 | fetch-depth: 0
91 |
92 | - name: Setup JDK
93 | uses: actions/setup-java@v4
94 | with:
95 | java-version: '11'
96 | distribution: 'temurin'
97 |
98 | - name: Run tests
99 | run: ./gradlew mingwX64Test --scan
100 |
101 | - name: publish mingw64 snapshot
102 | run: ./gradlew publishMingwX64PublicationToDeployRepository
103 |
104 | - name: Bundle the build report
105 | if: failure()
106 | run: find . -type d -name 'reports' | zip -@ -r build-reports.zip
107 |
108 | - name: Upload the build report
109 | if: failure()
110 | uses: actions/upload-artifact@master
111 | with:
112 | name: error-report
113 | path: build-reports.zip
114 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | version:
7 | description: "The release version"
8 | required: true
9 | branch:
10 | description: "The branch to release from"
11 | required: true
12 | default: 'master'
13 |
14 |
15 | env:
16 | GRADLE_OPTS: -Dorg.gradle.configureondemand=true -Dorg.gradle.parallel=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8"
17 | RELEASE_VERSION: ${{ github.event.inputs.version }}
18 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
19 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
20 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
21 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
22 |
23 | jobs:
24 | linux:
25 | runs-on: ubuntu-latest
26 |
27 | steps:
28 | - name: Checkout the repo
29 | uses: actions/checkout@v4
30 | with:
31 | fetch-depth: 0
32 | ref: ${{ github.event.inputs.branch }}
33 |
34 | - name: Setup JDK
35 | uses: actions/setup-java@v4
36 | with:
37 | java-version: '11'
38 | distribution: 'temurin'
39 |
40 | - name: deploy to sonatype
41 | run: ./gradlew publish
42 |
43 | macos:
44 | runs-on: macos-latest
45 |
46 | steps:
47 | - name: Checkout the repo
48 | uses: actions/checkout@v4
49 | with:
50 | fetch-depth: 0
51 | ref: ${{ github.event.inputs.branch }}
52 |
53 | - name: Setup JDK
54 | uses: actions/setup-java@v4
55 | with:
56 | java-version: '11'
57 | distribution: 'temurin'
58 |
59 | - name: deploy to sonatype
60 | run: |
61 | ./gradlew publishMacosX64PublicationToDeployRepository publishMacosArm64PublicationToDeployRepository publishIosArm64PublicationToDeployRepository publishIosX64PublicationToDeployRepository \
62 | publishTvosArm64PublicationToDeployRepository publishTvosX64PublicationToDeployRepository publishTvosSimulatorArm64PublicationToDeployRepository publishWatchosArm32PublicationToDeployRepository publishIosSimulatorArm64PublicationToDeployRepository \
63 | publishWatchosArm64PublicationToDeployRepository publishWatchosX64PublicationToDeployRepository publishWatchosSimulatorArm64PublicationToDeployRepository
64 |
65 | windows:
66 | runs-on: windows-latest
67 |
68 | steps:
69 | - name: Checkout the repo
70 | uses: actions/checkout@v4
71 | with:
72 | fetch-depth: 0
73 | ref: ${{ github.event.inputs.branch }}
74 |
75 | - name: Setup JDK
76 | uses: actions/setup-java@v4
77 | with:
78 | java-version: '11'
79 | distribution: 'temurin'
80 |
81 | - name: deploy to sonatype
82 | run: ./gradlew publishMingwX64PublicationToDeployRepository
83 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.log
3 | .idea
4 | *.iml
5 | **/.kotlintest/
6 | .gradle
7 |
8 | target/
9 | build/
10 | /build/
11 | out/
12 | lib_managed/
13 | src_managed/
14 | project/boot/
15 | project/plugins/project/
16 | credentials.sbt
17 | /.idea/
18 | .DS_Store
19 | .kotlin
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kotest-extensions-arrow
2 |
3 | ## Moved to main kotest repo
4 |
5 | Kotest extension for [Arrow](https://arrow-kt.io/).
6 |
7 | See [docs](https://kotest.io/docs/assertions/arrow.html).
8 |
9 | Please create issues on the main kotest [board](https://github.com/kotest/kotest/issues).
10 |
11 | [](https://github.com/kotest/kotest-extensions-arrow/actions)
12 | [
](http://search.maven.org/#search|ga|1|kotest-assertions-arrow)
13 | 
14 | [](https://kotlinlang.slack.com/archives/CT0G9SD7Z)
15 | [
](https://s01.oss.sonatype.org/content/repositories/snapshots/io/kotest/extensions/kotest-assertions-arrow/)
16 |
17 | ## How to use it
18 |
19 | ```kotlin
20 | depedencies {
21 | implementation("io.kotest.extensions:kotest-assertions-arrow:")
22 | implementation("io.kotest.extensions:kotest-assertions-arrow-fx-coroutines:")
23 | }
24 | ```
25 |
26 | for property-based testing:
27 |
28 | ```kotlin
29 | dependencies {
30 | implementation("io.kotest.extensions:kotest-property-arrow:")
31 | // optional: the following includes optics related Laws
32 | implementation("io.kotest.extensions:kotest-property-arrow-optics:")
33 | }
34 | ```
35 |
36 | to depend on snapshot releases add the snapshot repository url:
37 |
38 | ```kotlin
39 | repositories {
40 | //...
41 | maven {
42 | url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
43 | }
44 | }
45 | ```
46 |
47 | Note:
48 | Please add `io.arrow-kt:arrow-core:arrow-version`, `io.arrow-kt:arrow-fx-coroutines:arrow-version` or `io.arrow-kt:arrow-optics:arrow-version`, if they're missing in your classpath.
49 | Otherwise, it will lead to unresolved Reference errors.
50 | In the form of: "Cannot access class `arrow.core.Either` Check your module classpath for missing or conflicting dependencies."
51 | The project is not shipping the arrow jars because this leads to dependency conflicts and further adjusting the dependency graph.
52 |
53 | ## Changelog
54 |
55 | ### 2.0.0
56 |
57 | * Release for Kotlin 2.1.0 and Arrow 2.0
58 |
59 | ### 1.4.0
60 |
61 | * Release for Kotlin 1.8 and Kotest 5.7 and Arrow 1.2
62 |
63 | ### 1.3.3
64 |
65 | * Release for arrow 1.1.5
66 |
67 | ### 1.3.2
68 |
69 | * Release for arrow 1.1.4
70 |
71 | ### 1.3.1
72 |
73 | * Added `Ior` matchers.
74 |
75 | ### 1.3.0
76 |
77 | - Update to kotest 5.5.4
78 | - Add assertion module for arrow-fx-coroutines with combinators related to `Resource` and `ExitCase`
79 | - Add `Either.rethrow`
80 |
81 | ### 1.2.5
82 |
83 | - Upgrade to 5.2.3 and update kotlinx-coroutines to 1.6.1
84 |
85 | ### 1.2.4
86 |
87 | - Upgrade to 5.2.1 and restores compatibilty with 5.2.X series https://github.com/kotest/kotest-extensions-arrow/pull/149
88 |
89 | ### 1.2.3
90 |
91 | - fix linking error in native platforms [#140](https://github.com/kotest/kotest-extensions-arrow/issues/140)
92 |
93 | ### 1.2.2
94 |
95 | * update kotest to 5.1.0
96 | * update kotlin to 1.6.10
97 | * publish missing multiplatform targets affecting `1.2.1` and `1.2.0`
98 |
99 | ### 1.2.1
100 |
101 | * Added Arb.valid and Arb.invalid
102 | * Added Arb.nel(arb, range) - a variant of Arb.nel(arb) that accepts a range parameter
103 |
104 | ### 1.2.0
105 |
106 | * Upgrade to Arrow 1.0.1
107 | * Multiplatform artifacts for `kotest-assertions-arrow`, `kotest-property-arrow` and `kotest-property-arrow-optics`
108 | * `#2670` Replace explicit usage of eq with should be from kotest assertion core
109 | * `testLaws` has `RootScope` as a receiver instead of `RootContext`
110 |
111 | ### 1.1.1
112 |
113 | * removes deprecated members in `kotest-assertions-arrow`
114 |
115 | ### 1.1.0
116 |
117 | **Note that from this release, the minimium requirements are Kotest 5.0+ and Kotlin 1.6**
118 |
119 | * Update to Arrow 1.0.0
120 | * fix Java 1.8 compatibility [#2437](https://github.com/kotest/kotest/issues/2437)
121 | * Added `kotest-property-arrow` and `kotest-property-arrow-optics` for property-based testing with Arrow
122 | * includes deprecation cycle of 1.0.3
123 | * remove dependency to kotlinX-coroutines and kotest-property in `kotest-assertions-arrow`
124 |
125 |
126 | ### 1.0.3
127 |
128 | * Update to Arrow 0.13.2
129 | * Added a deprecation cycle for previous descriptors in 1.0.2 in favor of smart-casted variants in `io.kotest.assertions.arrow.core`
130 | * Reorg existing functions to `io.kotest.assertions.arrow.core`
131 | * Not leaking the arrow dependency into the runtime of users
132 | * Added Arb, Arb>
133 |
134 | ### 1.0.2
135 |
136 | * Updated to arrow 0.13.1
137 | * Updated option to support nullables
138 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | repositories {
2 | mavenCentral()
3 | gradlePluginPortal()
4 | }
5 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.kotlin.dsl.`kotlin-dsl`
2 |
3 | repositories {
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 |
8 | plugins {
9 | `kotlin-dsl`
10 | }
11 |
12 | dependencies {
13 | implementation(libs.kotlin.gradle.plugin)
14 | implementation(libs.animalsniffer)
15 | }
16 |
--------------------------------------------------------------------------------
/buildSrc/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.mpp.stability.nowarn=true
2 |
--------------------------------------------------------------------------------
/buildSrc/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | versionCatalogs {
3 | create("libs") {
4 | from(files("../gradle/libs.versions.toml"))
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/Ci.kt:
--------------------------------------------------------------------------------
1 | object Ci {
2 |
3 | // this is the version used for building snapshots
4 | // .GITHUB_RUN_NUMBER-snapshot will be appended
5 | private const val snapshotBase = "1.4.0"
6 |
7 | private val githubRunNumber = System.getenv("GITHUB_RUN_NUMBER")
8 |
9 | private val snapshotVersion = when (githubRunNumber) {
10 | null -> "$snapshotBase-LOCAL"
11 | else -> "$snapshotBase.${githubRunNumber}-SNAPSHOT"
12 | }
13 |
14 | private val releaseVersion = System.getenv("RELEASE_VERSION")
15 |
16 | val isRelease = releaseVersion != null
17 | val version = releaseVersion ?: snapshotVersion
18 | }
19 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/kotest-conventions.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.api.tasks.testing.Test
2 | import org.gradle.api.tasks.testing.logging.TestExceptionFormat
3 | import org.gradle.api.tasks.testing.logging.TestLogEvent
4 | import org.gradle.kotlin.dsl.named
5 |
6 | plugins {
7 | kotlin("multiplatform")
8 | id("ru.vyarus.animalsniffer")
9 | }
10 |
11 | repositories {
12 | mavenCentral()
13 | gradlePluginPortal()
14 | }
15 |
16 | group = "io.kotest.extensions"
17 | version = Ci.version
18 |
19 | kotlin {
20 | explicitApi()
21 |
22 | metadata()
23 |
24 | jvm {
25 | compilations.all {
26 | kotlinOptions.jvmTarget = "1.8"
27 | }
28 | }
29 |
30 | js(IR) {
31 | browser()
32 | nodejs()
33 | }
34 |
35 | linuxX64()
36 |
37 | mingwX64()
38 |
39 | iosArm64()
40 | iosSimulatorArm64()
41 | iosX64()
42 | macosArm64()
43 | macosX64()
44 | tvosArm64()
45 | tvosSimulatorArm64()
46 | tvosX64()
47 | watchosArm32()
48 | watchosArm64()
49 | watchosSimulatorArm64()
50 | watchosX64()
51 |
52 | applyDefaultHierarchyTemplate()
53 |
54 | sourceSets {
55 | val kotestVersion = resolveVersion("kotest")
56 |
57 | val jvmTest by getting {
58 | dependencies {
59 | implementation("io.kotest:kotest-runner-junit5:$kotestVersion")
60 | }
61 | }
62 |
63 | all {
64 | languageSettings.optIn("kotlin.RequiresOptIn")
65 | }
66 | }
67 | }
68 |
69 | animalsniffer {
70 | ignore = listOf("java.lang.*")
71 | }
72 |
73 | tasks.named("jvmTest") {
74 | useJUnitPlatform()
75 | maxParallelForks = Runtime.getRuntime().availableProcessors()
76 | testLogging {
77 | showExceptions = true
78 | showStandardStreams = true
79 | events = setOf(
80 | TestLogEvent.FAILED,
81 | TestLogEvent.PASSED
82 | )
83 | exceptionFormat = TestExceptionFormat.FULL
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/buildSrc/src/main/kotlin/versionCatalog.kt:
--------------------------------------------------------------------------------
1 | import org.gradle.api.Project
2 | import org.gradle.api.artifacts.VersionCatalogsExtension
3 | import org.gradle.kotlin.dsl.getByType
4 |
5 | // Workaround for https://github.com/gradle/gradle/issues/15383
6 | fun Project.resolveVersion(name: String) =
7 | extensions.getByType()
8 | .named("libs")
9 | .findVersion(name)
10 | .get().requiredVersion
11 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #https://github.com/gradle/gradle/issues/11308
2 | org.gradle.internal.publish.checksums.insecure=true
3 | systemProp.org.gradle.internal.publish.checksums.insecure=true
4 | org.gradle.parallel=false
5 | kotlin.mpp.stability.nowarn=true
6 | org.gradle.daemon=true
7 | org.gradle.jvmargs=-Xmx3G -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | kotlin.native.ignoreIncorrectDependencies=true
9 | kotlin.native.ignoreDisabledTargets=true
10 | kotlin.native.cacheKind.macosX64=none
11 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | arrow = "2.0.1"
3 | kotlin = "2.1.0"
4 | kotest = "5.8.1"
5 | kotlinx-coroutines = "1.6.4"
6 | animalsniffer = "1.7.2"
7 | kotlinBinaryCompatibilityValidator = "0.14.0"
8 |
9 | [libraries]
10 | kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
11 | kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
12 | kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" }
13 | kotest-framework-api = { module = "io.kotest:kotest-framework-api", version.ref = "kotest" }
14 | kotest-property = { module = "io.kotest:kotest-property", version.ref = "kotest" }
15 |
16 | kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
17 |
18 | arrow-fx-coroutines = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" }
19 | arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" }
20 | arrow-optics = { module = "io.arrow-kt:arrow-optics", version.ref = "arrow" }
21 | arrow-functions = { module = "io.arrow-kt:arrow-functions", version.ref = "arrow" }
22 |
23 | # Gradle plugins used in buildSrc
24 | kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
25 | animalsniffer = { module = "ru.vyarus:gradle-animalsniffer-plugin", version.ref = "animalsniffer" }
26 |
27 | [plugins]
28 | kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
29 |
30 | kotlin-binaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlinBinaryCompatibilityValidator" }
31 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kotest/kotest-extensions-arrow/cf140a405e6724b09dd49985476ef75bc7af7357/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
147 | # shellcheck disable=SC3045
148 | MAX_FD=$( ulimit -H -n ) ||
149 | warn "Could not query maximum file descriptor limit"
150 | esac
151 | case $MAX_FD in #(
152 | '' | soft) :;; #(
153 | *)
154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
155 | # shellcheck disable=SC3045
156 | ulimit -n "$MAX_FD" ||
157 | warn "Could not set maximum file descriptor limit to $MAX_FD"
158 | esac
159 | fi
160 |
161 | # Collect all arguments for the java command, stacking in reverse order:
162 | # * args from the command line
163 | # * the main class name
164 | # * -classpath
165 | # * -D...appname settings
166 | # * --module-path (only if needed)
167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
168 |
169 | # For Cygwin or MSYS, switch paths to Windows format before running java
170 | if "$cygwin" || "$msys" ; then
171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
173 |
174 | JAVACMD=$( cygpath --unix "$JAVACMD" )
175 |
176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
177 | for arg do
178 | if
179 | case $arg in #(
180 | -*) false ;; # don't mess with options #(
181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
182 | [ -e "$t" ] ;; #(
183 | *) false ;;
184 | esac
185 | then
186 | arg=$( cygpath --path --ignore --mixed "$arg" )
187 | fi
188 | # Roll the args list around exactly as many times as the number of
189 | # args, so each arg winds up back in the position where it started, but
190 | # possibly modified.
191 | #
192 | # NB: a `for` loop captures its iteration list before it begins, so
193 | # changing the positional parameters here affects neither the number of
194 | # iterations, nor the values presented in `arg`.
195 | shift # remove old arg
196 | set -- "$@" "$arg" # push replacement arg
197 | done
198 | fi
199 |
200 | # Collect all arguments for the java command;
201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
202 | # shell script including quotes and variable substitutions, so put them in
203 | # double quotes to make sure that they get re-expanded; and
204 | # * put everything else in single quotes, so that it's not re-expanded.
205 |
206 | set -- \
207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
208 | -classpath "$CLASSPATH" \
209 | org.gradle.wrapper.GradleWrapperMain \
210 | "$@"
211 |
212 | # Stop when "xargs" is not available.
213 | if ! command -v xargs >/dev/null 2>&1
214 | then
215 | die "xargs is not available"
216 | fi
217 |
218 | # Use "xargs" to parse quoted args.
219 | #
220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
221 | #
222 | # In Bash we could simply go:
223 | #
224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
225 | # set -- "${ARGS[@]}" "$@"
226 | #
227 | # but POSIX shell has neither arrays nor command substitution, so instead we
228 | # post-process each arg (as a line of input to sed) to backslash-escape any
229 | # character that might be a shell metacharacter, then use eval to reverse
230 | # that process (while maintaining the separation between arguments), and wrap
231 | # the whole thing up as a single "set" statement.
232 | #
233 | # This will of course break if any of these variables contains a newline or
234 | # an unmatched quote.
235 | #
236 |
237 | eval "set -- $(
238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
239 | xargs -n1 |
240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
241 | tr '\n' ' '
242 | )" '"$@"'
243 |
244 | exec "$JAVACMD" "$@"
245 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("kotest-conventions")
3 | }
4 |
5 | kotlin {
6 | sourceSets {
7 | commonMain {
8 | dependencies {
9 | compileOnly(libs.arrow.fx.coroutines)
10 | api(projects.kotestAssertionsArrow)
11 | api(libs.kotlinx.coroutines.core)
12 | implementation(libs.kotest.assertions.core)
13 | implementation(libs.kotest.framework.engine)
14 | implementation(libs.kotest.property)
15 | }
16 | }
17 |
18 | commonTest {
19 | dependencies {
20 | implementation(libs.arrow.fx.coroutines)
21 | }
22 | }
23 |
24 | jsMain {
25 | dependencies {
26 | api(libs.arrow.fx.coroutines)
27 | }
28 | }
29 |
30 | nativeMain {
31 | dependencies {
32 | implementation(libs.arrow.fx.coroutines)
33 | }
34 | }
35 | }
36 | }
37 |
38 | apply(from = "../publish-mpp.gradle.kts")
39 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.native.binary.memoryModel=experimental
2 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonMain/kotlin/io/kotest/assertions/arrow/fx/coroutines/ExitCase.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import arrow.fx.coroutines.ExitCase
4 | import kotlin.contracts.ExperimentalContracts
5 | import kotlin.contracts.contract
6 | import kotlinx.coroutines.CancellationException
7 |
8 | @OptIn(ExperimentalContracts::class)
9 | public fun ExitCase.shouldBeCancelled(
10 | failureMessage: (ExitCase) -> String = { "Expected ExitCase.Cancelled, but found $it" }
11 | ): ExitCase.Cancelled {
12 | contract {
13 | returns() implies (this@shouldBeCancelled is ExitCase.Cancelled)
14 | }
15 | return when (this) {
16 | is ExitCase.Completed -> throw AssertionError(failureMessage(this))
17 | is ExitCase.Cancelled -> this
18 | is ExitCase.Failure -> throw AssertionError(failureMessage(this))
19 | }
20 | }
21 |
22 | @OptIn(ExperimentalContracts::class)
23 | public fun ExitCase.shouldBeCancelled(cancelled: CancellationException): ExitCase.Cancelled {
24 | contract {
25 | returns() implies (this@shouldBeCancelled is ExitCase.Cancelled)
26 | }
27 | return shouldBeCancelled().also {
28 | exception shouldBe cancelled
29 | }
30 | }
31 |
32 | @OptIn(ExperimentalContracts::class)
33 | public fun ExitCase.shouldBeCompleted(
34 | failureMessage: (ExitCase) -> String = { "Expected ExitCase.Completed, but found $it" }
35 | ): ExitCase.Completed {
36 | contract {
37 | returns() implies (this@shouldBeCompleted is ExitCase.Completed)
38 | }
39 | return when (this) {
40 | is ExitCase.Completed -> this
41 | is ExitCase.Cancelled -> throw AssertionError(failureMessage(this))
42 | is ExitCase.Failure -> throw AssertionError(failureMessage(this))
43 | }
44 | }
45 |
46 | @OptIn(ExperimentalContracts::class)
47 | public fun ExitCase.shouldBeFailure(
48 | failureMessage: (ExitCase) -> String = { "Expected ExitCase.Failure, but found $it" }
49 | ): ExitCase.Failure {
50 | contract {
51 | returns() implies (this@shouldBeFailure is ExitCase.Failure)
52 | }
53 | return when (this) {
54 | is ExitCase.Completed -> throw AssertionError(failureMessage(this))
55 | is ExitCase.Cancelled -> throw AssertionError(failureMessage(this))
56 | is ExitCase.Failure -> this
57 | }
58 | }
59 |
60 | @OptIn(ExperimentalContracts::class)
61 | public fun ExitCase.shouldBeFailure(throwable: Throwable): ExitCase.Failure {
62 | contract {
63 | returns() implies (this@shouldBeFailure is ExitCase.Failure)
64 | }
65 | return shouldBeFailure().also {
66 | failure shouldBe throwable
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonMain/kotlin/io/kotest/assertions/arrow/fx/coroutines/ProjectResource.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import arrow.fx.coroutines.Resource
4 | import io.kotest.core.extensions.LazyMaterialized
5 | import io.kotest.core.listeners.ProjectListener
6 |
7 | /**
8 | * Useful to register [Resource] on a Project wide configuration.
9 | *
10 | * For example:
11 | *
12 | * ```kotlin
13 | * object ProjectConfig : AbstractProjectConfig() {
14 | * val hikari: ProjectResource =
15 | * ProjectResource(Resource.fromCloseable { HikariDataSource(...) })
16 | *
17 | * override fun extensions(): List = listOf(hikari)
18 | * }
19 | *
20 | * class MySpec : StringSpec({
21 | * "my test" {
22 | * val dataSource: HikariDataSource = ProjectConfig.hikari.get()
23 | * }
24 | * })
25 | * ```
26 | */
27 | public class ProjectResource private constructor(
28 | public val resource: Resource,
29 | private val default: ResourceLazyMaterialized
30 | ) : ProjectListener, LazyMaterialized by default {
31 |
32 | public constructor(resource: Resource) : this(resource, ResourceLazyMaterialized(resource))
33 |
34 | override suspend fun beforeProject() {
35 | default.init()
36 | }
37 |
38 | override suspend fun afterProject() {
39 | default.release()
40 | default.close()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonMain/kotlin/io/kotest/assertions/arrow/fx/coroutines/Resource.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import arrow.fx.coroutines.Resource
4 | import arrow.fx.coroutines.resourceScope
5 |
6 | public suspend infix fun Resource.shouldBeResource(a: A): A =
7 | resourceScope {
8 | bind() shouldBe a
9 | }
10 |
11 | public suspend infix fun Resource.shouldBeResource(expected: Resource): A =
12 | resourceScope {
13 | bind() shouldBe expected.bind()
14 | }
15 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonMain/kotlin/io/kotest/assertions/arrow/fx/coroutines/ResourceExtension.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import arrow.fx.coroutines.*
4 | import io.kotest.core.extensions.LazyMaterialized
5 | import io.kotest.core.extensions.LazyMountableExtension
6 | import io.kotest.core.listeners.AfterSpecListener
7 | import io.kotest.core.listeners.TestListener
8 | import io.kotest.core.spec.Spec
9 | import io.kotest.core.test.TestCase
10 | import io.kotest.core.test.TestResult
11 | import io.kotest.core.test.TestType
12 | import io.kotest.core.test.isRootTest
13 | import io.kotest.mpp.atomics.AtomicReference
14 | import kotlinx.coroutines.CompletableDeferred
15 | import kotlinx.coroutines.DelicateCoroutinesApi
16 |
17 | public enum class LifecycleMode {
18 | Spec, EveryTest, Leaf, Root
19 | }
20 |
21 | /** Turns a [Resource] into a [LazyMountableExtension] as a [ResourceExtension] */
22 | public fun Resource.extension(lifecycleMode: LifecycleMode = LifecycleMode.Spec): ResourceExtension =
23 | ResourceExtension(this, lifecycleMode)
24 |
25 | /**
26 | * Ensures that all finalizers of [resource] are invoked for the specified [LifecycleMode].
27 | * Installing this [LazyMountableExtension] returns a [LazyMaterialized]
28 | * from which you can extract initialized [A] using [LazyMaterialized.get].
29 | *
30 | * ```kotlin
31 | * class Connection(val name: String) {
32 | * fun open() = println("Open $name")
33 | * fun compute() = "Result of $name"
34 | * fun release(ex: ExitCase) = println("Closing $name with $ex")
35 | * }
36 | *
37 | * fun connection(name: String): Resource =
38 | * Resource({ Connection(name).also { it.open() } }, { conn, ex -> conn.release(ex) })
39 | *
40 | * class ResourceSpec : StringSpec({
41 | * val conn: TestResource =
42 | * install(ResourceExtension(connection("DB")))
43 | *
44 | * "get" {
45 | * conn.get().compute() shouldBe "Result of DB"
46 | * }
47 | *
48 | * afterSpec {
49 | * shouldThrow {
50 | * conn.get()
51 | * }.message shouldBe "Resource already closed and cannot be re-opened."
52 | * }
53 | * })
54 | * ```
55 | *
56 | * `conn` cannot be used during or after `afterSpec` since at that point the resource is already released,
57 | * and it will result in [IllegalStateException]
58 | *
59 | * @param lifecycleMode allows installing the [Resource] for other scopes besides the default [LifecycleMode.Spec].
60 | */
61 | public class ResourceExtension(
62 | public val resource: Resource, public val lifecycleMode: LifecycleMode = LifecycleMode.Spec,
63 | ) : LazyMountableExtension, TestListener, AfterSpecListener {
64 |
65 | private var underlying: ResourceLazyMaterialized? = null
66 |
67 | override fun mount(configure: A.() -> Unit): LazyMaterialized =
68 | ResourceLazyMaterialized(resource, configure).also {
69 | underlying = it
70 | }
71 |
72 | override suspend fun beforeSpec(spec: Spec) {
73 | super.beforeSpec(spec)
74 | if (lifecycleMode == LifecycleMode.Spec) {
75 | underlying?.init()
76 | }
77 | }
78 |
79 | override suspend fun afterSpec(spec: Spec) {
80 | if (lifecycleMode == LifecycleMode.Spec) {
81 | underlying?.release()
82 | }
83 | underlying?.close()
84 | }
85 |
86 | override suspend fun beforeAny(testCase: TestCase) {
87 | val every = lifecycleMode == LifecycleMode.EveryTest
88 | val root = lifecycleMode == LifecycleMode.Root && testCase.isRootTest()
89 | val leaf = lifecycleMode == LifecycleMode.Leaf && testCase.type == TestType.Test
90 | if (every || root || leaf) {
91 | underlying?.init()
92 | }
93 | }
94 |
95 | override suspend fun afterAny(testCase: TestCase, result: TestResult) {
96 | val every = lifecycleMode == LifecycleMode.EveryTest
97 | val root = lifecycleMode == LifecycleMode.Root && testCase.isRootTest()
98 | val leaf = lifecycleMode == LifecycleMode.Leaf && testCase.type == TestType.Test
99 | if (every || root || leaf) {
100 | underlying?.release()
101 | }
102 | }
103 | }
104 |
105 | internal class ResourceLazyMaterialized(
106 | private val resource: Resource,
107 | private val configure: A.() -> Unit = {},
108 | ) : LazyMaterialized {
109 |
110 | sealed interface State {
111 | data object Empty : State
112 | data object Closed : State
113 | data class Loading(
114 | val acquiring: CompletableDeferred = CompletableDeferred(),
115 | val finalizers: CompletableDeferred Unit> = CompletableDeferred(),
116 | ) : State
117 |
118 | data class Done(val value: A, val finalizers: suspend (ExitCase) -> Unit) : State
119 | }
120 |
121 | private val state = AtomicReference>(State.Empty)
122 |
123 | override suspend fun get(): A = init()
124 |
125 | @OptIn(DelicateCoroutinesApi::class)
126 | tailrec suspend fun init(): A = when (val current = state.value) {
127 | is State.Done -> current.value
128 | is State.Loading -> current.acquiring.await()
129 | is State.Closed -> throw IllegalStateException("Resource already closed and cannot be re-opened.")
130 | State.Empty -> {
131 | val loading = State.Loading()
132 | if (state.compareAndSet(State.Empty, loading)) {
133 | val (res, fin) = resource.allocate()
134 | .let { (a, finalizer) ->
135 | @Suppress("NAME_SHADOWING")
136 | val finalizer: suspend (ExitCase) -> Unit = { finalizer(it) }
137 | Pair(a, finalizer)
138 | }
139 | state.value = State.Done(res, fin)
140 | loading.acquiring.complete(res.also(configure))
141 | loading.finalizers.complete(fin)
142 | res
143 | } else init()
144 | }
145 | }
146 |
147 | tailrec suspend fun release(): Unit = when (val current = state.value) {
148 | State.Empty -> Unit
149 | is State.Done -> if (state.compareAndSet(current, State.Empty)) current.finalizers(ExitCase.Completed)
150 | else release()
151 |
152 | is State.Loading -> if (state.compareAndSet(current, State.Empty)) current.finalizers.await()
153 | .invoke(ExitCase.Completed)
154 | else release()
155 |
156 | State.Closed -> Unit
157 | }
158 |
159 | fun close() {
160 | state.value = State.Closed
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonMain/kotlin/io/kotest/assertions/arrow/fx/coroutines/shouldBe.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import io.kotest.matchers.shouldBe as coreShouldBe
4 |
5 | internal infix fun A.shouldBe(a: A): A =
6 | also {
7 | this coreShouldBe a
8 | }
9 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonTest/kotlin/io/kotest/assertions/arrow/fx/coroutines/ExitCaseTest.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import arrow.fx.coroutines.ExitCase
4 | import io.kotest.core.spec.style.StringSpec
5 | import io.kotest.property.Arb
6 | import io.kotest.property.arbitrary.map
7 | import io.kotest.property.arbitrary.string
8 | import io.kotest.property.checkAll
9 | import kotlinx.coroutines.CancellationException
10 |
11 | class ExitCaseTest : StringSpec({
12 | "shouldBeCancelled" {
13 | ExitCase.Cancelled(CancellationException("")).shouldBeCancelled()
14 | }
15 |
16 | "shouldBeCancelled(e)" {
17 | checkAll(Arb.string().map { CancellationException(it) }) { e ->
18 | ExitCase.Cancelled(e).shouldBeCancelled(e)
19 | }
20 | }
21 |
22 | "shouldBeCompleted" {
23 | ExitCase.Completed.shouldBeCompleted()
24 | }
25 |
26 | "shouldBeFailure" {
27 | checkAll(Arb.string().map(::RuntimeException)) { e ->
28 | ExitCase.Failure(e).shouldBeFailure()
29 | }
30 | }
31 |
32 | "shouldBeFailure(e)" {
33 | checkAll(Arb.string().map(::RuntimeException)) { e ->
34 | ExitCase.Failure(e).shouldBeFailure(e)
35 | }
36 | }
37 | })
38 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonTest/kotlin/io/kotest/assertions/arrow/fx/coroutines/ResourceExtensionLeafTest.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import io.kotest.core.extensions.install
4 | import io.kotest.core.spec.style.DescribeSpec
5 |
6 | class ResourceExtensionLeafTest : DescribeSpec({
7 | val sub = ResourceUnderTest()
8 |
9 | install(ResourceExtension(sub.asResource(), LifecycleMode.Leaf)) {
10 | configure()
11 | }
12 |
13 | describe("context") {
14 | sub.isOpen shouldBe false
15 | sub.isConfigured shouldBe false
16 | sub.count shouldBe 0
17 |
18 | it("should initialize per leaf") {
19 | sub.isOpen shouldBe true
20 | sub.isConfigured shouldBe true
21 | sub.count shouldBe 1
22 | }
23 |
24 | it("this test should have a new container") {
25 | sub.isConfigured shouldBe true
26 | sub.count shouldBe 2
27 | }
28 | }
29 | })
30 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonTest/kotlin/io/kotest/assertions/arrow/fx/coroutines/ResourceExtensionRootTest.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import io.kotest.core.extensions.install
4 | import io.kotest.core.spec.style.FunSpec
5 |
6 | class ResourceExtensionRootTest : FunSpec({
7 | val sub = ResourceUnderTest()
8 |
9 | install(ResourceExtension(sub.asResource(), LifecycleMode.Root)) {
10 | configure()
11 | }
12 |
13 | beforeSpec {
14 | sub.count shouldBe 0
15 | sub.isConfigured shouldBe false
16 | sub.isOpen shouldBe false
17 | sub.isReleased shouldBe false
18 | }
19 |
20 | context("initializes once for container") {
21 | test("should initialize per root") {
22 | sub.count shouldBe 1
23 | sub.isConfigured shouldBe true
24 | sub.isOpen shouldBe true
25 | sub.isReleased shouldBe false
26 | }
27 |
28 | test("should not have re-initialized") {
29 | sub.count shouldBe 1
30 | }
31 | }
32 |
33 | test("this root test should have a different container") {
34 | sub.count shouldBe 2
35 | sub.isConfigured shouldBe true
36 | sub.isOpen shouldBe true
37 | sub.isReleased shouldBe false
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonTest/kotlin/io/kotest/assertions/arrow/fx/coroutines/ResourceExtensionSpecTest.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import io.kotest.assertions.throwables.shouldThrow
4 | import io.kotest.core.extensions.install
5 | import io.kotest.core.spec.style.FunSpec
6 |
7 | class ResourceExtensionSpecTest : FunSpec({
8 |
9 | val sub = ResourceUnderTest()
10 |
11 | val res = install(sub.asResource().extension()) {
12 | configure()
13 | }
14 |
15 | test("acquired but not released") {
16 | res.get() shouldBe sub
17 | sub.isOpen shouldBe true
18 | sub.isConfigured shouldBe true
19 | }
20 |
21 | afterSpec {
22 | sub.isReleased shouldBe true
23 | shouldThrow {
24 | res.get()
25 | }
26 | }
27 | })
28 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow-fx-coroutines/src/commonTest/kotlin/io/kotest/assertions/arrow/fx/coroutines/ResourceUnderTest.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.fx.coroutines
2 |
3 | import arrow.fx.coroutines.Resource
4 | import arrow.fx.coroutines.resource
5 |
6 | class ResourceUnderTest {
7 | var count: Int = 0
8 | var isOpen: Boolean = false
9 | private set
10 | var isReleased: Boolean = false
11 | private set
12 | var isConfigured: Boolean = false
13 | private set
14 |
15 | fun configure(): Unit {
16 | isConfigured = true
17 | }
18 |
19 | fun asResource(): Resource = resource {
20 | install(
21 | acquire = {
22 | isReleased = false
23 | isOpen = true
24 | count++
25 | this@ResourceUnderTest
26 | },
27 | release = { res, _ ->
28 | res.isOpen = false
29 | res.isConfigured = false
30 | res.isReleased = true
31 | }
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("kotest-conventions")
3 | }
4 |
5 | kotlin {
6 | sourceSets {
7 | commonMain {
8 | dependencies {
9 | compileOnly(libs.arrow.core)
10 | implementation(libs.kotest.assertions.core)
11 | }
12 | }
13 |
14 | commonTest {
15 | dependencies {
16 | implementation(libs.arrow.core)
17 | implementation(libs.kotest.framework.engine)
18 | implementation(libs.kotest.property)
19 | }
20 | }
21 |
22 | jsMain {
23 | dependencies {
24 | api(libs.arrow.core)
25 | }
26 | }
27 |
28 | nativeMain {
29 | dependencies {
30 | implementation(libs.arrow.core)
31 | }
32 | }
33 | }
34 | }
35 |
36 | apply(from = "../publish-mpp.gradle.kts")
37 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/Utils.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow
2 |
3 | import io.kotest.matchers.shouldBe as coreShouldBe
4 | import io.kotest.matchers.shouldNotBe as coreShouldNotBe
5 |
6 | internal infix fun A.shouldBe(a: A): A {
7 | this coreShouldBe a
8 | return this
9 | }
10 |
11 |
12 | internal infix fun A.shouldNotBe(a: A?): A {
13 | this coreShouldNotBe a
14 | return this
15 | }
16 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/core/Either.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.Either
4 | import arrow.core.Either.Left
5 | import arrow.core.Either.Right
6 | import arrow.core.identity
7 | import io.kotest.assertions.arrow.shouldBe
8 | import io.kotest.assertions.arrow.shouldNotBe
9 | import kotlin.contracts.ExperimentalContracts
10 | import kotlin.contracts.contract
11 |
12 | /**
13 | * smart casts to [Either.Right] and fails with [failureMessage] otherwise.
14 | * ```kotlin
15 | * import arrow.core.Either.Right
16 | * import arrow.core.Either
17 | * import shouldBeRight
18 | *
19 | * fun main() {
20 | * //sampleStart
21 | * fun squared(i: Int): Int = i * i
22 | * val result = squared(5)
23 | * val either = Either.conditionally(result == 25, { result }, { 25 })
24 | * val a = either.shouldBeRight { "5 * 5 == 25 but was $it " }
25 | * val smartCasted: Right = either
26 | * //sampleEnd
27 | * println(smartCasted)
28 | * }
29 | * ```
30 | */
31 | @OptIn(ExperimentalContracts::class)
32 | public fun Either.shouldBeRight(failureMessage: (A) -> String = { "Expected Either.Right, but found Either.Left with value $it" }): B {
33 | contract {
34 | returns() implies (this@shouldBeRight is Right)
35 | }
36 | return when (this) {
37 | is Right -> value
38 | is Left -> throw AssertionError(failureMessage(value))
39 | }
40 | }
41 |
42 | public infix fun Either.shouldBeRight(b: B): B =
43 | shouldBeRight().shouldBe(b)
44 |
45 | public infix fun Either.shouldNotBeRight(b: B): B =
46 | shouldBeRight().shouldNotBe(b)
47 |
48 | /**
49 | * smart casts to [Either.Left] and fails with [failureMessage] otherwise.
50 | * ```kotlin
51 | * import arrow.core.Either.Left
52 | * import arrow.core.Either
53 | * import shouldBeLeft
54 | *
55 | * fun main() {
56 | * //sampleStart
57 | * val either = Either.conditionally(false, { "Always false" }, { throw RuntimeException("Will never execute") })
58 | * val a = either.shouldBeLeft()
59 | * val smartCasted: Left = either
60 | * //sampleEnd
61 | * println(smartCasted)
62 | * }
63 | * ```
64 | */
65 | @OptIn(ExperimentalContracts::class)
66 | public fun Either.shouldBeLeft(failureMessage: (B) -> String = { "Expected Either.Left, but found Either.Right with value $it" }): A {
67 | contract {
68 | returns() implies (this@shouldBeLeft is Left)
69 | }
70 | return when (this) {
71 | is Left -> value
72 | is Right -> throw AssertionError(failureMessage(value))
73 | }
74 | }
75 |
76 | public infix fun Either.shouldBeLeft(a: A): A =
77 | shouldBeLeft().shouldBe(a)
78 |
79 | public infix fun Either.shouldNotBeLeft(a: A): A =
80 | shouldBeLeft().shouldNotBe(a)
81 |
82 | /** for testing success & error scenarios with an [Either] generator **/
83 | public fun Either.rethrow(): A =
84 | fold({ throw it }, ::identity)
85 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/core/Ior.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.Ior
4 | import arrow.core.Ior.Left
5 | import arrow.core.Ior.Right
6 | import arrow.core.Ior.Both
7 | import arrow.core.identity
8 | import io.kotest.assertions.arrow.shouldBe
9 | import io.kotest.assertions.arrow.shouldNotBe
10 | import kotlin.contracts.ExperimentalContracts
11 | import kotlin.contracts.contract
12 | import io.kotest.matchers.Matcher
13 | import io.kotest.matchers.MatcherResult
14 | import io.kotest.matchers.should
15 | import io.kotest.matchers.shouldNot
16 |
17 | /**
18 | * smart casts to [Ior.Right] and fails with [failureMessage] otherwise.
19 | */
20 | @OptIn(ExperimentalContracts::class)
21 | public fun Ior.shouldBeRight(failureMessage: (Ior) -> String = { "Expected Ior.Right, but found ${it::class.simpleName}" }): B {
22 | contract {
23 | returns() implies (this@shouldBeRight is Right)
24 | }
25 | return when (this) {
26 | is Right -> value
27 | else -> throw AssertionError(failureMessage(this))
28 | }
29 | }
30 |
31 | public infix fun Ior.shouldBeRight(b: B): B =
32 | shouldBeRight().shouldBe(b)
33 |
34 | public infix fun Ior.shouldNotBeRight(b: B): B =
35 | shouldBeRight().shouldNotBe(b)
36 |
37 | /**
38 | * smart casts to [Ior.Left] and fails with [failureMessage] otherwise.
39 | */
40 | @OptIn(ExperimentalContracts::class)
41 | public fun Ior.shouldBeLeft(failureMessage: (Ior) -> String = { "Expected Ior.Left, but found ${it::class.simpleName}" }): A {
42 | contract {
43 | returns() implies (this@shouldBeLeft is Left)
44 | }
45 | return when (this) {
46 | is Left -> value
47 | else -> throw AssertionError(failureMessage(this))
48 | }
49 | }
50 |
51 | public infix fun Ior.shouldBeLeft(a: A): A =
52 | shouldBeLeft().shouldBe(a)
53 |
54 | public infix fun Ior.shouldNotBeLeft(a: A): A =
55 | shouldBeLeft().shouldNotBe(a)
56 |
57 | public fun beBoth(): Matcher> = Matcher {
58 | ior ->
59 | MatcherResult(
60 | ior is Both,
61 | { "Expected ior to be a Both, but was: ${ior::class.simpleName}" },
62 | { "Expected ior to not be a Both." }
63 | )
64 | }
65 |
66 | /**
67 | * smart casts to [Ior.Both]
68 | */
69 | @OptIn(ExperimentalContracts::class)
70 | public fun Ior.shouldBeBoth(): Ior.Both {
71 | contract {
72 | returns() implies (this@shouldBeBoth is Ior.Both)
73 | }
74 |
75 | this should beBoth()
76 |
77 | return this as Ior.Both
78 | }
79 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/core/Nel.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.NonEmptyList
4 | import io.kotest.matchers.collections.shouldBeSorted
5 | import io.kotest.matchers.collections.shouldNotBeSorted
6 | import io.kotest.matchers.collections.shouldBeUnique
7 | import io.kotest.matchers.collections.shouldContain
8 | import io.kotest.matchers.collections.shouldContainAll
9 | import io.kotest.matchers.collections.shouldContainDuplicates
10 | import io.kotest.matchers.collections.shouldContainNoNulls
11 | import io.kotest.matchers.collections.shouldContainNull
12 | import io.kotest.matchers.collections.shouldContainOnlyNulls
13 | import io.kotest.matchers.collections.shouldHaveElementAt
14 | import io.kotest.matchers.collections.shouldHaveSingleElement
15 | import io.kotest.matchers.collections.shouldHaveSize
16 | import io.kotest.matchers.collections.shouldNotHaveSize
17 | import io.kotest.matchers.collections.shouldNotBeUnique
18 | import io.kotest.matchers.collections.shouldNotContain
19 | import io.kotest.matchers.collections.shouldNotContainAll
20 | import io.kotest.matchers.collections.shouldNotContainDuplicates
21 | import io.kotest.matchers.collections.shouldNotContainNoNulls
22 | import io.kotest.matchers.collections.shouldNotContainNull
23 | import io.kotest.matchers.collections.shouldNotContainOnlyNulls
24 | import io.kotest.matchers.collections.shouldNotHaveElementAt
25 | import io.kotest.matchers.collections.shouldNotHaveSingleElement
26 |
27 | public fun NonEmptyList.shouldContainOnlyNulls(): NonEmptyList =
28 | apply { all.shouldContainOnlyNulls() }
29 |
30 | public fun NonEmptyList.shouldNotContainOnlyNulls(): NonEmptyList =
31 | apply { all.shouldNotContainOnlyNulls() }
32 |
33 | public fun NonEmptyList.shouldContainNull(): NonEmptyList =
34 | apply { all.shouldContainNull() }
35 |
36 | public fun NonEmptyList.shouldNotContainNull(): NonEmptyList =
37 | apply { all.shouldNotContainNull() }
38 |
39 | public fun NonEmptyList.shouldHaveElementAt(index: Int, element: A): Unit =
40 | all.shouldHaveElementAt(index, element)
41 |
42 | public fun NonEmptyList.shouldNotHaveElementAt(index: Int, element: A): Unit =
43 | all.shouldNotHaveElementAt(index, element)
44 |
45 | public fun NonEmptyList.shouldContainNoNulls(): NonEmptyList =
46 | apply { all.shouldContainNoNulls() }
47 |
48 | public fun NonEmptyList.shouldNotContainNoNulls(): NonEmptyList =
49 | apply { all.shouldNotContainNoNulls() }
50 |
51 | public infix fun NonEmptyList.shouldContain(a: A): Unit {
52 | all.shouldContain(a)
53 | }
54 |
55 | public infix fun NonEmptyList.shouldNotContain(a: A): Unit {
56 | all.shouldNotContain(a)
57 | }
58 |
59 | public fun NonEmptyList.shouldBeUnique(): NonEmptyList =
60 | apply { all.shouldBeUnique() }
61 |
62 | public fun NonEmptyList.shouldNotBeUnique(): NonEmptyList =
63 | apply { all.shouldNotBeUnique() }
64 |
65 | public fun NonEmptyList.shouldContainDuplicates(): NonEmptyList =
66 | apply { all.shouldContainDuplicates() }
67 |
68 | public fun NonEmptyList.shouldNotContainDuplicates(): NonEmptyList =
69 | apply { all.shouldNotContainDuplicates() }
70 |
71 | public fun NonEmptyList.shouldContainAll(vararg ts: A): Unit =
72 | all.shouldContainAll(*ts)
73 |
74 | public fun NonEmptyList.shouldNotContainAll(vararg ts: A): Unit =
75 | all.shouldNotContainAll(*ts)
76 |
77 | public infix fun NonEmptyList.shouldContainAll(ts: List): Unit =
78 | all.shouldContainAll(ts)
79 |
80 | public infix fun NonEmptyList.shouldNotContainAll(ts: List): Unit =
81 | all.shouldNotContainAll(ts)
82 |
83 | public infix fun NonEmptyList.shouldHaveSize(size: Int): NonEmptyList =
84 | apply { all.shouldHaveSize(size) }
85 |
86 | public infix fun NonEmptyList.shouldNotHaveSize(size: Int): NonEmptyList =
87 | apply { all.shouldNotHaveSize(size) }
88 |
89 | public infix fun NonEmptyList.shouldHaveSingleElement(a: A): Unit =
90 | all.shouldHaveSingleElement(a)
91 |
92 | public infix fun NonEmptyList.shouldNotHaveSingleElement(a: A): Unit =
93 | all.shouldNotHaveSingleElement(a)
94 |
95 | public fun > NonEmptyList.shouldBeSorted(): NonEmptyList =
96 | apply { all.shouldBeSorted() }
97 |
98 | public fun > NonEmptyList.shouldNotBeSorted(): NonEmptyList =
99 | apply { all.shouldNotBeSorted() }
100 |
101 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/core/Nelnspectors.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.NonEmptyList
4 | import io.kotest.inspectors.forAll
5 | import io.kotest.inspectors.forAny
6 | import io.kotest.inspectors.forAtLeast
7 | import io.kotest.inspectors.forAtLeastOne
8 | import io.kotest.inspectors.forAtMost
9 | import io.kotest.inspectors.forAtMostOne
10 | import io.kotest.inspectors.forExactly
11 | import io.kotest.inspectors.forNone
12 | import io.kotest.inspectors.forOne
13 | import io.kotest.inspectors.forSome
14 |
15 | @Deprecated(
16 | "use Kotest's Collection.forAll.",
17 | ReplaceWith("forAll", "io.kotest.inspectors.forAll")
18 | )
19 | public fun NonEmptyList.forAll(f: (A) -> Unit): Unit {
20 | all.forAll(f)
21 | }
22 |
23 | @Deprecated(
24 | "use Kotest's Collection.forOne.",
25 | ReplaceWith("forOne", "io.kotest.inspectors.forOne")
26 | )
27 | public fun NonEmptyList.forOne(f: (A) -> Unit): Unit {
28 | all.forOne(f)
29 | }
30 |
31 | @Deprecated(
32 | "use Kotest's Collection.forExactly.",
33 | ReplaceWith("forExactly", "io.kotest.inspectors.forExactly")
34 | )
35 | public fun NonEmptyList.forExactly(k: Int, f: (A) -> Unit): Unit {
36 | all.forExactly(k, f)
37 | }
38 |
39 | @Deprecated(
40 | "use Kotest's Collection.forSome.",
41 | ReplaceWith("forSome", "io.kotest.inspectors.forSome")
42 | )
43 | public fun NonEmptyList.forSome(f: (A) -> Unit): Unit {
44 | all.forSome(f)
45 | }
46 |
47 | @Deprecated(
48 | "use Kotest's Collection.forAny.",
49 | ReplaceWith("forAny", "io.kotest.inspectors.forAny")
50 | )
51 | public fun NonEmptyList.forAny(f: (A) -> Unit): Unit {
52 | all.forAny(f)
53 | }
54 |
55 | @Deprecated(
56 | "use Kotest's Collection.forAtLeastOne.",
57 | ReplaceWith("forAtLeastOne", "io.kotest.inspectors.forAtLeastOne")
58 | )
59 | public fun NonEmptyList.forAtLeastOne(f: (A) -> Unit): Unit {
60 | all.forAtLeastOne(f)
61 | }
62 |
63 | @Deprecated(
64 | "use Kotest's Collection.forAtLeast.",
65 | ReplaceWith("forAtLeast", "io.kotest.inspectors.forAtLeast")
66 | )
67 | public fun NonEmptyList.forAtLeast(k: Int, f: (A) -> Unit): Unit {
68 | all.forAtLeast(k, f)
69 | }
70 |
71 | @Deprecated(
72 | "use Kotest's Collection.forAtMostOne.",
73 | ReplaceWith("forAtMostOne", "io.kotest.inspectors.forAtMostOne")
74 | )
75 | public fun NonEmptyList.forAtMostOne(f: (A) -> Unit): Unit {
76 | all.forAtMostOne(f)
77 | }
78 |
79 | @Deprecated(
80 | "use Kotest's Collection.forAtMost.",
81 | ReplaceWith("forAtMost", "io.kotest.inspectors.forAtMost")
82 | )
83 | public fun NonEmptyList.forAtMost(k: Int, f: (A) -> Unit): Unit {
84 | all.forAtMost(k, f)
85 | }
86 |
87 | @Deprecated(
88 | "use Kotest's Collection.forNone.",
89 | ReplaceWith("forNone", "io.kotest.inspectors.forNone")
90 | )
91 | public fun NonEmptyList.forNone(f: (A) -> Unit): Unit {
92 | all.forNone(f)
93 | }
94 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/core/Option.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.None
4 | import arrow.core.Option
5 | import arrow.core.Some
6 | import io.kotest.assertions.arrow.shouldBe
7 | import io.kotest.assertions.arrow.shouldNotBe
8 | import kotlin.contracts.ExperimentalContracts
9 | import kotlin.contracts.contract
10 |
11 | /**
12 | * smart casts to [Some] and fails with [failureMessage] otherwise.
13 | * ```kotlin
14 | * import arrow.core.Option
15 | * import arrow.core.Some
16 | * import shouldBeSome
17 | *
18 | * fun main() {
19 | * //sampleStart
20 | * val list = listOf("4", "5", "6")
21 | * val option = Option.fromNullable(list.getOrNull(2))
22 | * val element = option.shouldBeSome()
23 | * val smartCasted: Some = option
24 | * //sampleEnd
25 | * println(smartCasted)
26 | * }
27 | * ```
28 | */
29 | @OptIn(ExperimentalContracts::class)
30 | public fun Option.shouldBeSome(failureMessage: () -> String = { "Expected Some, but found None" }): A {
31 | contract {
32 | returns() implies (this@shouldBeSome is Some)
33 | }
34 | return when (this) {
35 | None -> throw AssertionError(failureMessage())
36 | is Some -> value
37 | }
38 | }
39 |
40 | public infix fun Option.shouldBeSome(a: A): A =
41 | shouldBeSome().shouldBe(a)
42 |
43 | public infix fun Option.shouldNotBeSome(a: A): A =
44 | shouldBeSome().shouldNotBe(a)
45 |
46 | /**
47 | * smart casts to [None] and fails with [failureMessage] otherwise.
48 | * ```kotlin
49 | * import arrow.core.Option
50 | * import arrow.core.None
51 | * import shouldBeNone
52 | *
53 | * fun main() {
54 | * //sampleStart
55 | * val list = listOf("4", "5", "6")
56 | * val option = Option.fromNullable(list.getOrNull(5))
57 | * val element = option.shouldBeNone()
58 | * val smartCasted: None = option
59 | * //sampleEnd
60 | * println(smartCasted)
61 | * }
62 | * ```
63 | */
64 | @OptIn(ExperimentalContracts::class)
65 | public fun Option.shouldBeNone(failureMessage: (Some) -> String = { "Expected None, but found Some with value ${it.value}" }): None {
66 | contract {
67 | returns() implies (this@shouldBeNone is None)
68 | }
69 | return when (this) {
70 | None -> None
71 | is Some -> throw AssertionError(failureMessage(this))
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonMain/kotlin/io/kotest/assertions/arrow/core/Raise.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.raise.Raise
4 | import arrow.core.raise.recover
5 | import io.kotest.assertions.failure
6 | import io.kotest.assertions.print.print
7 |
8 | /**
9 | * Verifies if a block of code will raise a specified type of [T] (or subclasses).
10 | *
11 | * This function will include subclasses of [T]. For example, if you test for [CharSequence] and the code raises
12 | * [String], the test will pass.
13 | *
14 | * ```kotlin
15 | * val raised: String = shouldRaise {
16 | * raise("failed")
17 | * }
18 | * ```
19 | */
20 | public inline fun shouldRaise(block: Raise.() -> Any?): T {
21 | val expectedRaiseClass = T::class
22 | return recover({
23 | block(this)
24 | throw failure("Expected to raise ${expectedRaiseClass.simpleName} but nothing was raised.")
25 | }) { raised ->
26 | when (raised) {
27 | is T -> raised
28 | null -> throw failure("Expected to raise ${expectedRaiseClass.simpleName} but was raised instead.")
29 | else -> throw failure("Expected to raise ${expectedRaiseClass.simpleName} but ${raised::class.simpleName} was raised instead.")
30 | }
31 | }
32 | }
33 |
34 | /**
35 | * Verifies that a block of code will not raise anything.
36 | *
37 | * ```kotlin
38 | * val raised: String = shouldNotRaise {
39 | * raise("failed") // fails
40 | * }
41 | * ```
42 | */
43 | public inline fun shouldNotRaise(block: Raise.() -> T): T {
44 | return recover(block) { raised ->
45 | throw failure("No raise expected, but ${raised.print().value} was raised.")
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonTest/kotlin/io/kotest/assertions/arrow/core/EitherMatchers.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.Either
4 | import io.kotest.core.spec.style.StringSpec
5 | import io.kotest.property.Arb
6 | import io.kotest.property.arbitrary.bind
7 | import io.kotest.property.arbitrary.filter
8 | import io.kotest.property.arbitrary.int
9 | import io.kotest.property.checkAll
10 |
11 | class EitherMatchers : StringSpec({
12 | "shouldBeRight"{
13 | checkAll(Arb.int()) { i ->
14 | Either.Right(i) shouldBeRight i
15 | }
16 | }
17 |
18 | "shouldNotBeRight" {
19 | checkAll(
20 | Arb.bind(Arb.int(), Arb.int(), ::Pair)
21 | .filter { (a, b) -> a != b }
22 | ) { (a, b) ->
23 | Either.Right(a) shouldNotBeRight b
24 | }
25 | }
26 |
27 | "shouldBeLeft"{
28 | checkAll(Arb.int()) { i ->
29 | Either.Left(i) shouldBeLeft i
30 | }
31 | }
32 |
33 | "shouldNotBeLeft" {
34 | checkAll(
35 | Arb.bind(Arb.int(), Arb.int(), ::Pair)
36 | .filter { (a, b) -> a != b }
37 | ) { (a, b) ->
38 | Either.Left(a) shouldNotBeLeft b
39 | }
40 | }
41 | })
42 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonTest/kotlin/io/kotest/assertions/arrow/core/IorMatchers.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.Ior
4 | import io.kotest.core.spec.style.StringSpec
5 | import io.kotest.property.Arb
6 | import io.kotest.property.arbitrary.bind
7 | import io.kotest.property.arbitrary.filter
8 | import io.kotest.property.arbitrary.int
9 | import io.kotest.property.arbitrary.string
10 | import io.kotest.property.checkAll
11 | import io.kotest.matchers.shouldBe
12 |
13 | class IorMatchers : StringSpec({
14 | "shouldBeRight"{
15 | checkAll(Arb.int()) { i ->
16 | Ior.Right(i) shouldBeRight i
17 | }
18 | }
19 |
20 | "shouldNotBeRight" {
21 | checkAll(
22 | Arb.bind(Arb.int(), Arb.int(), ::Pair)
23 | .filter { (a, b) -> a != b }
24 | ) { (a, b) ->
25 | Ior.Right(a) shouldNotBeRight b
26 | }
27 | }
28 |
29 | "shouldBeLeft"{
30 | checkAll(Arb.int()) { i ->
31 | Ior.Left(i) shouldBeLeft i
32 | }
33 | }
34 |
35 | "shouldNotBeLeft" {
36 | checkAll(
37 | Arb.bind(Arb.int(), Arb.int(), ::Pair)
38 | .filter { (a, b) -> a != b }
39 | ) { (a, b) ->
40 | Ior.Left(a) shouldNotBeLeft b
41 | }
42 | }
43 |
44 | "shouldBeBoth" {
45 | checkAll(Arb.int(), Arb.string()) { i,j ->
46 | val ior = Ior.Both(i,j)
47 | ior.shouldBeBoth()
48 | ior.leftValue shouldBe i
49 | ior.rightValue shouldBe j
50 | }
51 | }
52 | })
53 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonTest/kotlin/io/kotest/assertions/arrow/core/NelMatchers.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.NonEmptyList
4 | import io.kotest.assertions.throwables.shouldThrow
5 | import io.kotest.core.spec.style.StringSpec
6 |
7 | class NelMatchers : StringSpec({
8 | "containNull: a nel contains at least one null" {
9 | NonEmptyList(1, listOf(2, null)).shouldContainNull()
10 | NonEmptyList(null, listOf()).shouldContainNull()
11 | NonEmptyList(1, listOf(2)).shouldNotContainNull()
12 | NonEmptyList(1, listOf()).shouldNotContainNull()
13 | }
14 |
15 | "haveElementAt: a nel contains an element at the right position" {
16 | NonEmptyList(1, listOf(2, null)).shouldHaveElementAt(1, 2)
17 | NonEmptyList(1, listOf(2, null)).shouldNotHaveElementAt(0, 42)
18 | }
19 |
20 | "a collection is sorted" {
21 | NonEmptyList(1, listOf(2, 3, 4)).shouldBeSorted()
22 | shouldThrow {
23 | NonEmptyList(2, listOf(1)).shouldBeSorted()
24 | }
25 | }
26 |
27 | "a collection is not sorted" {
28 | NonEmptyList(3, listOf(2, 1, 4)).shouldNotBeSorted()
29 | NonEmptyList(5, listOf(2, 3, 4)).shouldNotBeSorted()
30 | }
31 |
32 | "haveDuplicates: a collection is unique or not" {
33 | NonEmptyList(1, listOf(2, 3, 3)).shouldContainDuplicates()
34 | NonEmptyList(1, listOf(2, 3, 4)).shouldNotContainDuplicates()
35 | }
36 |
37 | "beUnique: a collection is unique or not" {
38 | NonEmptyList(1, listOf(2, 3, 4)).shouldBeUnique()
39 | NonEmptyList(1, listOf(2, 3, 3)).shouldNotBeUnique()
40 | }
41 |
42 | "a collection contains a single given element" {
43 | NonEmptyList(1, listOf()) shouldHaveSingleElement 1
44 | shouldThrow {
45 | NonEmptyList(1, listOf()) shouldHaveSingleElement 2
46 | }
47 | shouldThrow {
48 | NonEmptyList(1, listOf(2)) shouldHaveSingleElement 2
49 | }
50 |
51 | NonEmptyList(1, listOf()) shouldHaveSingleElement 1
52 | }
53 |
54 | "a collection does not contain a single element" {
55 | NonEmptyList(1, listOf(2)) shouldNotHaveSingleElement 1
56 | }
57 |
58 | "a collection contains an element" {
59 | NonEmptyList(1, listOf(2, 3)) shouldContain 2
60 | NonEmptyList(1, listOf(2, 3)) shouldNotContain 4
61 | shouldThrow {
62 | NonEmptyList(1, listOf(2, 3)) shouldContain 4
63 | }
64 | }
65 |
66 | "a collection has a certain size" {
67 | NonEmptyList(1, listOf(2, 3)) shouldHaveSize 3
68 | NonEmptyList(1, listOf(2, 3)) shouldNotHaveSize 2
69 | shouldThrow {
70 | NonEmptyList(1, listOf(2, 3)) shouldHaveSize 2
71 | }
72 | }
73 |
74 | "a collection contains zero nulls" {
75 | NonEmptyList(1, listOf(2, 3)).shouldContainNoNulls()
76 | NonEmptyList(null, listOf(null, null)).shouldNotContainNoNulls()
77 | NonEmptyList(1, listOf(null, null)).shouldNotContainNoNulls()
78 | }
79 |
80 | "a collection contains only nulls" {
81 | NonEmptyList(null, listOf(null, null)).shouldContainOnlyNulls()
82 | NonEmptyList(1, listOf(null, null)).shouldNotContainOnlyNulls()
83 | NonEmptyList(1, listOf(2, 3)).shouldNotContainOnlyNulls()
84 | }
85 |
86 | "a collection contains all the elements but in any order" {
87 | val col = NonEmptyList(1, listOf(2, 3, 4, 5))
88 |
89 | col.shouldContainAll(1, 2, 3)
90 | col.shouldContainAll(3, 2, 1)
91 | col.shouldContainAll(5, 1)
92 | col.shouldContainAll(1, 5)
93 | col.shouldContainAll(1)
94 | col.shouldContainAll(5)
95 |
96 | col.shouldContainAll(1, 2, 3, 4)
97 | col.shouldContainAll(1, 2, 3, 4, 5)
98 | col.shouldContainAll(3, 2, 1)
99 | col.shouldContainAll(5, 4, 3, 2, 1)
100 | col shouldContainAll listOf(1, 2, 3, 4)
101 |
102 | shouldThrow {
103 | col.shouldContainAll(1, 2, 6)
104 | }
105 |
106 | shouldThrow {
107 | col.shouldContainAll(6)
108 | }
109 |
110 | shouldThrow {
111 | col.shouldContainAll(0, 1, 2)
112 | }
113 |
114 | shouldThrow {
115 | col.shouldContainAll(3, 2, 0)
116 | }
117 | }
118 |
119 | "a collection shouldNot containAll elements" {
120 | val col = NonEmptyList(1, listOf(2, 3, 4, 5))
121 |
122 | col.shouldNotContainAll(99, 88, 77)
123 | col shouldNotContainAll listOf(99, 88, 77)
124 | }
125 | })
126 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonTest/kotlin/io/kotest/assertions/arrow/core/OptionMatchers.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.Option
4 | import arrow.core.Some
5 | import io.kotest.assertions.throwables.shouldThrow
6 | import io.kotest.core.spec.style.StringSpec
7 | import io.kotest.matchers.shouldBe
8 | import io.kotest.matchers.shouldNotBe
9 | import io.kotest.property.Arb
10 | import io.kotest.property.arbitrary.bind
11 | import io.kotest.property.arbitrary.filter
12 | import io.kotest.property.arbitrary.int
13 | import io.kotest.property.checkAll
14 |
15 | class OptionMatchers : StringSpec({
16 | "Option.shouldBeSome" {
17 | checkAll(Arb.int()) { i ->
18 | Option(i) shouldBeSome i
19 | }
20 | }
21 |
22 | "shouldNotBeSome" {
23 | checkAll(
24 | Arb.bind(Arb.int(), Arb.int(), ::Pair)
25 | .filter { (a, b) -> a != b }
26 | ) { (a, b) ->
27 | Some(a) shouldNotBeSome b
28 | }
29 | }
30 |
31 | "Option shouldBe some(value)" {
32 | shouldThrow {
33 | Option.fromNullable(null) shouldBeSome "foo"
34 | }.message shouldBe "Expected Some, but found None"
35 |
36 | shouldThrow {
37 | Option.fromNullable("boo") shouldBeSome "foo"
38 | }.message shouldBe "expected:<\"foo\"> but was:<\"boo\">"
39 |
40 | val some =
41 | Option.fromNullable("foo") shouldBeSome "foo"
42 |
43 | shouldThrow {
44 | some shouldNotBe "foo"
45 | }
46 | }
47 |
48 | "Option shouldBe none()" {
49 | shouldThrow {
50 | Option.fromNullable("foo").shouldBeNone()
51 | }.message shouldBe "Expected None, but found Some with value foo"
52 |
53 | Option.fromNullable(null).shouldBeNone()
54 | }
55 | })
56 |
--------------------------------------------------------------------------------
/kotest-assertions-arrow/src/commonTest/kotlin/io/kotest/assertions/arrow/core/RaiseMatchers.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.assertions.arrow.core
2 |
3 | import arrow.core.raise.Raise
4 | import io.kotest.assertions.arrow.shouldBe
5 | import io.kotest.assertions.throwables.shouldThrowWithMessage
6 | import io.kotest.core.spec.style.StringSpec
7 |
8 | class RaiseMatchers : StringSpec({
9 | "shouldRaise: specific type" {
10 | val raised = shouldRaise {
11 | raise("failed")
12 | }
13 | raised shouldBe "failed"
14 | }
15 |
16 | "shouldRaise: subtype" {
17 | val raised = shouldRaise {
18 | raise("failed")
19 | }
20 | raised shouldBe "failed"
21 | }
22 |
23 | "shouldRaise: nullable type" {
24 | val raised = shouldRaise {
25 | raise(null)
26 | }
27 | raised shouldBe null
28 | }
29 |
30 | "shouldRaise: fail if null is raised when not expected" {
31 | shouldThrowWithMessage("Expected to raise String but was raised instead.") {
32 | shouldRaise {
33 | raise(null)
34 | }
35 | }
36 | }
37 |
38 | "shouldRaise: fail if expected raise type differs from actual" {
39 | shouldThrowWithMessage("Expected to raise Int but String was raised instead.") {
40 | shouldRaise {
41 | raise("failed")
42 | }
43 | }
44 | }
45 |
46 | "shouldRaise: fail if nothing is raised" {
47 | shouldThrowWithMessage("Expected to raise Int but nothing was raised.") {
48 | shouldRaise {
49 | 42
50 | }
51 | }
52 | }
53 |
54 | "shouldNotRaise" {
55 | val res = shouldNotRaise {
56 | 42
57 | }
58 | res shouldBe 42
59 | }
60 |
61 | "shouldNotRaise: fail if something is raised" {
62 | shouldThrowWithMessage("No raise expected, but \"failed\" was raised.") {
63 | shouldNotRaise {
64 | raise("failed")
65 | }
66 | }
67 | }
68 |
69 | "shouldNotRaise: fail if null was raised" {
70 | shouldThrowWithMessage("No raise expected, but was raised.") {
71 | shouldNotRaise {
72 | raise(null)
73 | }
74 | }
75 | }
76 |
77 | "shouldNotRaise: allows suspend call in block" {
78 | val res = shouldNotRaise {
79 | suspend { 42 }()
80 | }
81 | res shouldBe 42
82 | }
83 |
84 | "shouldRaise: allows suspend call in block" {
85 | val res = shouldRaise {
86 | raise(suspend { 42 }())
87 | }
88 | res shouldBe 42
89 | }
90 |
91 | "shouldNotRaise: callable from non-suspend" {
92 | fun test() = shouldNotRaise { "success" }
93 | test() shouldBe "success"
94 | }
95 |
96 | "shouldRaise: callable from non-suspend" {
97 | fun test() = shouldRaise { raise("failed") }
98 | test() shouldBe "failed"
99 | }
100 | })
101 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("kotest-conventions")
3 | }
4 |
5 | kotlin {
6 | sourceSets {
7 | commonMain {
8 | dependencies {
9 | compileOnly(libs.arrow.optics)
10 | implementation(libs.arrow.functions)
11 | implementation(libs.kotest.assertions.core)
12 | implementation(libs.kotest.framework.api)
13 | implementation(libs.kotest.property)
14 | implementation(libs.kotlinx.coroutines.core)
15 | api(projects.kotestPropertyArrow)
16 | }
17 | }
18 |
19 | commonTest {
20 | dependencies {
21 | implementation(projects.kotestAssertionsArrow)
22 | implementation(libs.arrow.optics)
23 | implementation(libs.kotest.framework.api)
24 | implementation(libs.kotest.framework.engine)
25 | implementation(libs.kotest.property)
26 | implementation(libs.kotlinx.coroutines.core)
27 | }
28 | }
29 |
30 | jsMain {
31 | dependencies {
32 | api(libs.arrow.optics)
33 | }
34 | }
35 |
36 | nativeMain {
37 | dependencies {
38 | implementation(libs.arrow.optics)
39 | }
40 | }
41 | }
42 | }
43 |
44 | apply(from = "../publish-mpp.gradle.kts")
45 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/src/commonMain/kotlin/io/kotest/property/arrow/optics/IsoLaws.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("MemberVisibilityCanBePrivate", "unused")
2 |
3 | package io.kotest.property.arrow.optics
4 |
5 | import arrow.core.compose
6 | import arrow.core.identity
7 | import arrow.optics.Iso
8 | import io.kotest.property.Arb
9 | import io.kotest.property.PropertyContext
10 | import io.kotest.property.arrow.laws.Law
11 | import io.kotest.property.arrow.laws.equalUnderTheLaw
12 | import io.kotest.property.checkAll
13 |
14 | public object IsoLaws {
15 |
16 | public fun laws(
17 | iso: Iso,
18 | aGen: Arb,
19 | bGen: Arb,
20 | funcGen: Arb<(B) -> B>,
21 | eqa: (A, A) -> Boolean = { a, b -> a == b },
22 | eqb: (B, B) -> Boolean = { a, b -> a == b }
23 | ): List =
24 | listOf(
25 | Law("Iso Law: round trip one way") { iso.roundTripOneWay(aGen, eqa) },
26 | Law("Iso Law: round trip other way") { iso.roundTripOtherWay(bGen, eqb) },
27 | Law("Iso Law: modify identity is identity") { iso.modifyIdentity(aGen, eqa) },
28 | Law("Iso Law: compose modify") { iso.composeModify(aGen, funcGen, eqa) },
29 | Law("Iso Law: consitent set with modify") { iso.consistentSetModify(aGen, bGen, eqa) }
30 | )
31 |
32 | public suspend fun Iso.roundTripOneWay(aGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
33 | checkAll(aGen) { a ->
34 | reverseGet(get(a)).equalUnderTheLaw(a, eq)
35 | }
36 |
37 | public suspend fun Iso.roundTripOtherWay(bGen: Arb, eq: (B, B) -> Boolean): PropertyContext =
38 | checkAll(bGen) { b ->
39 | get(reverseGet(b)).equalUnderTheLaw(b, eq)
40 | }
41 |
42 | public suspend fun Iso.modifyIdentity(aGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
43 | checkAll(aGen) { a ->
44 | modify(a, ::identity).equalUnderTheLaw(a, eq)
45 | }
46 |
47 | public suspend fun Iso.composeModify(aGen: Arb, funcGen: Arb<(B) -> B>, eq: (A, A) -> Boolean): PropertyContext =
48 | checkAll(aGen, funcGen, funcGen) { a, f, g ->
49 | modify(modify(a, f), g).equalUnderTheLaw(modify(a, g compose f), eq)
50 | }
51 |
52 | public suspend fun Iso.consistentSetModify(aGen: Arb, bGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
53 | checkAll(aGen, bGen) { a, b ->
54 | set(b).equalUnderTheLaw(modify(a) { b }, eq)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/src/commonMain/kotlin/io/kotest/property/arrow/optics/LensLaws.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("MemberVisibilityCanBePrivate", "unused")
2 |
3 | package io.kotest.property.arrow.optics
4 |
5 | import arrow.core.compose
6 | import arrow.core.identity
7 | import arrow.optics.Lens
8 | import io.kotest.property.Arb
9 | import io.kotest.property.PropertyContext
10 | import io.kotest.property.arbitrary.constant
11 | import io.kotest.property.arrow.laws.Law
12 | import io.kotest.property.arrow.laws.equalUnderTheLaw
13 | import io.kotest.property.checkAll
14 |
15 | public object LensLaws {
16 |
17 | public fun laws(
18 | lensGen: Arb>,
19 | aGen: Arb,
20 | bGen: Arb,
21 | funcGen: Arb<(B) -> B>,
22 | eqa: (A, A) -> Boolean = { a, b -> a == b },
23 | eqb: (B, B) -> Boolean = { a, b -> a == b }
24 | ): List =
25 | listOf(
26 | Law("Lens law: get set") { lensGetSet(lensGen, aGen, eqa) },
27 | Law("Lens law: set get") { lensSetGet(lensGen, aGen, bGen, eqb) },
28 | Law("Lens law: is set idempotent") { lensSetIdempotent(lensGen, aGen, bGen, eqa) },
29 | Law("Lens law: modify identity") { lensModifyIdentity(lensGen, aGen, eqa) },
30 | Law("Lens law: compose modify") { lensComposeModify(lensGen, aGen, funcGen, eqa) },
31 | Law("Lens law: consistent set modify") { lensConsistentSetModify(lensGen, aGen, bGen, eqa) }
32 | )
33 |
34 | /**
35 | * Warning: Use only when a `Gen.constant()` applies
36 | */
37 | public fun laws(
38 | lens: Lens,
39 | aGen: Arb,
40 | bGen: Arb,
41 | funcGen: Arb<(B) -> B>,
42 | eqa: (A, A) -> Boolean = { a, b -> a == b },
43 | eqb: (B, B) -> Boolean = { a, b -> a == b }
44 | ): List = laws(Arb.constant(lens), aGen, bGen, funcGen, eqa, eqb)
45 |
46 | public suspend fun lensGetSet(lensGen: Arb>, aGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
47 | checkAll(lensGen, aGen) { lens, a ->
48 | lens.run {
49 | set(a, get(a)).equalUnderTheLaw(a, eq)
50 | }
51 | }
52 |
53 | public suspend fun lensSetGet(
54 | lensGen: Arb>,
55 | aGen: Arb,
56 | bGen: Arb,
57 | eq: (B, B) -> Boolean
58 | ): PropertyContext =
59 | checkAll(lensGen, aGen, bGen) { lens, a, b ->
60 | lens.run {
61 | get(set(a, b)).equalUnderTheLaw(b, eq)
62 | }
63 | }
64 |
65 | public suspend fun lensSetIdempotent(
66 | lensGen: Arb>,
67 | aGen: Arb,
68 | bGen: Arb,
69 | eq: (A, A) -> Boolean
70 | ): PropertyContext =
71 | checkAll(lensGen, aGen, bGen) { lens, a, b ->
72 | lens.run {
73 | set(set(a, b), b).equalUnderTheLaw(set(a, b), eq)
74 | }
75 | }
76 |
77 | public suspend fun lensModifyIdentity(
78 | lensGen: Arb>,
79 | aGen: Arb,
80 | eq: (A, A) -> Boolean
81 | ): PropertyContext =
82 | checkAll(lensGen, aGen) { lens, a ->
83 | lens.run {
84 | modify(a, ::identity).equalUnderTheLaw(a, eq)
85 | }
86 | }
87 |
88 | public suspend fun lensComposeModify(
89 | lensGen: Arb>,
90 | aGen: Arb,
91 | funcGen: Arb<(B) -> B>,
92 | eq: (A, A) -> Boolean
93 | ): PropertyContext =
94 | checkAll(lensGen, aGen, funcGen, funcGen) { lens, a, f, g ->
95 | lens.run {
96 | modify(modify(a, f), g).equalUnderTheLaw(modify(a, g compose f), eq)
97 | }
98 | }
99 |
100 | public suspend fun lensConsistentSetModify(
101 | lensGen: Arb>,
102 | aGen: Arb,
103 | bGen: Arb,
104 | eq: (A, A) -> Boolean
105 | ): PropertyContext =
106 | checkAll(lensGen, aGen, bGen) { lens, a, b ->
107 | lens.run {
108 | set(a, b).equalUnderTheLaw(modify(a) { b }, eq)
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/src/commonMain/kotlin/io/kotest/property/arrow/optics/OptionalLaws.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("MemberVisibilityCanBePrivate", "unused")
2 |
3 | package io.kotest.property.arrow.optics
4 |
5 | import arrow.core.compose
6 | import arrow.core.identity
7 | import arrow.optics.Optional
8 | import io.kotest.property.Arb
9 | import io.kotest.property.PropertyContext
10 | import io.kotest.property.arbitrary.constant
11 | import io.kotest.property.arrow.laws.Law
12 | import io.kotest.property.arrow.laws.equalUnderTheLaw
13 | import io.kotest.property.checkAll
14 |
15 | public object OptionalLaws {
16 |
17 | public fun laws(
18 | optionalGen: Arb>,
19 | aGen: Arb,
20 | bGen: Arb,
21 | funcGen: Arb<(B) -> B>,
22 | eqa: (A, A) -> Boolean = { a, b -> a == b },
23 | eqb: (B?, B?) -> Boolean = { a, b -> a == b }
24 | ): List = listOf(
25 | Law("Optional Law: set what you get") { getOptionSet(optionalGen, aGen, eqa) },
26 | Law("Optional Law: set what you get") { setGetOption(optionalGen, aGen, bGen, eqb) },
27 | Law("Optional Law: set is idempotent") { setIdempotent(optionalGen, aGen, bGen, eqa) },
28 | Law("Optional Law: modify identity = identity") { modifyIdentity(optionalGen, aGen, eqa) },
29 | Law("Optional Law: compose modify") { composeModify(optionalGen, aGen, funcGen, eqa) },
30 | Law("Optional Law: consistent set with modify") { consistentSetModify(optionalGen, aGen, bGen, eqa) }
31 | )
32 |
33 | /**
34 | * Warning: Use only when a `Gen.constant()` applies
35 | */
36 | public fun laws(
37 | optional: Optional,
38 | aGen: Arb,
39 | bGen: Arb,
40 | funcGen: Arb<(B) -> B>,
41 | eqa: (A, A) -> Boolean = { a, b -> a == b },
42 | eqb: (B?, B?) -> Boolean = { a, b -> a == b }
43 | ): List = laws(Arb.constant(optional), aGen, bGen, funcGen, eqa, eqb)
44 |
45 | public suspend fun getOptionSet(
46 | optionalGen: Arb>,
47 | aGen: Arb,
48 | eq: (A, A) -> Boolean
49 | ): PropertyContext =
50 | checkAll(optionalGen, aGen) { optional, a ->
51 | optional.run {
52 | getOrModify(a).fold(::identity) { set(a, it) }
53 | .equalUnderTheLaw(a, eq)
54 | }
55 | }
56 |
57 | public suspend fun setGetOption(
58 | optionalGen: Arb>,
59 | aGen: Arb,
60 | bGen: Arb,
61 | eq: (B?, B?) -> Boolean
62 | ): PropertyContext =
63 | checkAll(optionalGen, aGen, bGen) { optional, a, b ->
64 | optional.run {
65 | getOrNull(set(a, b)).equalUnderTheLaw(getOrNull(a)?.let { b }) { a, b -> eq(a, b) }
66 | }
67 | }
68 |
69 | public suspend fun setIdempotent(
70 | optionalGen: Arb>,
71 | aGen: Arb,
72 | bGen: Arb,
73 | eq: (A, A) -> Boolean
74 | ): PropertyContext =
75 | checkAll(optionalGen, aGen, bGen) { optional, a, b ->
76 | optional.run {
77 | set(set(a, b), b).equalUnderTheLaw(set(a, b), eq)
78 | }
79 | }
80 |
81 | public suspend fun modifyIdentity(
82 | optionalGen: Arb>,
83 | aGen: Arb,
84 | eq: (A, A) -> Boolean
85 | ): PropertyContext =
86 | checkAll(optionalGen, aGen) { optional, a ->
87 | optional.run {
88 | modify(a, ::identity).equalUnderTheLaw(a, eq)
89 | }
90 | }
91 |
92 | public suspend fun composeModify(
93 | optionalGen: Arb>,
94 | aGen: Arb,
95 | funcGen: Arb<(B) -> B>,
96 | eq: (A, A) -> Boolean
97 | ): PropertyContext =
98 | checkAll(optionalGen, aGen, funcGen, funcGen) { optional, a, f, g ->
99 | optional.run {
100 | modify(modify(a, f), g).equalUnderTheLaw(modify(a, g compose f), eq)
101 | }
102 | }
103 |
104 | public suspend fun consistentSetModify(
105 | optionalGen: Arb>,
106 | aGen: Arb,
107 | bGen: Arb,
108 | eq: (A, A) -> Boolean
109 | ): PropertyContext =
110 | checkAll(optionalGen, aGen, bGen) { optional, a, b ->
111 | optional.run {
112 | set(a, b).equalUnderTheLaw(modify(a) { b }, eq)
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/src/commonMain/kotlin/io/kotest/property/arrow/optics/PrismLaws.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("MemberVisibilityCanBePrivate", "unused")
2 |
3 | package io.kotest.property.arrow.optics
4 |
5 | import arrow.core.compose
6 | import arrow.core.identity
7 | import arrow.optics.Prism
8 | import io.kotest.property.Arb
9 | import io.kotest.property.PropertyContext
10 | import io.kotest.property.arrow.laws.Law
11 | import io.kotest.property.arrow.laws.equalUnderTheLaw
12 | import io.kotest.property.checkAll
13 |
14 | public object PrismLaws {
15 |
16 | public fun laws(
17 | prism: Prism,
18 | aGen: Arb,
19 | bGen: Arb,
20 | funcGen: Arb<(B) -> B>,
21 | eqa: (A, A) -> Boolean = { a, b -> a == b },
22 | eqb: (B?, B?) -> Boolean = { a, b -> a == b }
23 | ): List = listOf(
24 | Law("Prism law: partial round trip one way") { prism.partialRoundTripOneWay(aGen, eqa) },
25 | Law("Prism law: round trip other way") { prism.roundTripOtherWay(bGen, eqb) },
26 | Law("Prism law: modify identity") { prism.modifyIdentity(aGen, eqa) },
27 | Law("Prism law: compose modify") { prism.composeModify(aGen, funcGen, eqa) },
28 | Law("Prism law: consistent set modify") { prism.consistentSetModify(aGen, bGen, eqa) }
29 | )
30 |
31 | public suspend fun Prism.partialRoundTripOneWay(aGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
32 | checkAll(aGen) { a ->
33 | getOrModify(a).fold(::identity, ::reverseGet)
34 | .equalUnderTheLaw(a, eq)
35 | }
36 |
37 | public suspend fun Prism.roundTripOtherWay(bGen: Arb, eq: (B?, B?) -> Boolean): PropertyContext =
38 | checkAll(bGen) { b ->
39 | getOrNull(reverseGet(b))
40 | .equalUnderTheLaw(b, eq)
41 | }
42 |
43 | public suspend fun Prism.modifyIdentity(aGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
44 | checkAll(aGen) { a ->
45 | modify(a, ::identity).equalUnderTheLaw(a, eq)
46 | }
47 |
48 | public suspend fun Prism.composeModify(aGen: Arb, funcGen: Arb<(B) -> B>, eq: (A, A) -> Boolean): PropertyContext =
49 | checkAll(aGen, funcGen, funcGen) { a, f, g ->
50 | modify(modify(a, f), g).equalUnderTheLaw(modify(a, g compose f), eq)
51 | }
52 |
53 | public suspend fun Prism.consistentSetModify(aGen: Arb, bGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
54 | checkAll(aGen, bGen) { a, b ->
55 | set(a, b).equalUnderTheLaw(modify(a) { b }, eq)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/src/commonMain/kotlin/io/kotest/property/arrow/optics/TraversalLaws.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("MemberVisibilityCanBePrivate", "unused")
2 |
3 | package io.kotest.property.arrow.optics
4 |
5 | import arrow.core.compose
6 | import arrow.core.identity
7 | import arrow.optics.Traversal
8 | import io.kotest.property.Arb
9 | import io.kotest.property.PropertyContext
10 | import io.kotest.property.arrow.laws.Law
11 | import io.kotest.property.arrow.laws.equalUnderTheLaw
12 | import io.kotest.property.checkAll
13 | import kotlin.math.max
14 |
15 | public object TraversalLaws {
16 |
17 | public fun laws(
18 | traversal: Traversal,
19 | aGen: Arb,
20 | bGen: Arb,
21 | funcGen: Arb<(B) -> B>,
22 | eq: (A, A) -> Boolean = { a, b -> a == b }
23 | ): List = listOf(
24 | Law("Traversal law: set is idempotent") { traversal.setIdempotent(aGen, bGen, eq) },
25 | Law("Traversal law: modify identity") { traversal.modifyIdentity(aGen, eq) },
26 | Law("Traversal law: compose modify") { traversal.composeModify(aGen, funcGen, eq) }
27 | )
28 |
29 | public suspend fun Traversal.setIdempotent(aGen: Arb, bGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
30 | checkAll(max(aGen.minIterations(), bGen.minIterations()), aGen, bGen) { a, b ->
31 | set(set(a, b), b)
32 | .equalUnderTheLaw(set(a, b), eq)
33 | }
34 |
35 | public suspend fun Traversal.modifyIdentity(aGen: Arb, eq: (A, A) -> Boolean): PropertyContext =
36 | checkAll(aGen.minIterations(), aGen) { a ->
37 | modify(a, ::identity).equalUnderTheLaw(a, eq)
38 | }
39 |
40 | public suspend fun Traversal.composeModify(
41 | aGen: Arb,
42 | funcGen: Arb<(B) -> B>,
43 | eq: (A, A) -> Boolean
44 | ): PropertyContext =
45 | checkAll(
46 | max(max(aGen.minIterations(), funcGen.minIterations()), funcGen.minIterations()),
47 | aGen,
48 | funcGen,
49 | funcGen
50 | ) { a, f, g ->
51 | modify(modify(a, f), g)
52 | .equalUnderTheLaw(modify(a, g compose f), eq)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/kotest-property-arrow-optics/src/commonTest/kotlin/io/kotest/property/arrow/optics/TraversalTests.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.property.arrow.optics
2 |
3 | import arrow.optics.Optional
4 | import arrow.optics.Traversal
5 | import io.kotest.core.spec.style.StringSpec
6 | import io.kotest.property.Arb
7 | import io.kotest.property.arbitrary.boolean
8 | import io.kotest.property.arbitrary.int
9 | import io.kotest.property.arbitrary.list
10 | import io.kotest.property.arbitrary.pair
11 | import io.kotest.property.arbitrary.string
12 | import io.kotest.property.arrow.core.either
13 | import io.kotest.property.arrow.core.functionAToB
14 | import io.kotest.property.arrow.core.tuple9
15 | import io.kotest.property.arrow.laws.testLaws
16 |
17 | class TraversalTests : StringSpec({
18 | testLaws(
19 | "Traversal Laws for Optional",
20 | TraversalLaws.laws(
21 | traversal = Optional.listHead(),
22 | aGen = Arb.list(Arb.int()),
23 | bGen = Arb.int(),
24 | funcGen = Arb.functionAToB(Arb.int()),
25 | ),
26 | TraversalLaws.laws(
27 | traversal = Traversal.either(),
28 | aGen = Arb.either(Arb.string(), Arb.int()),
29 | bGen = Arb.int(),
30 | funcGen = Arb.functionAToB(Arb.int()),
31 | ),
32 | TraversalLaws.laws(
33 | traversal = Traversal.pair(),
34 | aGen = Arb.pair(Arb.boolean(), Arb.boolean()),
35 | bGen = Arb.boolean(),
36 | funcGen = Arb.functionAToB(Arb.boolean()),
37 | ),
38 | TraversalLaws.laws(
39 | traversal = Traversal.tuple9(),
40 | aGen = Arb.tuple9(
41 | Arb.boolean(),
42 | Arb.boolean(),
43 | Arb.boolean(),
44 | Arb.boolean(),
45 | Arb.boolean(),
46 | Arb.boolean(),
47 | Arb.boolean(),
48 | Arb.boolean(),
49 | Arb.boolean()
50 | ),
51 | bGen = Arb.boolean(),
52 | funcGen = Arb.functionAToB(Arb.boolean()),
53 | )
54 | )
55 | })
56 |
--------------------------------------------------------------------------------
/kotest-property-arrow/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("kotest-conventions")
3 | }
4 |
5 | kotlin {
6 | sourceSets {
7 | commonMain {
8 | dependencies {
9 | compileOnly(libs.arrow.core)
10 | implementation(libs.kotest.assertions.core)
11 | implementation(libs.kotest.framework.api)
12 | implementation(libs.kotest.property)
13 | }
14 | }
15 |
16 | commonTest {
17 | dependencies {
18 | implementation(projects.kotestAssertionsArrow)
19 | implementation(libs.arrow.core)
20 | implementation(libs.kotest.framework.engine)
21 | implementation(libs.kotest.property)
22 | implementation(libs.kotlinx.coroutines.core)
23 | }
24 | }
25 |
26 | jsMain {
27 | dependencies {
28 | api(libs.arrow.core)
29 | }
30 | }
31 |
32 | nativeMain {
33 | dependencies {
34 | implementation(libs.arrow.core)
35 | }
36 | }
37 | }
38 | }
39 |
40 | apply(from = "../publish-mpp.gradle.kts")
41 |
--------------------------------------------------------------------------------
/kotest-property-arrow/src/commonMain/kotlin/io/kotest/property/arrow/core/Either.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.property.arrow.core
2 |
3 | import arrow.core.Either
4 | import io.kotest.property.Arb
5 | import io.kotest.property.arbitrary.choice
6 | import io.kotest.property.arbitrary.map
7 |
8 | public fun Arb.Companion.either(left: Arb, right: Arb): Arb> =
9 | choice(left.map { Either.Left(it) }, right.map { Either.Right(it) })
10 |
11 | public fun Arb.or(arbB: Arb): Arb> =
12 | Arb.either(this, arbB)
13 |
--------------------------------------------------------------------------------
/kotest-property-arrow/src/commonMain/kotlin/io/kotest/property/arrow/core/Function.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.property.arrow.core
2 |
3 | import io.kotest.property.Arb
4 | import io.kotest.property.arbitrary.map
5 |
6 | public fun Arb.Companion.functionAToB(arb: Arb): Arb<(A) -> B> =
7 | arb.map { b: B -> { _: A -> b } }
8 |
9 | public fun Arb.Companion.functionAAToA(arb: Arb): Arb<(A, A) -> A> =
10 | arb.map { a: A -> { _: A, _: A -> a } }
11 |
12 | public fun Arb.Companion.functionBAToB(arb: Arb): Arb<(B, A) -> B> =
13 | arb.map { b: B -> { _: B, _: A -> b } }
14 |
15 | public fun Arb.Companion.functionABToB(arb: Arb): Arb<(A, B) -> B> =
16 | arb.map { b: B -> { _: A, _: B -> b } }
17 |
18 | public fun Arb.Companion.functionToA(arb: Arb): Arb<() -> A> =
19 | arb.map { a: A -> { a } }
20 |
--------------------------------------------------------------------------------
/kotest-property-arrow/src/commonMain/kotlin/io/kotest/property/arrow/core/Ior.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.property.arrow.core
2 |
3 | import arrow.core.Ior
4 | import io.kotest.property.Arb
5 | import io.kotest.property.arbitrary.choice
6 | import io.kotest.property.arbitrary.map
7 | import io.kotest.property.arbitrary.bind
8 |
9 |
10 | public fun Arb.Companion.ior(left: Arb, right: Arb): Arb> =
11 | Arb.choice(left.map { Ior.Left(it) },
12 | Arb.bind(left, right) { a, b -> Ior.Both(a, b) },
13 | right.map { Ior.Right(it) })
14 |
15 | public fun Arb.alignWith(arbB: Arb): Arb> =
16 | Arb.ior(this, arbB)
17 |
--------------------------------------------------------------------------------
/kotest-property-arrow/src/commonMain/kotlin/io/kotest/property/arrow/core/Nel.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.property.arrow.core
2 |
3 | import arrow.core.NonEmptyList
4 | import arrow.core.toNonEmptyListOrNull
5 | import io.kotest.property.Arb
6 | import io.kotest.property.arbitrary.filter
7 | import io.kotest.property.arbitrary.list
8 | import io.kotest.property.arbitrary.map
9 |
10 | public fun Arb.Companion.nel(a: Arb): Arb> = nonEmptyList(a)
11 |
12 | public fun Arb.Companion.nonEmptyList(a: Arb): Arb> =
13 | list(a)
14 | .filter(List::isNotEmpty)
15 | .map { it.toNonEmptyListOrNull()!! }
16 |
17 | public fun Arb.Companion.nel(a: Arb, size: IntRange): Arb> = nonEmptyList(a, size)
18 |
19 | public fun Arb.Companion.nonEmptyList(a: Arb, size: IntRange): Arb> =
20 | list(a, size)
21 | .filter(List::isNotEmpty)
22 | .map { it.toNonEmptyListOrNull()!! }
23 |
--------------------------------------------------------------------------------
/kotest-property-arrow/src/commonMain/kotlin/io/kotest/property/arrow/core/Option.kt:
--------------------------------------------------------------------------------
1 | package io.kotest.property.arrow.core
2 |
3 | import arrow.core.Option
4 | import arrow.core.toOption
5 | import io.kotest.property.Arb
6 | import io.kotest.property.arbitrary.map
7 | import io.kotest.property.arbitrary.orNull
8 |
9 | public fun Arb.Companion.option(arb: Arb): Arb