├── .codecov.yml ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── auto-labeling.yml │ ├── auto-release.yml │ ├── main.yml │ ├── publish.yml │ ├── verify-doc.yml │ └── website.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── build.gradle.kts ├── forge-core ├── build.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── io │ │ └── github │ │ └── hellocuriosity │ │ ├── Attempt.kt │ │ ├── Forgery.kt │ │ ├── Millis.kt │ │ ├── ModelForge.kt │ │ ├── ModelForgeException.kt │ │ ├── ModelForgeKotlin.kt │ │ └── providers │ │ ├── BooleanProvider.kt │ │ ├── ByteProvider.kt │ │ ├── CalendarProvider.kt │ │ ├── CharProvider.kt │ │ ├── DateProvider.kt │ │ ├── DoubleProvider.kt │ │ ├── FileProvider.kt │ │ ├── FloatProvider.kt │ │ ├── GetEnum.kt │ │ ├── InstantProvider.kt │ │ ├── IntegerProvider.kt │ │ ├── LongProvider.kt │ │ ├── Provider.kt │ │ ├── ShortProvider.kt │ │ ├── StringProvider.kt │ │ ├── UByteProvider.kt │ │ ├── UIntProvider.kt │ │ ├── ULongProvider.kt │ │ ├── UShortProvider.kt │ │ └── UuidProvider.kt │ └── test │ ├── kotlin │ └── io │ │ └── github │ │ └── hellocuriosity │ │ ├── ForgeryTest.kt │ │ ├── ModelForgeExceptionTest.kt │ │ ├── ModelForgeKotlinTest.kt │ │ ├── ModelForgeTest.kt │ │ ├── TestProviderObject.kt │ │ └── providers │ │ ├── BooleanProviderTest.kt │ │ ├── ByteProviderTest.kt │ │ ├── CalendarProviderTest.kt │ │ ├── CharProviderTest.kt │ │ ├── DateProviderTest.kt │ │ ├── DoubleProviderTest.kt │ │ ├── EnumProviderTest.kt │ │ ├── FileProviderTest.kt │ │ ├── FloatProviderTest.kt │ │ ├── InstantProviderTest.kt │ │ ├── IntegerProviderTest.kt │ │ ├── LongProviderTest.kt │ │ ├── ShortProviderTest.kt │ │ ├── StringProviderTest.kt │ │ ├── UByteProviderTest.kt │ │ ├── UIntProviderTest.kt │ │ ├── ULongProviderTest.kt │ │ ├── UShortProviderTest.kt │ │ └── UuidProviderTest.kt │ └── resources │ └── mockito-extensions │ └── org.mockito.plugins.MockMaker ├── forge-test-utils ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── io │ └── github │ └── hellocuriosity │ ├── Assert.kt │ └── TestObject.kt ├── gradle.properties ├── gradle ├── dependencies.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── scripts ├── create-release.sh ├── get_next_version.sh ├── publish.sh └── verify-doc.sh ├── settings.gradle.kts └── website ├── .gitignore ├── README.md ├── babel.config.js ├── docs ├── changelog.md ├── getting-started │ ├── _category_.json │ ├── custom-providers.md │ ├── generate-models.md │ └── gradle-dependency.md ├── introduction.md ├── migration.md └── supported-types │ ├── _category_.json │ ├── boolean.md │ ├── byte.md │ ├── calendar.md │ ├── char.md │ ├── date.md │ ├── double.md │ ├── enums.md │ ├── file.md │ ├── float.md │ ├── instant.md │ ├── int.md │ ├── long.md │ ├── short.md │ ├── string.md │ ├── ubyte.md │ ├── uint.md │ ├── ulong.md │ ├── ushort.md │ └── uuid.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src ├── components │ └── HomepageFeatures │ │ ├── index.js │ │ └── styles.module.css ├── css │ └── custom.css └── pages │ ├── index.js │ ├── index.module.css │ └── markdown-page.md ├── static ├── .nojekyll └── img │ ├── android.svg │ ├── build.svg │ ├── extendability.svg │ ├── favicon.ico │ ├── logo.svg │ └── open_source.svg └── yarn.lock /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: main 3 | notify: 4 | require_ci_to_pass: no 5 | 6 | coverage: 7 | precision: 1 8 | round: down 9 | range: "70...100" 10 | status: 11 | project: 12 | default: 13 | target: 93 14 | threshold: 1% 15 | only_pulls: true 16 | 17 | patch: 18 | default: 19 | target: 100 20 | threshold: 1% 21 | only_pulls: true 22 | 23 | changes: no 24 | 25 | parsers: 26 | gcov: 27 | branch_detection: 28 | conditional: yes 29 | loop: yes 30 | method: no 31 | macro: no 32 | 33 | github_checks: 34 | annotations: true 35 | 36 | ignore: 37 | - "**/build/**/*" 38 | - "**/*.gradle" 39 | - "forge-test-utils/**/*" 40 | 41 | comment: 42 | layout: "header, diff" 43 | behavior: default 44 | require_changes: no 45 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | insert_final_newline = true 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Help make model-forge better by reporting bugs, and other issues. 4 | labels: bug 5 | --- 6 | 7 | Your issue may have been reported already. Please have a quick look in the 8 | [issues](https://github.com/HelloCuriosity/model-forge/issues) before creating a new 9 | one. 10 | 11 | ## Observed Behavior 12 | 13 | 14 | 15 | ## Expected Behavior 16 | 17 | 18 | 19 | ## How to test / reproduce 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Help make model-forge better by creating a feature request. 4 | labels: feature 5 | --- 6 | 7 | Your feature request may already be reported! 8 | Please have a quick look in the [issues](https://github.com/HelloCuriosity/model-forge/issues) before creating a new one. 9 | 10 | ## Description 11 | 12 | 13 | ## Proposal 14 | 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | Before you create a pull request make sure you've reviewed our 3 | [code of conduct](https://github.com/HelloCuriosity/model-forge/blob/main/CODE_OF_CONDUCT.md) and 4 | [contribution](https://github.com/HelloCuriosity/model-forge/blob/main/CONTRIBUTING.md). 5 | 6 | ## Description 7 | 8 | 9 | 10 | ## How to test / reproduce 11 | 12 | 13 | 14 | ## Review checklist 15 | 16 | - [ ] PR is split into meaningful commits for the ease of reviewing 17 | - [ ] Description contains clear instructions on how to test the feature (where applicable) 18 | - [ ] Tests have been written 19 | - [ ] Appropriate labels have been applied 20 | - [ ] Update [README](../README.md) (where applicable) 21 | - [ ] Update [Documentation](../website/docs/introduction.md) (where applicable) 22 | -------------------------------------------------------------------------------- /.github/workflows/auto-labeling.yml: -------------------------------------------------------------------------------- 1 | name: Labeling 2 | 3 | on: 4 | pull_request: 5 | types: [ synchronize, opened, reopened, labeled, unlabeled ] 6 | 7 | jobs: 8 | labeling: 9 | name: Adding Labels 10 | runs-on: ubuntu-latest 11 | env: 12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 13 | permissions: 14 | pull-requests: write 15 | steps: 16 | - name: Labeling 17 | uses: hopeman15/labelicious@0.2.0 18 | -------------------------------------------------------------------------------- /.github/workflows/auto-release.yml: -------------------------------------------------------------------------------- 1 | name: Release ✈️ 2 | 3 | on: 4 | pull_request: 5 | types: [ closed ] 6 | branches: 7 | - main 8 | 9 | jobs: 10 | cancel-previous: 11 | name: Cancel Publishing 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Cancel Previous Build 15 | uses: styfle/cancel-workflow-action@0.12.1 16 | with: 17 | access_token: ${{ github.token }} 18 | 19 | create-release: 20 | if: ${{ (github.event.pull_request.merged == true) && (contains(github.event.pull_request.labels.*.name, 'release :tada:')) }} 21 | name: Create Release 22 | runs-on: ubuntu-latest 23 | needs: cancel-previous 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.AUTO_RELEASE_PAT }} 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | - name: Major Version Bump 30 | if: ${{ contains(github.event.*.labels.*.name, 'major :1st_place_medal:') }} 31 | run: make version BUMP=major 32 | - name: Minor Version Bump 33 | if: ${{ contains(github.event.*.labels.*.name, 'minor :2nd_place_medal:') }} 34 | run: make version BUMP=minor 35 | - name: Patch Version Bump 36 | if: ${{ contains(github.event.*.labels.*.name, 'patch :3rd_place_medal:') }} 37 | run: make version BUMP=patch 38 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Model Forge 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | cancel-previous: 13 | name: Cancel Previous 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Cancel Previous Build 17 | uses: styfle/cancel-workflow-action@0.12.1 18 | with: 19 | access_token: ${{ github.token }} 20 | 21 | test: 22 | runs-on: ubuntu-latest 23 | needs: cancel-previous 24 | strategy: 25 | matrix: 26 | java: [ '11','17' ] 27 | name: Test with Java ${{ matrix.java }} 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | - name: Set up JDK 34 | uses: actions/setup-java@v4.7.1 35 | with: 36 | distribution: 'temurin' 37 | java-version: ${{ matrix.java }} 38 | cache: 'gradle' 39 | - name: Lint 40 | run: make lint 41 | - name: Test 42 | run: make test 43 | 44 | report: 45 | name: "Report" 46 | runs-on: ubuntu-latest 47 | needs: cancel-previous 48 | steps: 49 | - name: Checkout 50 | uses: actions/checkout@v4 51 | with: 52 | fetch-depth: 0 53 | - name: "Generate Coverage Report" 54 | run: make coverage 55 | - name: "Upload Coverage Report" 56 | uses: codecov/codecov-action@v5.4.2 57 | with: 58 | files: ./build/reports/kover/result.xml 59 | token: ${{ secrets.CODECOV_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publishing 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | cancel-previous: 10 | name: Cancel Publishing 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Cancel Previous Build 14 | uses: styfle/cancel-workflow-action@0.12.1 15 | with: 16 | access_token: ${{ github.token }} 17 | 18 | publish: 19 | name: Publish 20 | runs-on: ubuntu-latest 21 | needs: cancel-previous 22 | env: 23 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USER }} 24 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PWD }} 25 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.KEY }} 26 | ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.KEY_ID }} 27 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.KEY_PWD }} 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | - name: Set up JDK 32 | uses: actions/setup-java@v4.7.1 33 | with: 34 | distribution: 'temurin' 35 | java-version: '17' 36 | cache: 'gradle' 37 | - name: Publish 38 | run: | 39 | export IS_RELEASE=${{ github.event_name == 'push' && contains(github.ref, 'refs/tags/') }} 40 | make publish 41 | -------------------------------------------------------------------------------- /.github/workflows/verify-doc.yml: -------------------------------------------------------------------------------- 1 | name: Verify Documentation 2 | 3 | on: 4 | pull_request: 5 | types: [ synchronize, opened, reopened, labeled, unlabeled ] 6 | 7 | jobs: 8 | check_docs: 9 | if: ${{ contains(github.event.pull_request.labels.*.name, 'release :tada:') }} 10 | name: Check 11 | runs-on: ubuntu-latest 12 | permissions: 13 | actions: read 14 | strategy: 15 | matrix: 16 | file: [ 'website/docs/changelog.md','README.md' ] 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | - name: Major 21 | if: ${{ contains(github.event.*.labels.*.name, 'major :1st_place_medal:') }} 22 | run: make verify-doc BUMP=major FILE=${{ matrix.file }} 23 | - name: Minor 24 | if: ${{ contains(github.event.*.labels.*.name, 'minor :2nd_place_medal:') }} 25 | run: make verify-doc BUMP=minor FILE=${{ matrix.file }} 26 | - name: Patch 27 | if: ${{ contains(github.event.*.labels.*.name, 'patch :3rd_place_medal:') }} 28 | run: make verify-doc BUMP=patch FILE=${{ matrix.file }} 29 | -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | name: Website 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | cancel-previous: 13 | name: Cancel Previous 14 | permissions: 15 | contents: read 16 | actions: write 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Cancel Previous Build 20 | uses: styfle/cancel-workflow-action@0.12.1 21 | with: 22 | access_token: ${{ github.token }} 23 | 24 | build: 25 | name: Build 26 | permissions: 27 | contents: write 28 | runs-on: ubuntu-latest 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v4 32 | with: 33 | fetch-depth: 0 34 | 35 | - name: Setup Node 36 | uses: actions/setup-node@v4.4.0 37 | with: 38 | node-version: '22' 39 | cache: 'yarn' 40 | cache-dependency-path: 'website/yarn.lock' 41 | 42 | - name: Lint 43 | run: make lint-docs 44 | 45 | - name: Install Dependencies 46 | working-directory: website/ 47 | run: yarn install 48 | 49 | - name: Build the Website 50 | working-directory: website/ 51 | run: yarn build 52 | 53 | - name: Deploy Github Pages (only on main) 54 | uses: JamesIves/github-pages-deploy-action@v4.7.3 55 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} 56 | with: 57 | branch: gh-pages 58 | folder: website/build/ 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | .gradle 3 | build/ 4 | 5 | # IntelliJ 6 | *.iml 7 | *.ipr 8 | *.iws 9 | .idea/ 10 | 11 | # Misc 12 | .DS_Store 13 | 14 | # other stuff 15 | /*/out 16 | .env 17 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | info@hello-curiosity.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | [homepage]: https://www.contributor-covenant.org 122 | 123 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 124 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | :rocket: Thanks for showing your interest and taking the time to contribute :rocket: 4 | 5 | The following is a set of guidelines for contributing to this project. These are guidelines not rules, so feel free to 6 | propose changes to this document via a pull request. 7 | 8 | ## Table of Contents 9 | 10 | * [Quick Start](#quick-start) 11 | * [Code Style Guidelines](#code-style-guidelines) 12 | * [Work with feature branches](#work-with-feature-branches) 13 | * [Testing](#testing) 14 | * [Running Tests](#running-tests) 15 | 16 | ## Quick start 17 | 18 | 1. Fork the repository 19 | 2. Create your feature branch 20 | 3. Commit your changes 21 | 4. Push to the branch 22 | 5. Create a Pull Request 23 | 24 | ## Code Style Guidelines 25 | 26 | Code style is driven by the `kotlinter` and `detekt` if it doesn't adhere to it your build will break. You can run 27 | analysis with: 28 | 29 | $ make lint 30 | 31 | ## Work with Feature Branches 32 | 33 | While developing create feature branches from main. A good rule of thumb is to prefix them with one of the following: 34 | 35 | * feature/... 36 | * enhancement/... 37 | * fix/... 38 | 39 | ## Testing 40 | 41 | Tests are imperative to the success and quality of any feature, regardless of its size. It is essential to the 42 | development process, which not only speeds up the development of further features, but works as a safe guard protecting 43 | existing and already tested code. 44 | 45 | `Features aren't ready or finished before tests are written.` 46 | 47 | ### Running Tests 48 | 49 | You can run tests with: 50 | 51 | $ make test 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hello Curiosity 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUMP ?= patch 2 | FILE ?= empty 3 | 4 | all: clean format lint lint-docs test coverage build 5 | .PHONY: all 6 | 7 | build: 8 | ./gradlew build 9 | .PHONY: build 10 | 11 | clean: 12 | ./gradlew clean 13 | .PHONY: clean 14 | 15 | coverage: 16 | ./gradlew koverHtmlReport koverXmlReport ${GRADLE_ARGS} 17 | .PHONY: coverage 18 | 19 | docs: 20 | ./gradlew dokkaHtml 21 | .PHONY: docs 22 | 23 | format: 24 | ./gradlew formatKotlin 25 | .PHONY: format 26 | 27 | lint: 28 | ./gradlew lintKotlin detekt 29 | .PHONY: lint 30 | 31 | local: 32 | ./gradlew build publishToMavenLocal 33 | .PHONY: local 34 | 35 | publish: 36 | ./scripts/publish.sh ${GITHUB_RUN_NUMBER} 37 | .PHONY: publish 38 | 39 | test: 40 | ./gradlew test 41 | .PHONY: test 42 | 43 | verify-doc: 44 | ./scripts/verify-doc.sh ${BUMP} ${FILE} 45 | .PHONY: verify-doc 46 | 47 | version: 48 | ./scripts/create-release.sh ${BUMP} 49 | .PHONY: version 50 | 51 | # Website 52 | build-docs: 53 | (cd website/ && yarn build) 54 | .PHONY: build-docs 55 | 56 | lint-docs: 57 | npx docusaurus-mdx-checker 58 | .PHONY: lint-docs 59 | 60 | install-docs: 61 | (cd website/ && yarn install) 62 | .PHONY: install-docs 63 | 64 | start-docs: 65 | (cd website/ && yarn start) 66 | .PHONY: start-docs 67 | 68 | upgrade-docs: 69 | (cd website/ && yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest @docusaurus/module-type-aliases@latest) 70 | .PHONY: upgrade-docs 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Model Forge 🔥🔨 2 | 3 | [![Build Status](https://github.com/HelloCuriosity/model-forge/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/HelloCuriosity/model-forge/actions) 4 | [![codecov](https://codecov.io/gh/HelloCuriosity/model-forge/branch/main/graph/badge.svg?token=0P2Q8SLFO7)](https://codecov.io/gh/HelloCuriosity/model-forge) 5 | [![License](https://img.shields.io/dub/l/vibe-d.svg)](https://github.com/HelloCuriosity/model-forge/blob/main/LICENSE) 6 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) 7 | [![](https://img.shields.io/maven-central/v/io.github.hellocuriosity/model-forge?color=blue)](https://search.maven.org/search?q=io.github.hellocuriosity) 8 | [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.hellocuriosity/model-forge?server=https%3A%2F%2Fs01.oss.sonatype.org)](https://s01.oss.sonatype.org/content/repositories/snapshots/io/github/hellocuriosity/model-forge/) 9 | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) 10 | 11 | ## About 12 | 13 | Model Forge is a library to automate model generation for automated testing: 14 | 15 | - unit 16 | - integration 17 | - etc. 18 | 19 | ## Getting Started 20 | 21 | ### Gradle Setup 22 | 23 |
24 | Kotlin 25 | 26 | ```kotlin 27 | dependencies { 28 | testImplementation("io.github.hellocuriosity:model-forge:1.5.1") 29 | } 30 | ``` 31 | 32 |
33 | 34 |
35 | Groovy 36 | 37 | ```groovy 38 | dependencies { 39 | testImplementation 'io.github.hellocuriosity:model-forge:1.5.1' 40 | } 41 | ``` 42 | 43 |
44 | 45 | ### Feeling Adventurous 💥 46 | 47 | If you're feeling adventurous you can be on the cutting edge and test a snapshot: 48 | 49 |
50 | Kotlin 51 | 52 | ```kotlin 53 | repositories { 54 | maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") 55 | } 56 | 57 | dependencies { 58 | testImplementation("io.github.hellocuriosity:model-forge:1.5.1.xx-SNAPSHOT") 59 | } 60 | ``` 61 | 62 |
63 | 64 |
65 | Groovy 66 | 67 | ```groovy 68 | repositories { 69 | maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } 70 | } 71 | 72 | dependencies { 73 | testImplementation 'io.github.hellocuriosity:model-forge:1.5.1.xx-SNAPSHOT' 74 | } 75 | ``` 76 | 77 |
78 | 79 | ### Define your model 80 | 81 | ```kotlin 82 | data class Employee( 83 | val id: Long, 84 | val name: String, 85 | val dob: Instant, 86 | ) 87 | ``` 88 | 89 | ### Generate model 90 | 91 | ```kotlin 92 | val forge = ModelForge() 93 | val testObject = forge.build() 94 | ``` 95 | 96 | or by delegating 97 | 98 | ```kotlin 99 | val testObject: Employee by forgery() 100 | ``` 101 | 102 | You can create different sized lists by specifying the number of elements. 103 | 104 | ```kotlin 105 | val forge = ModelForge() 106 | val list = forge.buildList(3) 107 | ``` 108 | 109 | or by delegating 110 | 111 | ```kotlin 112 | val testObjects: List by forgeryList() 113 | ``` 114 | 115 | ## Custom Provider 116 | 117 | While Model Forge aims to fully automate model generation, you may run into an instance where you need to customize your 118 | data. This is easily achievable by defining a custom provider and adding it to the forge. 119 | 120 | ### Define your provider 121 | 122 | ```kotlin 123 | val testProvider: Provider = Provider { 124 | Employee( 125 | id = 15L, 126 | name = "Josh", 127 | dob = Instant.ofEpochMilli(1315260000000) 128 | ) 129 | } 130 | ``` 131 | 132 | ### Add your provider to the forge 133 | 134 | ```java 135 | forge.addProvider(TestObject.class,testProvider); 136 | ``` 137 | 138 | or Kotlin 139 | 140 | ```kotlin 141 | forge.addProvider(testProvider) 142 | ``` 143 | 144 | ### Inline your provider(s) 145 | 146 | Alternatively you can add your forgery providers inline 147 | 148 | ```kotlin 149 | 150 | val forge = ModelForge().apply { 151 | addProvider { 152 | Employee( 153 | id = 2L, 154 | name = "Hendrik", 155 | dob = Instant.ofEpochMilli(1574486400000) 156 | ) 157 | } 158 | } 159 | val employee by forgery(forge) 160 | 161 | ``` 162 | 163 | ## Supported Types 164 | 165 | Model Forge currently supports the auto generation for the following types: 166 | 167 | ### Types 168 | 169 | * Boolean 170 | * Byte 171 | * Calendar 172 | * Char 173 | * Date 174 | * Double 175 | * Enums 176 | * File 177 | * Float 178 | * Int 179 | * Instant 180 | * Long 181 | * Short 182 | * String 183 | * UByte 184 | * UInt 185 | * ULong 186 | * UShort 187 | * UUID 188 | 189 | ### Collections 190 | 191 | * List 192 | * Map 193 | * Set 194 | 195 | _Can't find your data type? Feel free to create a pull request or open an issue_ 🪂 196 | 197 | ## Contributing 198 | 199 | Thanks for showing your interest in wanting to improve Model Forge. Before you get started take a look at our 200 | [code of conduct](CODE_OF_CONDUCT.md) and [contribution](CONTRIBUTING.md) guides 🙌 201 | 202 | ### Contributors 203 | 204 | If you contribute to Model Forge, please feel free to add yourself to the list! 205 | 206 | * [Kyle Roe](https://github.com/hopeman15) - Maintainer 207 | * [Adriaan Duz](https://github.com/nxtstep) - Contributor 208 | * Kotlin class definitions 209 | * Forgery and forgeries property delegate 210 | * Reified inline extension functions 211 | * [Alicja Kozikowska](https://github.com/Ashlett) - Contributor 212 | * Fix StringProvider treatment of invalid wordCount 213 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import io.gitlab.arturbosch.detekt.Detekt 2 | import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask 3 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 4 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 5 | 6 | allprojects { 7 | group = "io.github.hellocuriosity" 8 | version = System.getenv("VERSION") ?: "local" 9 | 10 | apply(plugin = "org.jetbrains.kotlin.jvm") 11 | apply(plugin = "org.jetbrains.kotlinx.kover") 12 | 13 | kotlin { 14 | jvmToolchain(17) 15 | } 16 | } 17 | 18 | plugins { 19 | kotlin("jvm") version libs.versions.kotlin.get() 20 | 21 | // Quality gate 22 | alias(libs.plugins.kotlinter) 23 | alias(libs.plugins.detekt) 24 | alias(libs.plugins.kover) 25 | } 26 | 27 | repositories { 28 | mavenCentral() 29 | } 30 | 31 | dependencies { 32 | implementation(kotlin("stdlib")) 33 | } 34 | 35 | tasks.withType { 36 | compilerOptions { 37 | jvmTarget.set(JvmTarget.JVM_17) 38 | } 39 | } 40 | 41 | tasks.test { 42 | useJUnit() 43 | } 44 | 45 | tasks.withType().configureEach { 46 | jvmTarget = "17" 47 | reports { 48 | html.required.set(true) 49 | xml.required.set(false) 50 | txt.required.set(false) 51 | sarif.required.set(false) 52 | } 53 | } 54 | 55 | tasks.withType().configureEach { 56 | jvmTarget = "17" 57 | } 58 | 59 | // Kover 60 | dependencies { 61 | kover(project(":forge-core")) 62 | } 63 | -------------------------------------------------------------------------------- /forge-core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.JavadocJar 2 | import com.vanniktech.maven.publish.KotlinJvm 3 | import com.vanniktech.maven.publish.SonatypeHost 4 | 5 | plugins { 6 | kotlin("jvm") 7 | 8 | // Quality gate 9 | alias(libs.plugins.kotlinter) 10 | alias(libs.plugins.detekt) 11 | 12 | // Publishing 13 | alias(libs.plugins.vanniktech) 14 | 15 | // Documentation 16 | alias(libs.plugins.dokka) 17 | } 18 | 19 | repositories { 20 | mavenCentral() 21 | } 22 | 23 | dependencies { 24 | implementation(libs.objenesis) 25 | implementation(libs.lorem) 26 | // Javax inject 27 | api(libs.inject) 28 | 29 | // Testing 30 | testImplementation(project(":forge-test-utils")) 31 | testImplementation(libs.junitJupiter) 32 | testImplementation(kotlin("test-junit")) 33 | testImplementation(libs.mockK) 34 | } 35 | 36 | mavenPublishing { 37 | publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) 38 | signAllPublications() 39 | 40 | coordinates( 41 | "io.github.hellocuriosity", 42 | "model-forge", 43 | System.getenv("VERSION") ?: "local" 44 | ) 45 | 46 | configure( 47 | KotlinJvm( 48 | javadocJar = JavadocJar.Dokka("dokkaHtml"), 49 | sourcesJar = true, 50 | ) 51 | ) 52 | 53 | pom { 54 | name.set("Model Forge") 55 | description.set("Model Forge is a library to automate model generation for automated testing.") 56 | url.set("https://github.com/HelloCuriosity/model-forge") 57 | licenses { 58 | license { 59 | name.set("MIT Licence") 60 | url.set("https://github.com/HelloCuriosity/model-forge/blob/main/LICENSE") 61 | } 62 | } 63 | developers { 64 | developer { 65 | id.set("hopeman15") 66 | name.set("Kyle Roe") 67 | } 68 | } 69 | scm { 70 | connection.set("scm:git:https://github.com/HelloCuriosity/model-forge.git") 71 | developerConnection.set("scm:git:https://github.com/HelloCuriosity/model-forge.git") 72 | url.set("https://github.com/HelloCuriosity/model-forge") 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/Attempt.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | @Suppress("SwallowedException") 4 | fun attempt(block: () -> T) = 5 | try { 6 | block() 7 | } catch (e: InstantiationError) { 8 | // Throw model forge exception instead 9 | throw ModelForgeException("${e.message} is not yet supported") 10 | } 11 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/Forgery.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import javax.inject.Provider 4 | 5 | public inline fun forgery(forger: ModelForge = ModelForge()): Lazy = 6 | lazy { 7 | forger.build(T::class) 8 | } 9 | 10 | public inline fun forgeryList( 11 | forger: ModelForge = ModelForge(), 12 | size: Int = 10, 13 | ): Lazy> = lazy { forger.buildList(T::class, size) } 14 | 15 | public inline fun forgerySet( 16 | forger: ModelForge = ModelForge(), 17 | size: Int = 10, 18 | ): Lazy> = lazy { forger.buildSet(T::class, size) } 19 | 20 | public inline fun ModelForge.addProvider(provider: Provider) { 21 | addProvider(T::class.java, provider) 22 | } 23 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/Millis.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | object Millis { 4 | const val NINETEEN_EIGHTY_SIX = 529714800000L 5 | const val TWENTY_TWENTY_ONE = 1617141600000L 6 | } 7 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/ModelForge.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import io.github.hellocuriosity.providers.BooleanProvider 4 | import io.github.hellocuriosity.providers.ByteProvider 5 | import io.github.hellocuriosity.providers.CalendarProvider 6 | import io.github.hellocuriosity.providers.CharProvider 7 | import io.github.hellocuriosity.providers.DateProvider 8 | import io.github.hellocuriosity.providers.DoubleProvider 9 | import io.github.hellocuriosity.providers.FileProvider 10 | import io.github.hellocuriosity.providers.FloatProvider 11 | import io.github.hellocuriosity.providers.InstantProvider 12 | import io.github.hellocuriosity.providers.IntegerProvider 13 | import io.github.hellocuriosity.providers.LongProvider 14 | import io.github.hellocuriosity.providers.ShortProvider 15 | import io.github.hellocuriosity.providers.StringProvider 16 | import io.github.hellocuriosity.providers.UByteProvider 17 | import io.github.hellocuriosity.providers.UIntProvider 18 | import io.github.hellocuriosity.providers.ULongProvider 19 | import io.github.hellocuriosity.providers.UShortProvider 20 | import io.github.hellocuriosity.providers.UuidProvider 21 | import io.github.hellocuriosity.providers.getEnum 22 | import org.objenesis.Objenesis 23 | import org.objenesis.ObjenesisStd 24 | import org.objenesis.instantiator.ObjectInstantiator 25 | import java.io.File 26 | import java.lang.reflect.Field 27 | import java.lang.reflect.Modifier 28 | import java.lang.reflect.ParameterizedType 29 | import java.time.Instant 30 | import java.util.Calendar 31 | import java.util.Date 32 | import java.util.UUID 33 | import javax.inject.Provider 34 | 35 | open class ModelForge { 36 | val providers: HashMap, Provider<*>> = HashMap() 37 | 38 | /** 39 | * Creates an automatically generated model object 40 | * 41 | * @param Type to instantiate 42 | * @param clazz Class to instantiate 43 | * 44 | * @return Instance of clazz 45 | */ 46 | open fun build(clazz: Class): T = 47 | realBuild(clazz) ?: throw ModelForgeException("Could not create a forgery for: ${clazz.name}") 48 | 49 | private fun realBuild(clazz: Class): T? { 50 | if (providers.isNotEmpty() && clazz != List::class.java) { 51 | providers[clazz]?.let { return it.get() as T } 52 | } 53 | 54 | return clazz.generate { 55 | if (clazz.classLoader == null) { 56 | return@generate null 57 | } 58 | attempt { 59 | val objenesis: Objenesis = ObjenesisStd() 60 | val instantiator: ObjectInstantiator = objenesis.getInstantiatorOf(clazz) 61 | instantiator.newInstance() as T 62 | }.also { model -> 63 | clazz.eligibleFields().map { field -> 64 | field.isAccessible = true 65 | when (field.type) { 66 | List::class.java -> field.set(model, field.getValues()) 67 | Map::class.java -> field.set(model, field.getValues()) 68 | Set::class.java -> field.set(model, field.getValues()) 69 | else -> 70 | realBuild(field.type)?.also { 71 | field.set(model, it) 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * Creates an automatically generated model list with 81 | * or without a specified size. 82 | * 83 | * @param Type to instantiate 84 | * @param clazz Class to instantiate 85 | * @param size Int number of models to create (defaulting to 10) 86 | * 87 | * @return Instance of clazz 88 | */ 89 | open fun buildList( 90 | clazz: Class, 91 | size: Int = 10, 92 | ): List { 93 | val list = mutableListOf() 94 | for (i in 0 until size) { 95 | list.add(i, build(clazz)) 96 | } 97 | return list 98 | } 99 | 100 | /** 101 | * Creates an automatically generated model map with 102 | * a specified size. 103 | * 104 | * @param type ParameterizedType to instantiate 105 | * @param size Int number of models to create 106 | * 107 | * @return Instance of clazz 108 | */ 109 | private fun buildMap( 110 | type: ParameterizedType, 111 | size: Int, 112 | ): Map { 113 | val key = type.actualTypeArguments[0] as Class<*> 114 | val value = type.actualTypeArguments[1] as Class<*> 115 | 116 | val map = 117 | (0 until size).associate { 118 | build(key) to build(value) 119 | } 120 | 121 | return map 122 | } 123 | 124 | /** 125 | * Creates an automatically generated model set with 126 | * or without a specified size. 127 | * 128 | * @param Type to instantiate 129 | * @param clazz Class to instantiate 130 | * @param size Int number of models to create (defaulting to 10) 131 | * 132 | * @return Instance of clazz 133 | */ 134 | open fun buildSet( 135 | clazz: Class, 136 | size: Int = 10, 137 | ): Set { 138 | val set = mutableSetOf() 139 | for (i in 0 until size) { 140 | set.add(build(clazz)) 141 | } 142 | return set 143 | } 144 | 145 | /** 146 | * Adds a custom provider for generating models 147 | * 148 | * @param Type to instantiate 149 | * @param clazz Class to instantiate 150 | * @param provider Provider for populating data 151 | * 152 | */ 153 | open fun addProvider( 154 | clazz: Class, 155 | provider: Provider, 156 | ) { 157 | providers[clazz] = provider 158 | } 159 | 160 | /** 161 | * Get all eligible fields for model generation 162 | * 163 | * @param Type to instantiate 164 | * @param clazz Class (declaredFields) to filter 165 | * 166 | * @return Array of eligible fields 167 | */ 168 | private fun Class.eligibleFields(): Array = 169 | this.declaredFields 170 | .filter { field -> !Modifier.isTransient(field.modifiers) && !Modifier.isStatic(field.modifiers) } 171 | .toTypedArray() 172 | 173 | /** 174 | * Auto generate values for specific providers 175 | * 176 | * @param Type to instantiate 177 | * @param clazz Class (type) to auto generate 178 | * 179 | * @return Any autogenerated values 180 | */ 181 | @Suppress("ComplexMethod") 182 | private fun Class.generate(orDefault: () -> T?): T? = 183 | when (this) { 184 | Boolean::class.java, java.lang.Boolean::class.java -> BooleanProvider().get() as T 185 | Byte::class.java, java.lang.Byte::class.java -> ByteProvider().get() as T 186 | Calendar::class.java -> CalendarProvider().get() as T 187 | Char::class.java, java.lang.Character::class.java -> CharProvider().get() as T 188 | Date::class.java -> DateProvider().get() as T 189 | Double::class.java, java.lang.Double::class.java -> DoubleProvider().get() as T 190 | File::class.java -> FileProvider().get() as T 191 | Float::class.java, java.lang.Float::class.java -> FloatProvider().get() as T 192 | Int::class.java, java.lang.Integer::class.java -> IntegerProvider().get() as T 193 | Instant::class.java -> InstantProvider().get() as T 194 | Long::class.java, java.lang.Long::class.java -> LongProvider().get() as T 195 | Short::class.java, java.lang.Short::class.java -> ShortProvider().get() as T 196 | String::class.java, java.lang.String::class.java -> StringProvider().get() as T 197 | UByte::class.java -> UByteProvider().get() as T 198 | UInt::class.java -> UIntProvider().get() as T 199 | ULong::class.java -> ULongProvider().get() as T 200 | UShort::class.java -> UShortProvider().get() as T 201 | UUID::class.java -> UuidProvider().get() as T 202 | else -> if (isEnum) getEnum() as T else orDefault() 203 | } 204 | 205 | /** 206 | * Auto generate values 207 | * 208 | * @param Field Field needed to determine list type 209 | * @param size Int number of items to create (defaulting to 10) 210 | * 211 | * @return Autogenerated values 212 | */ 213 | private fun Field.getValues(size: Int = 10): Any { 214 | val type = genericType as ParameterizedType 215 | val clazz = type.actualTypeArguments.first() as Class<*> 216 | 217 | return when (this.type) { 218 | List::class.java -> buildList(clazz, size) 219 | Map::class.java -> buildMap(type, size) 220 | Set::class.java -> buildSet(clazz, size) 221 | else -> throw ModelForgeException("Could not create values for: ${clazz.name}") 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/ModelForgeException.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import java.lang.RuntimeException 4 | 5 | class ModelForgeException( 6 | message: String, 7 | ) : RuntimeException(message) 8 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/ModelForgeKotlin.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import javax.inject.Provider 4 | import kotlin.reflect.KClass 5 | 6 | /** 7 | * Creates an automatically generated model object 8 | * 9 | * @param Type to instantiate 10 | * @param clazz Class to instantiate 11 | * 12 | * @return Instance of clazz 13 | */ 14 | public fun ModelForge.build(clazz: KClass): T = build(clazz.java) 15 | 16 | /** 17 | * Creates an automatically generated model list with 18 | * or without a specified size. 19 | * 20 | * @param Type to instantiate 21 | * @param clazz Class to instantiate 22 | * @param size Int number of models to create (defaulting to 10) 23 | * 24 | * @return Instance of clazz 25 | */ 26 | public fun ModelForge.buildList( 27 | clazz: KClass, 28 | size: Int = 10, 29 | ): List = buildList(clazz.java, size) 30 | 31 | /** 32 | * Creates an automatically generated model set with 33 | * or without a specified size. 34 | * 35 | * @param Type to instantiate 36 | * @param clazz Class to instantiate 37 | * @param size Int number of models to create (defaulting to 10) 38 | * 39 | * @return Instance of clazz 40 | */ 41 | public fun ModelForge.buildSet( 42 | clazz: KClass, 43 | size: Int = 10, 44 | ): Set = buildSet(clazz.java, size) 45 | 46 | /** 47 | * Adds a custom provider for generating models 48 | * 49 | * @param Type to instantiate 50 | * @param clazz Class to instantiate 51 | * @param provider Provider for populating data 52 | * 53 | */ 54 | public fun ModelForge.addProvider( 55 | clazz: KClass, 56 | provider: Provider, 57 | ) = addProvider(clazz.java, provider) 58 | 59 | public inline fun ModelForge.build(): T = build(T::class) 60 | 61 | public inline fun ModelForge.buildList(size: Int = 10): List = buildList(T::class, size) 62 | 63 | public inline fun ModelForge.buildSet(size: Int = 10): Set = buildSet(T::class, size) 64 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/BooleanProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | 5 | /** 6 | * Auto generates a boolean 7 | * 8 | * @param random random generator 9 | * 10 | * @return Boolean instance 11 | */ 12 | class BooleanProvider( 13 | private val random: Random = Random, 14 | ) : Provider { 15 | override fun get(): Boolean = random.nextBoolean() 16 | } 17 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/ByteProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | /** 4 | * Auto generates a Byte between two values 5 | * 6 | * @param min minimum value 7 | * @param max maximum value 8 | * @param provider IntegerProvider 9 | * 10 | * @return Byte instance 11 | */ 12 | class ByteProvider( 13 | private val min: Int = Byte.MIN_VALUE.toInt(), 14 | private val max: Int = Byte.MAX_VALUE.toInt(), 15 | private val provider: IntegerProvider = 16 | IntegerProvider(min, max), 17 | ) : Provider { 18 | override fun get(): Byte = provider.get().toByte() 19 | } 20 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/CalendarProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.Millis.NINETEEN_EIGHTY_SIX 4 | import io.github.hellocuriosity.Millis.TWENTY_TWENTY_ONE 5 | import java.util.Calendar 6 | 7 | /** 8 | * Auto generates a Calendar instance between two dates 9 | * 10 | * @param from start date in millis 11 | * @param until end date in millis 12 | * @param dateProvider data generator 13 | * 14 | * @return Instance of Calendar 15 | */ 16 | class CalendarProvider( 17 | private val from: Long = NINETEEN_EIGHTY_SIX, 18 | private val until: Long = TWENTY_TWENTY_ONE, 19 | private val dateProvider: DateProvider = DateProvider(from, until), 20 | ) : Provider { 21 | override fun get(): Calendar { 22 | val calendar = Calendar.getInstance() 23 | calendar.time = dateProvider.get() 24 | return calendar 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/CharProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | /** 4 | * Auto generates a Char 5 | * 6 | * @param chars list of possible characters 7 | * 8 | * @return Char instance 9 | */ 10 | class CharProvider( 11 | private val chars: List = DEFAULT_CHAR_LIST, 12 | ) : Provider { 13 | companion object { 14 | val DEFAULT_CHAR_LIST = ('a'..'z') + ('A'..'Z') + ('0'..'9') 15 | } 16 | 17 | override fun get(): Char = chars.random() 18 | } 19 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/DateProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.Millis.NINETEEN_EIGHTY_SIX 4 | import io.github.hellocuriosity.Millis.TWENTY_TWENTY_ONE 5 | import java.util.Date 6 | import kotlin.random.Random 7 | 8 | /** 9 | * Auto generates a Date between two dates 10 | * 11 | * @param from start date in millis 12 | * @param until end date in millis 13 | * @param random random generator 14 | * 15 | * @return Date instance 16 | */ 17 | class DateProvider( 18 | private val from: Long = NINETEEN_EIGHTY_SIX, 19 | private val until: Long = TWENTY_TWENTY_ONE, 20 | private val random: Random = Random, 21 | ) : Provider { 22 | override fun get(): Date = Date(random.nextLong(from, until)) 23 | } 24 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/DoubleProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | 5 | /** 6 | * Auto generates a Double between two values 7 | * 8 | * @param min minimum value 9 | * @param max maximum value 10 | * @param random random generator 11 | * 12 | * @return Double instance 13 | */ 14 | class DoubleProvider( 15 | private val min: Double = Double.MIN_VALUE, 16 | private val max: Double = Double.MAX_VALUE, 17 | private val random: Random = Random, 18 | ) : Provider { 19 | override fun get(): Double = random.nextDouble(min, max) 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/FileProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import java.io.File 4 | 5 | class FileProvider( 6 | private val stringProvider: StringProvider = StringProvider(), 7 | ) : Provider { 8 | override fun get(): File = File(stringProvider.get()) 9 | } 10 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/FloatProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | 5 | /** 6 | * Auto generates a Float between two values 7 | * 8 | * @param min minimum value 9 | * @param max maximum value 10 | * @param random random generator 11 | * 12 | * @return Float instance 13 | */ 14 | class FloatProvider( 15 | private val min: Double = Float.MIN_VALUE.toDouble(), 16 | private val max: Double = Float.MAX_VALUE.toDouble(), 17 | private val random: Random = Random, 18 | ) : Provider { 19 | override fun get(): Float = random.nextDouble(min, max).toFloat() 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/GetEnum.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.ModelForgeException 4 | import kotlin.random.Random 5 | 6 | /** 7 | * Auto generates an enum value 8 | * 9 | * @param Type to instantiate 10 | * @param random random generator 11 | * 12 | * @return enum value 13 | */ 14 | fun Class.getEnum(random: Random = Random): Any { 15 | if (enumConstants.isNotEmpty()) { 16 | val idx = random.nextInt(enumConstants.size) 17 | return enumConstants[idx] ?: throw ModelForgeException("$this is null") 18 | } else { 19 | throw ModelForgeException("$this does not define enum values") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/InstantProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.Millis.NINETEEN_EIGHTY_SIX 4 | import io.github.hellocuriosity.Millis.TWENTY_TWENTY_ONE 5 | import java.time.Instant 6 | import kotlin.random.Random 7 | 8 | /** 9 | * Auto generates an Instant instance between two dates 10 | * 11 | * @param from start date in millis 12 | * @param until end date in millis 13 | * @param random random generator 14 | * 15 | * @return Instance of Instant 16 | */ 17 | class InstantProvider( 18 | private val from: Long = NINETEEN_EIGHTY_SIX, 19 | private val until: Long = TWENTY_TWENTY_ONE, 20 | private val random: Random = Random, 21 | ) : Provider { 22 | override fun get(): Instant = Instant.ofEpochMilli(random.nextLong(from, until)) 23 | } 24 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/IntegerProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | 5 | /** 6 | * Auto generates an Int between two values 7 | * 8 | * @param min minimum value 9 | * @param max maximum value 10 | * @param random random generator 11 | * 12 | * @return Int instance 13 | */ 14 | class IntegerProvider( 15 | private val min: Int = Int.MIN_VALUE, 16 | private val max: Int = Int.MAX_VALUE, 17 | private val random: Random = Random, 18 | ) : Provider { 19 | override fun get(): Int = random.nextInt(min, max) 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/LongProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | 5 | /** 6 | * Auto generates a Long between two values 7 | * 8 | * @param min minimum value 9 | * @param max maximum value 10 | * @param random random generator 11 | * 12 | * @return Long instance 13 | */ 14 | class LongProvider( 15 | private val min: Long = Long.MIN_VALUE, 16 | private val max: Long = Long.MAX_VALUE, 17 | private val random: Random = Random, 18 | ) : Provider { 19 | override fun get(): Long = random.nextLong(min, max) 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/Provider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import javax.inject.Provider as JavaxProvider 4 | 5 | /** 6 | * Legacy support for the removed Provider interface 7 | */ 8 | typealias Provider = JavaxProvider 9 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/ShortProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | /** 4 | * Auto generates a Short between two values 5 | * 6 | * @param min minimum value 7 | * @param max maximum value 8 | * @param provider IntegerProvider 9 | * 10 | * @return Short instance 11 | */ 12 | class ShortProvider( 13 | private val min: Int = Short.MIN_VALUE.toInt(), 14 | private val max: Int = Short.MAX_VALUE.toInt(), 15 | private val provider: IntegerProvider = IntegerProvider(min, max), 16 | ) : Provider { 17 | override fun get(): Short = provider.get().toShort() 18 | } 19 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/StringProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import com.thedeanda.lorem.LoremIpsum 4 | import io.github.hellocuriosity.ModelForgeException 5 | 6 | /** 7 | * Auto generates a String 8 | * 9 | * @param wordCount amount of words to be generated 10 | * @param lorem lorem ipsum generator 11 | * 12 | * @return String instance 13 | */ 14 | class StringProvider( 15 | private val wordCount: Int = DEFAULT_VALUE, 16 | private val lorem: LoremIpsum = LoremIpsum.getInstance(), 17 | ) : Provider { 18 | companion object { 19 | const val DEFAULT_VALUE = 1 20 | } 21 | 22 | override fun get(): String = 23 | when { 24 | wordCount < 0 -> throw ModelForgeException("Cannot generate string of $wordCount words") 25 | wordCount == 0 -> "" 26 | else -> lorem.getWords(wordCount) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/UByteProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | /** 4 | * Auto generates a UByte between two values 5 | * 6 | * @param min minimum value 7 | * @param max maximum value 8 | * @param provider UIntProvider 9 | * 10 | * @return UByte instance 11 | */ 12 | class UByteProvider( 13 | private val min: UInt = UByte.MIN_VALUE.toUInt(), 14 | private val max: UInt = UByte.MAX_VALUE.toUInt(), 15 | private val provider: UIntProvider = 16 | UIntProvider(min, max), 17 | ) : Provider { 18 | override fun get(): UByte = provider.get().toUByte() 19 | } 20 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/UIntProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | import kotlin.random.nextUInt 5 | 6 | /** 7 | * Auto generates an UInt between two values 8 | * 9 | * @param min minimum value 10 | * @param max maximum value 11 | * @param random random generator 12 | * 13 | * @return UInt instance 14 | */ 15 | class UIntProvider( 16 | private val min: UInt = UInt.MIN_VALUE, 17 | private val max: UInt = UInt.MAX_VALUE, 18 | private val random: Random = Random, 19 | ) : Provider { 20 | override fun get(): UInt = random.nextUInt(min, max) 21 | } 22 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/ULongProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.random.Random 4 | import kotlin.random.nextULong 5 | 6 | /** 7 | * Auto generates a ULong between two values 8 | * 9 | * @param min minimum value 10 | * @param max maximum value 11 | * @param random random generator 12 | * 13 | * @return ULong instance 14 | */ 15 | class ULongProvider( 16 | private val min: ULong = ULong.MIN_VALUE, 17 | private val max: ULong = ULong.MAX_VALUE, 18 | private val random: Random = Random, 19 | ) : Provider { 20 | override fun get(): ULong = random.nextULong(min, max) 21 | } 22 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/UShortProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | /** 4 | * Auto generates a UShort between two values 5 | * 6 | * @param min minimum value 7 | * @param max maximum value 8 | * @param provider UIntProvider 9 | * 10 | * @return UShort instance 11 | */ 12 | class UShortProvider( 13 | private val min: UInt = UShort.MIN_VALUE.toUInt(), 14 | private val max: UInt = UShort.MAX_VALUE.toUInt(), 15 | private val provider: UIntProvider = UIntProvider(min, max), 16 | ) : Provider { 17 | override fun get(): UShort = provider.get().toUShort() 18 | } 19 | -------------------------------------------------------------------------------- /forge-core/src/main/kotlin/io/github/hellocuriosity/providers/UuidProvider.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import java.util.UUID 4 | 5 | /** 6 | * Auto generates an UUID 7 | * 8 | * @return UUID instance 9 | */ 10 | class UuidProvider : Provider { 11 | override fun get(): UUID = UUID.randomUUID() 12 | } 13 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/ForgeryTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import org.junit.Test 4 | import java.time.Instant 5 | import kotlin.random.Random 6 | import kotlin.test.assertEquals 7 | import kotlin.test.assertNull 8 | 9 | class ForgeryTest { 10 | @Test 11 | fun testForgery() { 12 | val testObject: TestObject by forgery() 13 | testObject.assert() 14 | } 15 | 16 | @Test 17 | fun testEnumForgery() { 18 | val testEnum by forgery() 19 | 20 | val rawValue = testEnum.value 21 | assertEquals(testEnum, TestEnumWithValue::value.safeFindEnumCase(rawValue)) 22 | } 23 | 24 | @Test 25 | fun testForgeryListDefault() { 26 | val list: List by forgeryList() 27 | assertEquals(10, list.size) 28 | list.map { testObject -> 29 | testObject.assert() 30 | } 31 | } 32 | 33 | @Test 34 | fun testForgeryListWithSize() { 35 | val size = 3 36 | val list: List by forgeryList(size = size) 37 | assertEquals(size, list.size) 38 | list.map { testObject -> 39 | testObject.assert() 40 | } 41 | } 42 | 43 | @Test 44 | fun testForgerySetDefault() { 45 | val set: Set by forgerySet() 46 | assertEquals(10, set.size) 47 | set.map { testObject -> 48 | testObject.assert() 49 | } 50 | } 51 | 52 | @Test 53 | fun testForgerySetWithSize() { 54 | val size = 3 55 | val set: Set by forgerySet(size = size) 56 | assertEquals(size, set.size) 57 | set.map { testObject -> 58 | testObject.assert() 59 | } 60 | } 61 | 62 | @Test(expected = ModelForgeException::class) 63 | fun testForgeryWithUnsupportedType() { 64 | data class UnsupportedTestObject( 65 | private val random: Random, 66 | ) 67 | 68 | val testObject: UnsupportedTestObject by forgery() 69 | assertNull(testObject) 70 | } 71 | 72 | @Test 73 | fun testForgeryWithProvider() { 74 | data class FancyObject( 75 | val retrieveMe: String = "wrong it", 76 | ) 77 | 78 | class FancyHolder( 79 | val value: FancyObject, 80 | ) 81 | 82 | val forger = 83 | ModelForge().apply { 84 | addProvider { 85 | FancyObject("got it") 86 | } 87 | } 88 | 89 | val fancyHolder by forgery(forger) 90 | assertEquals("got it", fancyHolder.value.retrieveMe) 91 | } 92 | 93 | @Test 94 | fun testReadmeExample() { 95 | data class Employee( 96 | val id: Long, 97 | val name: String, 98 | val dob: Instant, 99 | ) 100 | 101 | val forge = 102 | ModelForge().apply { 103 | addProvider { 104 | Employee( 105 | id = 2L, 106 | name = "Hendrik", 107 | dob = Instant.ofEpochMilli(1574486400000), 108 | ) 109 | } 110 | } 111 | val employee by forgery(forge) 112 | assertEquals("Hendrik", employee.name) 113 | } 114 | } 115 | 116 | /** 117 | * Find the enum case or null when not found. 118 | * The receiver is the predicate. This allows for `Enum::value.safeFindEnumCase("value")` on an Enum(val value: String) 119 | */ 120 | private inline fun , V> ((T) -> V).safeFindEnumCase(value: V): T? = 121 | enumValues().firstOrNull { 122 | this(it) == value 123 | } 124 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/ModelForgeExceptionTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class ModelForgeExceptionTest { 7 | @Test 8 | fun testException() { 9 | val message = "test message" 10 | val exception = ModelForgeException(message) 11 | 12 | assertEquals(message, exception.message) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/ModelForgeKotlinTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import org.junit.Test 4 | import kotlin.random.Random 5 | import kotlin.test.assertEquals 6 | import kotlin.test.assertNull 7 | 8 | class ModelForgeKotlinTest { 9 | private val forge = ModelForge() 10 | 11 | @Test 12 | fun testBuild() { 13 | val testObject = forge.build(TestObject::class) 14 | testObject.assert() 15 | } 16 | 17 | @Test 18 | fun testReifiedBuild() { 19 | val testObject: TestObject = forge.build() 20 | testObject.assert() 21 | } 22 | 23 | @Test 24 | fun testBuildWithProvider() { 25 | forge.addProvider(TestProviderObject::class, testProvider) 26 | val testObject = forge.build(TestProviderObject::class) 27 | testObject.assert() 28 | } 29 | 30 | @Test 31 | fun testBuildListDefault() { 32 | val list = forge.buildList(TestObject::class) 33 | assertEquals(10, list.size) 34 | list.map { testObject -> 35 | testObject.assert() 36 | } 37 | } 38 | 39 | @Test 40 | fun testBuildListWithSize() { 41 | val size = 3 42 | val list = forge.buildList(TestObject::class, size) 43 | assertEquals(size, list.size) 44 | list.map { testObject -> 45 | testObject.assert() 46 | } 47 | } 48 | 49 | @Test 50 | fun testReifiedBuildListWithSize() { 51 | val size = 3 52 | val list = forge.buildList(size) 53 | assertEquals(size, list.size) 54 | list.map { testObject -> 55 | testObject.assert() 56 | } 57 | } 58 | 59 | @Test(expected = ModelForgeException::class) 60 | fun testBuildWithUnsupportedType() { 61 | data class UnsupportedTestObject( 62 | private val random: Random, 63 | ) 64 | 65 | val testObject = forge.build(UnsupportedTestObject::class) 66 | assertNull(testObject) 67 | } 68 | 69 | @Test 70 | fun testBuildSetDefault() { 71 | val set = forge.buildSet(TestObject::class) 72 | assertEquals(10, set.size) 73 | set.map { testObject -> 74 | testObject.assert() 75 | } 76 | } 77 | 78 | @Test 79 | fun testBuildSetWithSize() { 80 | val size = 3 81 | val set = forge.buildSet(TestObject::class, size) 82 | assertEquals(size, set.size) 83 | set.map { testObject -> 84 | testObject.assert() 85 | } 86 | } 87 | 88 | @Test 89 | fun testReifiedBuildSetWithSize() { 90 | val size = 3 91 | val set = forge.buildSet(size) 92 | assertEquals(size, set.size) 93 | set.map { testObject -> 94 | testObject.assert() 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/ModelForgeTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import org.junit.Test 4 | import kotlin.random.Random 5 | import kotlin.test.assertEquals 6 | import kotlin.test.assertNull 7 | 8 | class ModelForgeTest { 9 | private val forge = ModelForge() 10 | 11 | @Test 12 | fun testBuild() { 13 | val testObject = forge.build(TestObject::class.java) 14 | testObject.assert() 15 | } 16 | 17 | @Test 18 | fun testBuildWithProvider() { 19 | forge.addProvider(TestProviderObject::class.java, testProvider) 20 | val testObject = forge.build(TestProviderObject::class.java) 21 | testObject.assert() 22 | } 23 | 24 | @Test(expected = ModelForgeException::class) 25 | fun testBuildWithUnsupportedType() { 26 | data class UnsupportedTestObject( 27 | private val random: Random, 28 | ) 29 | 30 | val testObject = forge.build(UnsupportedTestObject::class.java) 31 | assertNull(testObject) 32 | } 33 | 34 | @Test 35 | fun testBuildListDefault() { 36 | val list = forge.buildList(TestObject::class.java) 37 | assertEquals(10, list.size) 38 | list.map { testObject -> 39 | testObject.assert() 40 | } 41 | } 42 | 43 | @Test 44 | fun testBuildListWithSize() { 45 | val size = 3 46 | val list = forge.buildList(TestObject::class.java, size) 47 | assertEquals(size, list.size) 48 | list.map { testObject -> 49 | testObject.assert() 50 | } 51 | } 52 | 53 | @Test 54 | fun testBuildSetDefault() { 55 | val set = forge.buildSet(TestObject::class.java) 56 | assertEquals(10, set.size) 57 | for (testObject in set) { 58 | testObject.assert() 59 | } 60 | } 61 | 62 | @Test 63 | fun testBuildSetWithSize() { 64 | val size = 3 65 | val set = forge.buildSet(TestObject::class.java, size) 66 | assertEquals(size, set.size) 67 | for (testObject in set) { 68 | testObject.assert() 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/TestProviderObject.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import javax.inject.Provider 4 | import kotlin.test.assertEquals 5 | import kotlin.test.assertTrue 6 | 7 | data class TestProviderObject( 8 | val booleanValue: Boolean, 9 | val enumValue: TestEnum, 10 | val floatValue: Float, 11 | val intValue: Int, 12 | val stringValue: String, 13 | ) 14 | 15 | val testProvider: Provider = 16 | Provider { 17 | TestProviderObject( 18 | booleanValue = true, 19 | enumValue = TestEnum.TWO, 20 | floatValue = 15.0f, 21 | intValue = 15, 22 | stringValue = "custom string", 23 | ) 24 | } 25 | 26 | fun TestProviderObject.assert() { 27 | assertTrue(this.booleanValue) 28 | assertEquals(TestEnum.TWO, this.enumValue) 29 | assertEquals(15.0f, this.floatValue) 30 | assertEquals(15, this.intValue) 31 | assertEquals("custom string", this.stringValue) 32 | } 33 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/BooleanProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.random.Random 10 | import kotlin.test.assertTrue 11 | 12 | class BooleanProviderTest { 13 | private val random: Random = mockk() 14 | 15 | @After 16 | fun teardown() { 17 | confirmVerified(random) 18 | } 19 | 20 | @Test 21 | fun testGet() { 22 | every { random.nextBoolean() } returns true 23 | 24 | val boolean = BooleanProvider(random = random).get() 25 | assertTrue(boolean) 26 | 27 | verify { random.nextBoolean() } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/ByteProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertTrue 5 | 6 | class ByteProviderTest { 7 | @Test 8 | fun testGet() { 9 | val byte = ByteProvider().get() 10 | assertTrue(byte in Byte.MIN_VALUE until Byte.MAX_VALUE) 11 | } 12 | 13 | @Test 14 | fun testGetWithCustomMaxMin() { 15 | val min = 5 16 | val max = 10 17 | val byte = ByteProvider(min = min, max = max).get() 18 | assertTrue(byte in min until max) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/CalendarProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.test.assertEquals 10 | import kotlin.test.assertTrue 11 | 12 | class CalendarProviderTest { 13 | private val dateProvider: DateProvider = mockk() 14 | 15 | @After 16 | fun tearDown() { 17 | confirmVerified(dateProvider) 18 | } 19 | 20 | @Test 21 | fun testGet() { 22 | val date = DateProvider().get() 23 | every { dateProvider.get() } returns date 24 | 25 | val calendar = CalendarProvider(dateProvider = dateProvider).get() 26 | assertEquals(date, calendar.time) 27 | 28 | verify { dateProvider.get() } 29 | } 30 | 31 | @Test 32 | fun testGetWithCustomTimeSpan() { 33 | val start = 1L 34 | val end = 2L 35 | val calendar = CalendarProvider(from = start, until = end).get() 36 | assertTrue(calendar.timeInMillis in start until end) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/CharProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertTrue 5 | 6 | class CharProviderTest { 7 | @Test 8 | fun testGet() { 9 | val char = CharProvider().get() 10 | assertTrue(CharProvider.DEFAULT_CHAR_LIST.contains(char)) 11 | } 12 | 13 | @Test 14 | fun testGetWithCustomChars() { 15 | val customChars = listOf('a', 'b') 16 | val char = CharProvider(chars = customChars).get() 17 | assertTrue(customChars.contains(char)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/DateProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.Millis.NINETEEN_EIGHTY_SIX 4 | import io.github.hellocuriosity.Millis.TWENTY_TWENTY_ONE 5 | import io.mockk.confirmVerified 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import io.mockk.verify 9 | import org.junit.After 10 | import org.junit.Test 11 | import kotlin.random.Random 12 | import kotlin.test.assertEquals 13 | import kotlin.test.assertTrue 14 | 15 | class DateProviderTest { 16 | private val random: Random = mockk() 17 | 18 | @After 19 | fun teardown() { 20 | confirmVerified(random) 21 | } 22 | 23 | @Test 24 | fun testGet() { 25 | val date = DateProvider().get() 26 | assertTrue(date.time in NINETEEN_EIGHTY_SIX until TWENTY_TWENTY_ONE) 27 | } 28 | 29 | @Test 30 | fun testGetWithCustomTimeSpan() { 31 | val start = 1L 32 | val end = 2L 33 | val date = DateProvider(from = start, until = end).get() 34 | assertTrue(date.time in start until end) 35 | } 36 | 37 | @Test 38 | fun testGetRandomCalled() { 39 | every { random.nextLong(any(), any()) } returns 1315260000000L // 06.09.2011 40 | 41 | val date = DateProvider(random = random).get() 42 | assertEquals(1315260000000L, date.time) 43 | 44 | verify { random.nextLong(eq(NINETEEN_EIGHTY_SIX), eq(TWENTY_TWENTY_ONE)) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/DoubleProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.random.Random 10 | import kotlin.test.assertEquals 11 | import kotlin.test.assertTrue 12 | 13 | class DoubleProviderTest { 14 | private val random: Random = mockk() 15 | 16 | @After 17 | fun teardown() { 18 | confirmVerified(random) 19 | } 20 | 21 | @Test 22 | fun testGet() { 23 | val double = DoubleProvider().get() 24 | assertTrue(double >= Double.MIN_VALUE) 25 | assertTrue(double <= Double.MAX_VALUE) 26 | } 27 | 28 | @Test 29 | fun testGetWithCustomMaxMin() { 30 | val min = 5.0 31 | val max = 10.0 32 | val double = DoubleProvider(min = min, max = max).get() 33 | assertTrue(double >= min) 34 | assertTrue(double <= max) 35 | } 36 | 37 | @Test 38 | fun testGetRandomCalled() { 39 | val randomDouble = 15.0 40 | every { random.nextDouble(any(), any()) } returns randomDouble 41 | 42 | val min = 5.0 43 | val max = 10.0 44 | val double = 45 | DoubleProvider( 46 | min = min, 47 | max = max, 48 | random = random, 49 | ).get() 50 | 51 | assertEquals(randomDouble, double) 52 | 53 | verify { random.nextDouble(eq(min), eq(max)) } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/EnumProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.ModelForgeException 4 | import io.github.hellocuriosity.TestEnum 5 | import io.mockk.confirmVerified 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import io.mockk.verify 9 | import org.junit.After 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | import org.junit.runners.Parameterized 13 | import org.junit.runners.Suite 14 | import kotlin.random.Random 15 | import kotlin.test.assertEquals 16 | 17 | @RunWith(Suite::class) 18 | @Suite.SuiteClasses( 19 | EnumProviderTest.TestEnums::class, 20 | EnumProviderTest.TestExceptions::class, 21 | ) 22 | class EnumProviderTest { 23 | @RunWith(Parameterized::class) 24 | open class TestEnums( 25 | private val expected: TestEnum, 26 | private val mock: Int, 27 | ) { 28 | companion object { 29 | @JvmStatic 30 | @Parameterized.Parameters 31 | fun data() = 32 | listOf( 33 | arrayOf(TestEnum.ONE, 0), 34 | arrayOf(TestEnum.TWO, 1), 35 | arrayOf(TestEnum.THREE, 2), 36 | ) 37 | } 38 | 39 | private val random: Random = mockk() 40 | 41 | @After 42 | fun teardown() { 43 | confirmVerified(random) 44 | } 45 | 46 | @Test 47 | fun testGetEnum() { 48 | every { random.nextInt(any()) } returns mock 49 | assertEquals(expected, TestEnum::class.java.getEnum(random = random)) 50 | verify { random.nextInt(eq(TestEnum::class.java.enumConstants.size)) } 51 | } 52 | } 53 | 54 | open class TestExceptions { 55 | private enum class NoValueEnum 56 | 57 | @Test(expected = ModelForgeException::class) 58 | fun testGetEnumNoValuesDefined() { 59 | NoValueEnum::class.java.getEnum() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/FileProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.test.assertEquals 10 | 11 | class FileProviderTest { 12 | private val stringProvider: StringProvider = mockk() 13 | 14 | @After 15 | fun tearDown() { 16 | confirmVerified(stringProvider) 17 | } 18 | 19 | @Test 20 | fun testGet() { 21 | val filename = "filename" 22 | every { stringProvider.get() } returns filename 23 | 24 | val file = FileProvider(stringProvider).get() 25 | assertEquals(filename, file.name) 26 | 27 | verify { stringProvider.get() } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/FloatProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.random.Random 10 | import kotlin.test.assertEquals 11 | import kotlin.test.assertTrue 12 | 13 | class FloatProviderTest { 14 | private val random: Random = mockk() 15 | 16 | @After 17 | fun teardown() { 18 | confirmVerified(random) 19 | } 20 | 21 | @Test 22 | fun testGet() { 23 | val float = FloatProvider().get() 24 | assertTrue(float >= Float.MIN_VALUE) 25 | assertTrue(float <= Float.MAX_VALUE) 26 | } 27 | 28 | @Test 29 | fun testGetWithCustomMaxMin() { 30 | val min = 5.0 31 | val max = 10.0 32 | val float = FloatProvider(min = min, max = max).get() 33 | assertTrue(float >= min) 34 | assertTrue(float <= max) 35 | } 36 | 37 | @Test 38 | fun testGetRandomCalled() { 39 | val randomDouble = 15.0 40 | every { random.nextDouble(any(), any()) } returns randomDouble 41 | 42 | val min = 5.0 43 | val max = 10.0 44 | val float = 45 | FloatProvider( 46 | min = min, 47 | max = max, 48 | random = random, 49 | ).get() 50 | 51 | assertEquals(randomDouble.toFloat(), float) 52 | 53 | verify { random.nextDouble(eq(min), eq(max)) } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/InstantProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.Millis.NINETEEN_EIGHTY_SIX 4 | import io.github.hellocuriosity.Millis.TWENTY_TWENTY_ONE 5 | import io.mockk.confirmVerified 6 | import io.mockk.every 7 | import io.mockk.mockk 8 | import io.mockk.verify 9 | import org.junit.After 10 | import org.junit.Test 11 | import kotlin.random.Random 12 | import kotlin.test.assertEquals 13 | import kotlin.test.assertTrue 14 | 15 | class InstantProviderTest { 16 | private val random: Random = mockk() 17 | 18 | @After 19 | fun teardown() { 20 | confirmVerified(random) 21 | } 22 | 23 | @Test 24 | fun testGet() { 25 | val instant = InstantProvider().get() 26 | assertTrue(instant.toEpochMilli() in NINETEEN_EIGHTY_SIX until TWENTY_TWENTY_ONE) 27 | } 28 | 29 | @Test 30 | fun testGetWithCustomTimeSpan() { 31 | val start = 1L 32 | val end = 2L 33 | val instant = InstantProvider(from = start, until = end).get() 34 | assertTrue(instant.toEpochMilli() in start until end) 35 | } 36 | 37 | @Test 38 | fun testGetRandomCalled() { 39 | every { random.nextLong(any(), any()) } returns 1315260000000L // 06.09.2011 40 | 41 | val instant = InstantProvider(random = random).get() 42 | assertEquals(1315260000000L, instant.toEpochMilli()) 43 | 44 | verify { random.nextLong(eq(NINETEEN_EIGHTY_SIX), eq(TWENTY_TWENTY_ONE)) } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/IntegerProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.random.Random 10 | import kotlin.test.assertEquals 11 | import kotlin.test.assertTrue 12 | 13 | class IntegerProviderTest { 14 | private val random: Random = mockk() 15 | 16 | @After 17 | fun teardown() { 18 | confirmVerified(random) 19 | } 20 | 21 | @Test 22 | fun testGet() { 23 | val integer = IntegerProvider().get() 24 | assertTrue(integer in Int.MIN_VALUE until Int.MAX_VALUE) 25 | } 26 | 27 | @Test 28 | fun testGetWithCustomMaxMin() { 29 | val min = 5 30 | val max = 10 31 | val integer = IntegerProvider(min = min, max = max).get() 32 | assertTrue(integer in min until max) 33 | } 34 | 35 | @Test 36 | fun testGetRandomCalled() { 37 | val randomInt = 15 38 | every { random.nextInt(any(), any()) } returns randomInt 39 | 40 | val min = 5 41 | val max = 10 42 | val integer = 43 | IntegerProvider( 44 | min = min, 45 | max = max, 46 | random = random, 47 | ).get() 48 | 49 | assertEquals(randomInt, integer) 50 | 51 | verify { random.nextInt(eq(min), eq(max)) } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/LongProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import org.junit.Test 9 | import kotlin.random.Random 10 | import kotlin.test.assertEquals 11 | import kotlin.test.assertTrue 12 | 13 | class LongProviderTest { 14 | private val random: Random = mockk() 15 | 16 | @After 17 | fun teardown() { 18 | confirmVerified(random) 19 | } 20 | 21 | @Test 22 | fun testGet() { 23 | val long = LongProvider().get() 24 | assertTrue(long in Long.MIN_VALUE until Long.MAX_VALUE) 25 | } 26 | 27 | @Test 28 | fun testGetWithCustomMaxMin() { 29 | val min = 5L 30 | val max = 10L 31 | val long = LongProvider(min = min, max = max).get() 32 | assertTrue(long in min until max) 33 | } 34 | 35 | @Test 36 | fun testGetRandomCalled() { 37 | val randomLong = 15L 38 | 39 | every { random.nextLong(any(), any()) } returns randomLong 40 | val min = 5L 41 | val max = 10L 42 | val long = 43 | LongProvider( 44 | min = min, 45 | max = max, 46 | random = random, 47 | ).get() 48 | 49 | assertEquals(randomLong, long) 50 | 51 | verify { random.nextLong(eq(min), eq(max)) } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/ShortProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import kotlin.test.Test 4 | import kotlin.test.assertTrue 5 | 6 | class ShortProviderTest { 7 | @Test 8 | fun testGet() { 9 | val short = ShortProvider().get() 10 | assertTrue(short in Short.MIN_VALUE until Short.MAX_VALUE) 11 | } 12 | 13 | @Test 14 | fun testGetWithCustomMaxMin() { 15 | val min = 5 16 | val max = 10 17 | val short = ShortProvider(min = min, max = max).get() 18 | assertTrue(short in min until max) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/StringProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.github.hellocuriosity.ModelForgeException 4 | import org.junit.Test 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | 7 | class StringProviderTest { 8 | @Test 9 | fun testGet() { 10 | for (i in 1..1000) { 11 | val words = StringProvider(wordCount = i).get() 12 | assertEquals(i, words.count()) 13 | } 14 | } 15 | 16 | @Test 17 | fun testGetZero() { 18 | val emptyString = StringProvider(wordCount = 0).get() 19 | assertEquals("", emptyString) 20 | } 21 | 22 | @Test(expected = ModelForgeException::class) 23 | fun testGetLessThanZero() { 24 | StringProvider(wordCount = -1).get() 25 | } 26 | 27 | private fun String.count(): Int = this.trim().splitToSequence(" ").count() 28 | } 29 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/UByteProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import kotlin.test.Test 9 | import kotlin.test.assertEquals 10 | import kotlin.test.assertTrue 11 | 12 | class UByteProviderTest { 13 | private val provider: UIntProvider = mockk() 14 | 15 | @After 16 | fun teardown() { 17 | confirmVerified(provider) 18 | } 19 | 20 | @Test 21 | fun testGet() { 22 | val uByte = UByteProvider().get() 23 | assertTrue(uByte in UByte.MIN_VALUE until UByte.MAX_VALUE) 24 | } 25 | 26 | @Test 27 | fun testGetWithCustomMaxMin() { 28 | val min = 5u 29 | val max = 10u 30 | val uByte = UByteProvider(min = min, max = max).get() 31 | assertTrue(uByte in min until max) 32 | } 33 | 34 | @Test 35 | fun testGetProviderCalled() { 36 | every { provider.get() } returns 15u 37 | 38 | val uByte = UByteProvider(provider = provider).get() 39 | assertEquals(15u.toUByte(), uByte) 40 | 41 | verify { provider.get() } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/UIntProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertTrue 5 | 6 | class UIntProviderTest { 7 | @Test 8 | fun testGet() { 9 | val uInteger = UIntProvider().get() 10 | assertTrue(uInteger in UInt.MIN_VALUE until UInt.MAX_VALUE) 11 | } 12 | 13 | @Test 14 | fun testGetWithCustomMaxMin() { 15 | val min = 5u 16 | val max = 10u 17 | val uInteger = UIntProvider(min = min, max = max).get() 18 | assertTrue(uInteger in min until max) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/ULongProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertTrue 5 | 6 | class ULongProviderTest { 7 | @Test 8 | fun testGet() { 9 | val uLong = ULongProvider().get() 10 | assertTrue(uLong in ULong.MIN_VALUE until ULong.MAX_VALUE) 11 | } 12 | 13 | @Test 14 | fun testGetWithCustomMaxMin() { 15 | val min: ULong = 5u 16 | val max: ULong = 10u 17 | val uLong = ULongProvider(min = min, max = max).get() 18 | assertTrue(uLong in min until max) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/UShortProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import io.mockk.confirmVerified 4 | import io.mockk.every 5 | import io.mockk.mockk 6 | import io.mockk.verify 7 | import org.junit.After 8 | import kotlin.test.Test 9 | import kotlin.test.assertEquals 10 | import kotlin.test.assertTrue 11 | 12 | class UShortProviderTest { 13 | private val provider: UIntProvider = mockk() 14 | 15 | @After 16 | fun teardown() { 17 | confirmVerified(provider) 18 | } 19 | 20 | @Test 21 | fun testGet() { 22 | val uShort = UShortProvider().get() 23 | assertTrue(uShort in UShort.MIN_VALUE until UShort.MAX_VALUE) 24 | } 25 | 26 | @Test 27 | fun testGetWithCustomMaxMin() { 28 | val min = 5u 29 | val max = 10u 30 | val uShort = UShortProvider(min = min, max = max).get() 31 | assertTrue(uShort in min until max) 32 | } 33 | 34 | @Test 35 | fun testGetProviderCalled() { 36 | every { provider.get() } returns 15u 37 | 38 | val uShort = UShortProvider(provider = provider).get() 39 | assertEquals(15u.toUShort(), uShort) 40 | 41 | verify { provider.get() } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /forge-core/src/test/kotlin/io/github/hellocuriosity/providers/UuidProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity.providers 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class UuidProviderTest { 7 | @Test 8 | fun testGet() { 9 | val uuid = UuidProvider().get().toString() 10 | val groups = uuid.split("-") 11 | 12 | /** 13 | * UUIDs are displayed in five groups separated by hyphens, 14 | * in the form 8-4-4-4-12 for a total of 36 characters 15 | */ 16 | assertEquals(36, uuid.length) 17 | assertEquals(5, groups.size) 18 | assertEquals(8, groups[0].length) 19 | assertEquals(4, groups[1].length) 20 | assertEquals(4, groups[2].length) 21 | assertEquals(4, groups[3].length) 22 | assertEquals(12, groups[4].length) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /forge-core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline 2 | 3 | -------------------------------------------------------------------------------- /forge-test-utils/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | implementation(kotlin("stdlib")) 11 | implementation(kotlin("test")) 12 | } 13 | -------------------------------------------------------------------------------- /forge-test-utils/src/main/kotlin/io/github/hellocuriosity/Assert.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import java.io.File 4 | import java.time.Instant 5 | import java.util.Calendar 6 | import java.util.Date 7 | import java.util.UUID 8 | import kotlin.test.assertTrue 9 | 10 | fun assertBoolean(value: T) = 11 | assertType(Boolean::class.java, value::class.java) 12 | 13 | fun assertByte(value: T) = 14 | assertType(Byte::class.java, value::class.java) 15 | 16 | fun assertCalendar(value: T) = 17 | assertType(Calendar::class.java, value::class.java) 18 | 19 | fun assertChar(value: T) = 20 | assertType(Char::class.java, value::class.java) 21 | 22 | fun assertDate(value: T) = 23 | assertType(Date::class.java, value::class.java) 24 | 25 | fun assertDouble(value: T) = 26 | assertType(Double::class.java, value::class.java) 27 | 28 | fun assertFile(value: T) = 29 | assertType(File::class.java, value::class.java) 30 | 31 | fun assertFloat(value: T) = 32 | assertType(Float::class.java, value::class.java) 33 | 34 | fun assertInt(value: T) = 35 | assertType(Int::class.java, value::class.java) 36 | 37 | fun assertInstant(value: T) = 38 | assertType(Instant::class.java, value::class.java) 39 | 40 | fun assertLong(value: T) = 41 | assertType(Long::class.java, value::class.java) 42 | 43 | fun assertShort(value: T) = 44 | assertType(Short::class.java, value::class.java) 45 | 46 | fun assertString(value: T) = 47 | assertType(String::class.java, value::class.java) 48 | 49 | fun assertUByte(value: T) = 50 | assertType(UByte::class.java, value::class.java) 51 | 52 | fun assertUInt(value: T) = 53 | assertType(UInt::class.java, value::class.java) 54 | 55 | fun assertULong(value: T) = 56 | assertType(ULong::class.java, value::class.java) 57 | 58 | fun assertUShort(value: T) = 59 | assertType(UShort::class.java, value::class.java) 60 | 61 | fun assertUUID(value: T) = 62 | assertType(UUID::class.java, value::class.java) 63 | 64 | private fun > assertType(expectedType: Class<*>, value: T) = 65 | assertTrue(message = "Expected ${expectedType.simpleName} but was ${value::class.simpleName}") { 66 | value::class.java == expectedType::class.java 67 | } 68 | -------------------------------------------------------------------------------- /forge-test-utils/src/main/kotlin/io/github/hellocuriosity/TestObject.kt: -------------------------------------------------------------------------------- 1 | package io.github.hellocuriosity 2 | 3 | import java.io.File 4 | import java.time.Instant 5 | import java.util.Calendar 6 | import java.util.Date 7 | import java.util.UUID 8 | import kotlin.test.assertEquals 9 | import kotlin.test.assertNotNull 10 | import kotlin.test.assertTrue 11 | 12 | data class TestObject( 13 | val booleanValue: Boolean, 14 | val booleanOptional: Boolean?, 15 | val byteValue: Byte, 16 | val byteOptional: Byte?, 17 | val calendarValue: Calendar, 18 | val calendarOptional: Calendar?, 19 | val charValue: Char, 20 | val charOptional: Char?, 21 | val complexObject: ComplexObject, 22 | val complexOptional: ComplexObject?, 23 | val dateValue: Date, 24 | val dateOptional: Date?, 25 | val doubleValue: Double, 26 | val doubleOptional: Double?, 27 | val enumValue: TestEnum, 28 | val enumOptional: TestEnum?, 29 | val enumWithValue: TestEnumWithValue, 30 | val enumWithValueOptional: TestEnumWithValue?, 31 | val file: File, 32 | val fileOptional: File?, 33 | val floatValue: Float, 34 | val floatOptional: Float?, 35 | val intValue: Int, 36 | val intValueOptional: Int?, 37 | val instantValue: Instant, 38 | val instantOptional: Instant?, 39 | val listValues: List, 40 | val listOptional: List?, 41 | val longValue: Long, 42 | val longOptional: Long?, 43 | val mapValues: Map, 44 | val mapOptional: Map?, 45 | val setValues: Set, 46 | val setOptional: Set?, 47 | val shortValue: Short, 48 | val shortOptional: Short?, 49 | val stringValue: String, 50 | val stringOptional: String?, 51 | val uByteValue: UByte, 52 | val uByteOptional: UByte?, 53 | val uIntValue: UInt, 54 | val uIntOptional: UInt?, 55 | val uLongValue: ULong, 56 | val uLongOptional: ULong?, 57 | val uShortValue: UShort, 58 | val uShortOptional: UShort?, 59 | val uuidValue: UUID, 60 | val uuidOptional: UUID?, 61 | ) 62 | 63 | enum class TestEnum { 64 | ONE, TWO, THREE 65 | } 66 | 67 | enum class TestEnumWithValue(val value: String) { 68 | FOUR("four"), FIVE("five"), SIX("six"); 69 | 70 | companion object { 71 | /** 72 | * Unused because we aren't validating this, but still necessary 73 | * to validate that these objects aren't being generated 74 | */ 75 | 76 | const val COMPANION_VALUE = 1234 77 | fun list() = listOf(FOUR, FIVE, SIX) 78 | } 79 | } 80 | 81 | data class ComplexObject( 82 | val booleanValue: Boolean, 83 | val calendarValue: Calendar, 84 | val dateValue: Date, 85 | val doubleValue: Double, 86 | val enumValue: TestEnum, 87 | val floatValue: Float, 88 | val intValue: Int, 89 | val instantValue: Instant, 90 | val longValue: Long, 91 | val stringValue: String, 92 | ) 93 | 94 | fun TestObject.assert() { 95 | assertNotNull(booleanValue) 96 | assertBoolean(booleanValue) 97 | assertNotNull(booleanOptional) 98 | assertBoolean(booleanOptional) 99 | assertNotNull(byteValue) 100 | assertByte(byteValue) 101 | assertNotNull(byteOptional) 102 | assertByte(byteOptional) 103 | assertNotNull(calendarValue) 104 | assertCalendar(calendarValue) 105 | assertNotNull(calendarOptional) 106 | assertCalendar(calendarOptional) 107 | assertNotNull(charValue) 108 | assertChar(charValue) 109 | assertNotNull(charOptional) 110 | assertChar(charOptional) 111 | assertNotNull(complexObject) 112 | assertNotNull(complexOptional) 113 | assertNotNull(dateValue) 114 | assertDate(dateValue) 115 | assertNotNull(dateOptional) 116 | assertDate(dateOptional) 117 | assertNotNull(doubleValue) 118 | assertDouble(doubleValue) 119 | assertNotNull(doubleOptional) 120 | assertDouble(doubleOptional) 121 | assertNotNull(enumValue) 122 | assertNotNull(enumOptional) 123 | assertNotNull(enumWithValue) 124 | assertNotNull(enumWithValueOptional) 125 | assertNotNull(file) 126 | assertFile(file) 127 | assertNotNull(fileOptional) 128 | assertFile(fileOptional) 129 | assertNotNull(floatValue) 130 | assertFloat(floatValue) 131 | assertNotNull(floatOptional) 132 | assertFloat(floatOptional) 133 | assertNotNull(intValue) 134 | assertInt(intValue) 135 | assertNotNull(intValueOptional) 136 | assertInt(intValueOptional) 137 | assertNotNull(instantValue) 138 | assertInstant(instantValue) 139 | assertNotNull(instantOptional) 140 | assertInstant(instantOptional) 141 | assertNotNull(longValue) 142 | assertLong(longValue) 143 | assertNotNull(longOptional) 144 | assertLong(longOptional) 145 | assertNotNull(listValues) 146 | assertEquals(10, listValues.size) 147 | assertNotNull(listOptional) 148 | assertEquals(10, listOptional.size) 149 | assertNotNull(mapValues) 150 | assertTrue(mapValues.size in 0 until 11) 151 | assertNotNull(mapOptional) 152 | assertTrue(mapOptional.size in 0 until 11) 153 | assertNotNull(setValues) 154 | assertEquals(10, setValues.size) 155 | assertNotNull(setOptional) 156 | assertEquals(10, setOptional.size) 157 | assertNotNull(shortValue) 158 | assertShort(shortValue) 159 | assertNotNull(shortOptional) 160 | assertShort(shortOptional) 161 | assertNotNull(stringValue) 162 | assertString(stringValue) 163 | assertTrue(stringValue.isNotBlank()) 164 | assertNotNull(stringOptional) 165 | assertTrue(stringOptional.isNotBlank()) 166 | assertString(stringOptional) 167 | assertUByte(uByteValue) 168 | assertNotNull(uByteOptional) 169 | assertUByte(uByteOptional) 170 | assertUInt(uIntValue) 171 | assertNotNull(uIntOptional) 172 | assertUInt(uIntOptional) 173 | assertULong(uLongValue) 174 | assertNotNull(uLongOptional) 175 | assertULong(uLongOptional) 176 | assertUShort(uShortValue) 177 | assertNotNull(uShortOptional) 178 | assertUShort(uShortOptional) 179 | assertNotNull(uuidValue) 180 | assertUUID(uuidValue) 181 | assertNotNull(uuidOptional) 182 | assertUUID(uuidOptional) 183 | 184 | // Validate populated values for ComplexObject 185 | assertNotNull(complexObject.booleanValue) 186 | assertBoolean(complexObject.booleanValue) 187 | assertNotNull(complexObject.calendarValue) 188 | assertCalendar(complexObject.calendarValue) 189 | assertNotNull(complexObject.dateValue) 190 | assertDate(complexObject.dateValue) 191 | assertNotNull(complexObject.doubleValue) 192 | assertDouble(complexObject.doubleValue) 193 | assertNotNull(complexObject.enumValue) 194 | assertNotNull(complexObject.floatValue) 195 | assertFloat(complexObject.floatValue) 196 | assertNotNull(complexObject.intValue) 197 | assertInt(complexObject.intValue) 198 | assertNotNull(complexObject.instantValue) 199 | assertInstant(complexObject.instantValue) 200 | assertNotNull(complexObject.longValue) 201 | assertLong(complexObject.longValue) 202 | assertNotNull(complexObject.stringValue) 203 | assertString(complexObject.stringValue) 204 | assertTrue(complexObject.stringValue.isNotBlank()) 205 | } 206 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.daemon=true 3 | -------------------------------------------------------------------------------- /gradle/dependencies.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | dokka = "2.0.0" 3 | javax = "1" 4 | objenesis = "3.4" 5 | lorem = "2.2" 6 | jupiter = "5.12.2" 7 | mockk = "1.14.2" 8 | kotlin = "2.0.0" 9 | kotlinter = "5.0.2" 10 | detekt = "1.23.8" 11 | kover = "0.9.1" 12 | vanniktech = "0.31.0" 13 | 14 | [libraries] 15 | objenesis = { module = "org.objenesis:objenesis", version.ref = "objenesis" } 16 | lorem = { module = "com.thedeanda:lorem", version.ref = "lorem" } 17 | 18 | inject = { module = "javax.inject:javax.inject", version.ref = "javax" } 19 | 20 | junitJupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "jupiter" } 21 | mockK = { module = "io.mockk:mockk", version.ref = "mockk" } 22 | 23 | [plugins] 24 | detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } 25 | dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } 26 | kotlinter = { id = "org.jmailen.kotlinter", version.ref = "kotlinter" } 27 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } 28 | vanniktech = { id = "com.vanniktech.maven.publish", version.ref = "vanniktech" } 29 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloCuriosity/model-forge/76c04cf8134a3a7dbf7718302dba768087340fa1/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.14-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /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 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 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="\\\"\\\"" 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 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /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 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "labels": [ 7 | "dependencies" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /scripts/create-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o pipefail 4 | 5 | RELEASE_TYPE=${1} 6 | 7 | NEW_RELEASE=$(./scripts/get_next_version.sh "$RELEASE_TYPE") 8 | 9 | echo "Bumping version to $NEW_RELEASE" 10 | 11 | gh release create "$NEW_RELEASE" --target "main" --generate-notes 12 | -------------------------------------------------------------------------------- /scripts/get_next_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o pipefail 4 | 5 | RELEASE_TYPE=${1} 6 | 7 | # Get current release version 8 | git fetch --force --tags 9 | CURRENT_RELEASE=$(git tag --sort=committerdate | tail -1) 10 | 11 | # Separate major, minor and patch numbers 12 | IFS='.' read -r MAJOR MINOR PATCH <<<"$CURRENT_RELEASE" 13 | 14 | # $/${} is unnecessary on arithmetic variables. 15 | # shellcheck disable=SC2004 16 | if [[ "${RELEASE_TYPE}" == "major" ]]; then 17 | NEW_RELEASE="$(($MAJOR + 1)).0.0" 18 | elif [ "${RELEASE_TYPE}" == "minor" ]; then 19 | NEW_RELEASE="$MAJOR.$(($MINOR + 1)).0" 20 | else 21 | NEW_RELEASE="$MAJOR.$MINOR.$(($PATCH + 1))" 22 | fi 23 | 24 | echo "$NEW_RELEASE" 25 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o pipefail 4 | 5 | BUILD_NR=${1} 6 | 7 | REPO_DIR="$(cd "$(dirname "$0")/../" && pwd)" 8 | 9 | # Create Version 10 | git fetch --force --tags 11 | TAG=$(git tag --sort=committerdate | tail -1) 12 | 13 | if [[ "${IS_RELEASE}" == "true" ]]; then 14 | export VERSION="$TAG" 15 | echo "Publishing Release: $VERSION" 16 | else 17 | export VERSION="${TAG}.${BUILD_NR}-SNAPSHOT" 18 | echo "Publishing Snapshot: $VERSION" 19 | fi 20 | 21 | # Publish 22 | "${REPO_DIR}"/gradlew publishToMavenCentral --no-configuration-cache 23 | -------------------------------------------------------------------------------- /scripts/verify-doc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o pipefail 4 | 5 | RELEASE_TYPE=${1} 6 | FILE=${2} 7 | 8 | VERSION=$(./scripts/get_next_version.sh "$RELEASE_TYPE") 9 | 10 | echo "Verifying version $VERSION is defined in: $FILE." 11 | if grep -R "$VERSION" "$FILE"; then 12 | echo "Version info found." 13 | exit 0 14 | else 15 | echo "Version info not found in: $FILE" 16 | exit 1 17 | fi 18 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "model-forge" 2 | include( 3 | "forge-core", 4 | "forge-test-utils" 5 | ) 6 | 7 | dependencyResolutionManagement { 8 | versionCatalogs { 9 | create("libs") { 10 | from(files("gradle/dependencies.versions.toml")) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ make install-docs 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ make start-docs 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without 18 | having to restart the server. 19 | 20 | ### Build 21 | 22 | ``` 23 | $ make build-docs 24 | ``` 25 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /website/docs/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Changelog 6 | 7 | ## [1.5.1] - December 5th 2024 8 | 9 | * Support Kotlin version 2.0.0 10 | * Re-enable support for java 17 11 | 12 | ## [1.5.0] - October 31st 2024 13 | 14 | * Auto generation for additional types: 15 | * UByte 16 | * UInt 17 | * ULong 18 | * UShort 19 | * Dependency updates 20 | 21 | ## [1.4.0] - February 19th 2024 22 | 23 | * Support Kotlin version 1.9.22 24 | * Dependency updates 25 | 26 | ## [1.3.0] - February 2nd 2023 27 | 28 | * Support Kotlin version 1.8.0 29 | * Dependency updates 30 | 31 | ## [1.2.0] - November 12th 2022 32 | 33 | * Add missing tests for sets 34 | * Added asserts for types in tests 35 | * Docusaurus website 36 | * Workflow dependency updates 37 | 38 | ## [1.1.0] - October 5th 2022 39 | 40 | * Dependency updates 41 | * Housekeeping and general clean up 42 | 43 | ## [1.0.0] - July 25th 2022 44 | 45 | * Auto publishing via CI 46 | * Auto labeling via CI 47 | * Removed deprecated reified functions 48 | * Added missing test for custom char provider 49 | * Added code of conduct 50 | * Minor housekeeping tasks 51 | * Dependency updates 52 | 53 | ## [0.9.0] - March 22nd 2022 54 | 55 | * Support for map collection type. 56 | * Better output message when using `attempt`. 57 | * Removed the custom Provider interface in favor of `javax.inject.Provider`. 58 | * See [Migration Guide](migration.md) for details. 59 | * Moved dependency definitions to `buildSrc` 60 | * Fixed StringProvider to be more explicit when generating strings. 61 | 62 | ## [0.8.0] - December 7th 2021 63 | 64 | * Auto generation for additional types: 65 | * Byte 66 | * Char 67 | * Short 68 | * UUID 69 | * Support for Set collection type 70 | * Dependency updates 71 | 72 | ## [0.7.4] - September 16th 2021 73 | 74 | * Build with Java 8 75 | 76 | ## [0.7.3] - September 15th 2021 77 | 78 | * Add GitHub Action dependencies to dependabot 79 | * Dependency updates 80 | 81 | ## [0.7.2] - July 29th 2021 82 | 83 | * Allow for forging Enums directly through ModelForge.build 84 | * Add more Kotlin `Provider` fanciness 85 | 86 | ## [0.7.1] - June 26th 2021 87 | 88 | * Don't instantiate reflective objects when handling custom providers 89 | 90 | ## [0.7.0] - June 7th 2021 91 | 92 | * Added reified inline extension functions 93 | 94 | ## [0.6.0] - June 3rd 2021 95 | 96 | * Exclude Companion Object Values 97 | * Support for File type. 98 | 99 | ## [0.5.1] - June 1st 2021 100 | 101 | * Support for optional variables. 102 | 103 | ## [0.5.0] - May 30th 2021 104 | 105 | * Support for List collection type. 106 | * Support for complex data types. 107 | * Support for custom providers. 108 | 109 | ## [0.4.0] - May 26th 2021 110 | 111 | Auto generation for enums. 112 | 113 | ## [0.3.0] - May 13th 2021 114 | 115 | Auto generation for additional types: 116 | 117 | * Calendar 118 | * Instant 119 | 120 | ## [0.2.0] - May 6th 2021 121 | 122 | * Allow using Kotlin class definitions directly. 123 | * Added forgery and forgeries property delegate. 124 | 125 | ## [0.1.1] - April 30th 2021 126 | 127 | * Java 8 compatibility 128 | 129 | ## [0.1.0] - April 28th 2021 130 | 131 | The initial release supports auto generated models for the following types: 132 | 133 | * Boolean 134 | * Date 135 | * Double 136 | * Float 137 | * Int 138 | * Long 139 | * String 140 | -------------------------------------------------------------------------------- /website/docs/getting-started/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Getting Started", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /website/docs/getting-started/custom-providers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Custom Providers 6 | 7 | While Model Forge aims to fully automate model generation, you may run into an 8 | instance where you need to customize your data. This is easily achievable by 9 | defining a custom provider and adding it to the forge. 10 | 11 | #### Define your provider 12 | 13 | ```kotlin 14 | val employeeProvider: Provider = Provider { 15 | Employee( 16 | id = 15L, 17 | name = "Josh", 18 | dob = Instant.ofEpochMilli(1315260000000) 19 | ) 20 | } 21 | ``` 22 | 23 | #### Add your provider to the forge 24 | 25 | ```kotlin 26 | forge.addProvider(employeeProvider) 27 | ``` 28 | 29 | #### Inline your provider(s) 30 | 31 | Alternatively you can add your forgery providers inline 32 | 33 | ```kotlin 34 | 35 | val forge = ModelForge().apply { 36 | addProvider { 37 | Employee( 38 | id = 2L, 39 | name = "Hendrik", 40 | dob = Instant.ofEpochMilli(1574486400000) 41 | ) 42 | } 43 | } 44 | val employee by forgery(forge) 45 | 46 | ``` 47 | -------------------------------------------------------------------------------- /website/docs/getting-started/generate-models.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | import Tabs from '@theme/Tabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | # Generating Models 9 | 10 | #### Define your model 11 | 12 | ```kotlin 13 | data class Employee( 14 | val id: Long, 15 | val name: String, 16 | val dob: Instant, 17 | ) 18 | ``` 19 | 20 | #### Generate 21 | 22 | 23 | 24 | ```kotlin 25 | val forge = ModelForge() 26 | val employee = forge.build() 27 | ``` 28 | 29 | 30 | 31 | ```kotlin 32 | val forge = ModelForge() 33 | val employees = forge.buildList(size = 3) 34 | ``` 35 | 36 | 37 | 38 | 39 | ```kotlin 40 | val forge = ModelForge() 41 | val employees = forge.buildSet(size = 3) 42 | ``` 43 | 44 | 45 | 46 | 47 | #### or by Delegation 48 | 49 | 50 | 51 | ```kotlin 52 | val employee: Employee by forgery() 53 | ``` 54 | 55 | 56 | 57 | 58 | ```kotlin 59 | val employees: List by forgeryList(size = 3) 60 | ``` 61 | 62 | 63 | 64 | 65 | 66 | ```kotlin 67 | val employees: List by forgerySet(size = 3) 68 | ``` 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /website/docs/getting-started/gradle-dependency.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Gradle Setup 6 | 7 | Add the dependency to your `build.gradle` file to get started: 8 | 9 | ```kotlin 10 | dependencies { 11 | testImplementation("io.github.hellocuriosity:model-forge:1.5.1") 12 | } 13 | ``` 14 | 15 | #### Feeling Adventurous 💥 16 | 17 | If you're feeling adventurous you can be on the cutting edge and test a snapshot: 18 | 19 | ```kotlin 20 | repositories { 21 | maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots/") 22 | } 23 | 24 | dependencies { 25 | testImplementation("io.github.hellocuriosity:model-forge:1.5.1.xx-SNAPSHOT") 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /website/docs/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Introduction 6 | 7 | Model Forge is a library that automates model generation for use in tests and currently 8 | supports the auto generation for the following types: 9 | 10 | #### Types 11 | 12 | * Boolean 13 | * Byte 14 | * Calendar 15 | * Char 16 | * Date 17 | * Double 18 | * Enums 19 | * File 20 | * Float 21 | * Int 22 | * Instant 23 | * Long 24 | * Short 25 | * String 26 | * UByte 27 | * UInt 28 | * ULong 29 | * UShort 30 | * UUID 31 | 32 | #### Collections 33 | 34 | * List 35 | * Map 36 | * Set 37 | -------------------------------------------------------------------------------- /website/docs/migration.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Migration Guide 6 | 7 | ## 1.0.0 - July 25th 2022 8 | 9 | In version 1.0.0 we removed the deprecated `forgeries` and `build` (list) reified functions. The same functionality 10 | is covered in the `forgeryList()` and `buildList()` functions. 11 | 12 | Replace instances of: 13 | ```kotlin 14 | forgeries() 15 | ``` 16 | 17 | with: 18 | ```kotlin 19 | forgeryList() 20 | ``` 21 | 22 | Replace all instances of: 23 | ```kotlin 24 | build() // list 25 | ``` 26 | 27 | with: 28 | ```kotlin 29 | buildList() 30 | ``` 31 | 32 | ## 0.9.0 - March 22nd 2022 33 | 34 | In version 0.9.0 we removed the custom provider in favor of javax provider for easier 35 | adaptation in other projects. 36 | 37 | Replace all instances of: 38 | ```kotlin 39 | import io.github.hellocuriosity.providers.Provider 40 | ``` 41 | 42 | with: 43 | ```kotlin 44 | import javax.inject.Provider 45 | ``` 46 | -------------------------------------------------------------------------------- /website/docs/supported-types/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Supported Types", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /website/docs/supported-types/boolean.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Boolean 6 | 7 | The `boolean` provider generates a random boolean value. 8 | 9 | ### Default Behavior 10 | `true` or `false` 11 | -------------------------------------------------------------------------------- /website/docs/supported-types/byte.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Byte 6 | 7 | The `byte` provider generates a random byte value between two integers. 8 | 9 | ### Default Behavior 10 | Between `-128` and `127` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `min` and `max` values to the provider: 15 | 16 | ```kotlin 17 | val byte = ByteProvider(min = 1, max = 15).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/calendar.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Calendar 6 | 7 | The `calendar` provider generates a random calendar instance between two `timestamps`. 8 | 9 | ### Default Behavior 10 | From `529714800000L` until `1617141600000L` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `timestamps` to the provider: 15 | 16 | ```kotlin 17 | val calendar = CalendarProvider(from = 1315260000000L, until = 1574486400000L).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/char.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Char 6 | 7 | The `char` provider generates a random character from a list of chars. 8 | 9 | ### Default Behavior 10 | 11 | From `('a'..'z') + ('A'..'Z') + ('0'..'9')` 12 | 13 | ### Extending the Provider 14 | 15 | You can specify a list of chars to pass to the provider: 16 | 17 | ```kotlin 18 | val char = CharProvider(chars = listOf('a', 'b')).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/date.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Date 6 | 7 | The `date` provider generates a random calendar instance between two `timestamps`. 8 | 9 | ### Default Behavior 10 | From `529714800000L` until `1617141600000L` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `timestamps` to the provider: 15 | 16 | ```kotlin 17 | val date = DateProvider(from = 1315260000000L, until = 1574486400000L).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/double.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Double 6 | 7 | The `double` provider generates a random double value between two doubles. 8 | 9 | ### Default Behavior 10 | 11 | Between `4.9E-324` and `1.7976931348623157E308` 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific range you can pass custom `min` and `max` values to the provider: 16 | 17 | ```kotlin 18 | val double = DoubleProvider(min = 1.0, max = 15.0).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/enums.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Enums 6 | 7 | The forge auto generates an `enum` value from the enum class. 8 | -------------------------------------------------------------------------------- /website/docs/supported-types/file.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | --- 4 | 5 | # File 6 | 7 | The `file` provider generates a file with a random name from the `StringProvider`. 8 | 9 | ### Default Behavior 10 | 11 | See [string](string.md). 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific file name, you can pass a custom `string` provider: 16 | 17 | ```kotlin 18 | val provider: Provider = Provider { "filename" } 19 | val file = FileProvider(stringProvider = provider).get() 20 | ``` 21 | -------------------------------------------------------------------------------- /website/docs/supported-types/float.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 9 3 | --- 4 | 5 | # Float 6 | 7 | The `float` provider generates a random float value between two doubles. 8 | 9 | ### Default Behavior 10 | 11 | Between `1.4E-45F` and `3.4028235E38F` 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific range you can pass custom `min` and `max` values to the provider: 16 | 17 | ```kotlin 18 | val float = FloatProvider(min = 1.0, max = 15.0).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/instant.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 11 3 | --- 4 | 5 | # Instant 6 | 7 | The `instant` provider generates a random instant between two `timestamps`. 8 | 9 | ### Default Behavior 10 | From `529714800000L` until `1617141600000L` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `timestamps` to the provider: 15 | 16 | ```kotlin 17 | val instant = InstantProvider(from = 1315260000000L, until = 1574486400000L).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/int.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 10 3 | --- 4 | 5 | # Int 6 | 7 | The `int` provider generates a random int value between two integers. 8 | 9 | ### Default Behavior 10 | Between `-2147483648` and `2147483647` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `min` and `max` values to the provider: 15 | 16 | ```kotlin 17 | val integer = IntegerProvider(min = 1, max = 15).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/long.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 12 3 | --- 4 | 5 | # Long 6 | 7 | The `long` provider generates a random long value between two longs. 8 | 9 | ### Default Behavior 10 | Between `-9223372036854775807L - 1L` and `9223372036854775807L` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `min` and `max` values to the provider: 15 | 16 | ```kotlin 17 | val long = LongProvider(min = 1L, max = 15L).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/short.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 13 3 | --- 4 | 5 | # Short 6 | 7 | The `short` provider generates a random short value between two integers. 8 | 9 | ### Default Behavior 10 | Between `-32768` and `32767` 11 | 12 | ### Extending the Provider 13 | 14 | If you need a specific range you can pass custom `min` and `max` values to the provider: 15 | 16 | ```kotlin 17 | val short = ShortProvider(min = 1, max = 15).get() 18 | ``` 19 | -------------------------------------------------------------------------------- /website/docs/supported-types/string.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 14 3 | --- 4 | 5 | # String 6 | 7 | The `string` provider generate a random string. 8 | 9 | ### Default Behavior 10 | 11 | `1` word 12 | 13 | ### Extending the Provider 14 | 15 | You can specify the number of words by passing the count to the provider: 16 | 17 | ```kotlin 18 | val words = StringProvider(wordCount = 15).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/ubyte.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 15 3 | --- 4 | 5 | # UByte 6 | 7 | The `UByte` provider generates a random UByte value between two unsigned integers. 8 | 9 | ### Default Behavior 10 | 11 | Between `0` and `255` 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific range you can pass custom `min` and `max` values to the provider: 16 | 17 | ```kotlin 18 | val uByte = UByteProvider(min = 1u, max = 15u).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/uint.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 16 3 | --- 4 | 5 | # UInt 6 | 7 | The `UInt` provider generates a random UInt value between two unsigned integers. 8 | 9 | ### Default Behavior 10 | 11 | Between 0 and 4,294,967,295 (232 - 1) 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific range you can pass custom `min` and `max` values to the provider: 16 | 17 | ```kotlin 18 | val uInteger = UIntProvider(min = 1u, max = 15u).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/ulong.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 17 3 | --- 4 | 5 | # ULong 6 | 7 | The `ULong` provider generates a random ULong value between two unsigned longs. 8 | 9 | ### Default Behavior 10 | 11 | Between 0 and 18,446,744,073,709,551,615 (264 - 1) 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific range you can pass custom `min` and `max` values to the provider: 16 | 17 | ```kotlin 18 | val uLong = ULongProvider(min = 1u, max = 15u).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/ushort.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 18 3 | --- 4 | 5 | # UShort 6 | 7 | The `UShort` provider generates a random UShort value between two unsigned integers. 8 | 9 | ### Default Behavior 10 | 11 | Between `0` and `65,535` 12 | 13 | ### Extending the Provider 14 | 15 | If you need a specific range you can pass custom `min` and `max` values to the provider: 16 | 17 | ```kotlin 18 | val uShort = UShortProvider(min = 1u, max = 15u).get() 19 | ``` 20 | -------------------------------------------------------------------------------- /website/docs/supported-types/uuid.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 19 3 | --- 4 | 5 | # UUID 6 | 7 | The `UUID` provider generates random UUIDs. 8 | -------------------------------------------------------------------------------- /website/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Note: type annotations allow type checking and IDEs autocompletion 3 | 4 | const {themes} = require('prism-react-renderer'); 5 | const lightTheme = themes.github; 6 | const darkTheme = themes.dracula; 7 | 8 | /** @type {import('@docusaurus/types').Config} */ 9 | const config = { 10 | title: 'Model Forge', 11 | tagline: 'Kotlin model generation for tests', 12 | url: 'https://hellocuriosity.github.io/', 13 | baseUrl: '/model-forge/', 14 | onBrokenLinks: 'throw', 15 | onBrokenMarkdownLinks: 'warn', 16 | favicon: 'img/favicon.ico', 17 | 18 | // GitHub pages deployment config. 19 | // If you aren't using GitHub pages, you don't need these. 20 | organizationName: 'HelloCuriosity', // Usually your GitHub org/user name. 21 | projectName: 'model-forge', // Usually your repo name. 22 | 23 | // Even if you don't use internalization, you can use this field to set useful 24 | // metadata like html lang. For example, if your site is Chinese, you may want 25 | // to replace "en" with "zh-Hans". 26 | i18n: { 27 | defaultLocale: 'en', 28 | locales: ['en'], 29 | }, 30 | 31 | presets: [ 32 | [ 33 | 'classic', 34 | /** @type {import('@docusaurus/preset-classic').Options} */ 35 | ({ 36 | docs: { 37 | sidebarPath: require.resolve('./sidebars.js'), 38 | // Please change this to your repo. 39 | // Remove this to remove the "edit this page" links. 40 | editUrl: 41 | 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/', 42 | }, 43 | theme: { 44 | customCss: require.resolve('./src/css/custom.css'), 45 | }, 46 | }), 47 | ], 48 | ], 49 | 50 | themeConfig: 51 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 52 | ({ 53 | navbar: { 54 | title: 'Model Forge', 55 | logo: { 56 | alt: 'Model Forge Logo', 57 | src: 'img/logo.svg', 58 | }, 59 | items: [ 60 | { 61 | type: 'doc', 62 | docId: 'introduction', 63 | position: 'left', 64 | label: 'Docs', 65 | }, 66 | { 67 | href: 'https://github.com/HelloCuriosity/model-forge', 68 | label: 'GitHub', 69 | position: 'right', 70 | }, 71 | ], 72 | }, 73 | footer: { 74 | style: 'dark', 75 | links: [ 76 | { 77 | title: 'Docs', 78 | items: [ 79 | { 80 | label: 'Getting Started', 81 | to: '/docs/getting-started/gradle-dependency', 82 | }, 83 | { 84 | label: 'Custom Providers', 85 | to: '/docs/getting-started/custom-providers', 86 | }, 87 | { 88 | label: 'Supported Types', 89 | to: 'docs/category/supported-types', 90 | }, 91 | ], 92 | }, 93 | { 94 | title: 'Community', 95 | items: [ 96 | { 97 | label: 'Stack Overflow', 98 | href: 'https://stackoverflow.com/questions/tagged/model-forge', 99 | }, 100 | ], 101 | }, 102 | { 103 | title: 'More', 104 | items: [ 105 | { 106 | label: 'GitHub', 107 | href: 'https://github.com/HelloCuriosity/model-forge', 108 | }, 109 | ], 110 | }, 111 | ], 112 | copyright: `Copyright © ${new Date().getFullYear()} Model Forge Team - Built with Docusaurus.`, 113 | }, 114 | prism: { 115 | theme: lightTheme, 116 | darkTheme: darkTheme, 117 | }, 118 | colorMode: { 119 | defaultMode: 'light', 120 | disableSwitch: true, // disabled dark mode 121 | respectPrefersColorScheme: false, 122 | }, 123 | }), 124 | }; 125 | 126 | module.exports = config; 127 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^3.0.0", 18 | "@docusaurus/preset-classic": "^3.0.0", 19 | "@mdx-js/react": "^3.0.0", 20 | "clsx": "^2.0.0", 21 | "prism-react-renderer": "^2.0.0", 22 | "react": "^19.0.0", 23 | "react-dom": "^19.0.0" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "^3.0.0", 27 | "@docusaurus/types": "3.7.0" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.5%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "engines": { 42 | "node": ">=18.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /website/src/components/HomepageFeatures/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import styles from './styles.module.css'; 4 | 5 | const FeatureList = [ 6 | { 7 | title: 'Auto Generate Models', 8 | Svg: require('@site/static/img/build.svg').default, 9 | description: ( 10 | <> 11 | Model forge auto generates models for use in unit or integration tests. Removing the need to create 12 | static models and removes redundancy in your tests. 13 | 14 | ), 15 | }, 16 | { 17 | title: 'Extendability', 18 | Svg: require('@site/static/img/extendability.svg').default, 19 | description: ( 20 | <> 21 | Model forge can easily be extended with custom providers, helping you specify distinct behavior where 22 | needed. 23 | 24 | ), 25 | }, 26 | { 27 | title: 'Android Friendly', 28 | Svg: require('@site/static/img/android.svg').default, 29 | description: ( 30 | <> 31 | Model forge can be used in jvm and android projects alike. 32 | 33 | ), 34 | }, 35 | { 36 | title: 'Open-Source', 37 | Svg: require('@site/static/img/open_source.svg').default, 38 | description: ( 39 | <> 40 | Model forge is open-source and developed by the community. Help us shape the future by joining us 41 | on GitHub. 42 | 43 | ), 44 | }, 45 | ]; 46 | 47 | function Feature({Svg, title, description}) { 48 | return ( 49 |
50 |
51 | 52 |
53 |
54 |

{title}

55 |

{description}

56 |
57 |
58 | ); 59 | } 60 | 61 | export default function HomepageFeatures() { 62 | return ( 63 |
64 |
65 |
66 | {FeatureList.map((props, idx) => ( 67 | 68 | ))} 69 |
70 |
71 |
72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /website/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #ff3908; 10 | --ifm-code-font-size: 95%; 11 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 12 | 13 | /*--ifm-color-secondary: #f7e3d5; !* button color *!*/ 14 | /*--ifm-color-secondary-dark: #4fddbf; !* button hover *!*/ 15 | /*--ifm-color-gray-900: #000000; !* button text color *!*/ 16 | } 17 | 18 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 19 | [data-theme='dark'] { 20 | --ifm-color-primary: #f7e3d5; 21 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 22 | 23 | /*--ifm-color-secondary: #c3eb78; !* button color *!*/ 24 | /*--ifm-color-secondary-dark: #4fddbf; !* button hover *!*/ 25 | /*--ifm-color-gray-900: #FFFFFF; !* button text color *!*/ 26 | } 27 | -------------------------------------------------------------------------------- /website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Link from '@docusaurus/Link'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import Layout from '@theme/Layout'; 6 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const {siteConfig} = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 |

{siteConfig.title}

16 |

{siteConfig.tagline}

17 |
18 | 21 | Start Forging 🔥🔨 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home() { 30 | const {siteConfig} = useDocusaurusContext(); 31 | return ( 32 | 35 | 36 |
37 | 38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /website/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /website/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloCuriosity/model-forge/76c04cf8134a3a7dbf7718302dba768087340fa1/website/static/.nojekyll -------------------------------------------------------------------------------- /website/static/img/android.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/static/img/build.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/static/img/extendability.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloCuriosity/model-forge/76c04cf8134a3a7dbf7718302dba768087340fa1/website/static/img/favicon.ico -------------------------------------------------------------------------------- /website/static/img/open_source.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --------------------------------------------------------------------------------