├── version.txt ├── settings.gradle ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── spockito-junit4-idea-testrun.png ├── spockito-junit5-idea-testrun.png ├── .travis.yml ├── spockito-junit5 ├── README.md └── src │ ├── main │ └── java │ │ └── org │ │ └── tools4j │ │ └── spockito │ │ └── jupiter │ │ ├── AggregateTableRow.java │ │ ├── SpockitoExtension.java │ │ ├── JoinOn.java │ │ ├── TableValueConverter.java │ │ ├── TableRowAggregator.java │ │ ├── TableArgumentsProvider.java │ │ ├── TableSource.java │ │ ├── TableSourceDataProvider.java │ │ └── TableRowConverters.java │ └── test │ └── java │ └── org │ └── tools4j │ └── spockito │ └── jupiter │ ├── Operation.java │ ├── TableSourceTest.java │ └── SpockitoExtensionTest.java ├── LICENSE.md ├── etc └── LICENSE.template ├── spockito-table └── src │ ├── main │ └── java │ │ └── org │ │ └── tools4j │ │ └── spockito │ │ └── table │ │ ├── TableConverter.java │ │ ├── TableRowConverter.java │ │ ├── TableJoiner.java │ │ ├── SpockitoException.java │ │ ├── Row.java │ │ ├── SpockitoData.java │ │ ├── TableRow.java │ │ ├── DataProvider.java │ │ ├── Data.java │ │ ├── Column.java │ │ ├── Primitives.java │ │ ├── TableData.java │ │ ├── ValueConverter.java │ │ ├── Table.java │ │ ├── SpockitoTableConverter.java │ │ ├── InjectionContext.java │ │ ├── Strings.java │ │ ├── TableDataProvider.java │ │ ├── SpockitoTableJoiner.java │ │ ├── GenericTypes.java │ │ ├── SpockitoTableRow.java │ │ ├── SpockitoTableRowConverter.java │ │ └── SpockitoTable.java │ └── test │ └── java │ └── org │ └── tools4j │ └── spockito │ └── table │ ├── UnescapeTest.java │ └── TableTest.java ├── .github └── workflows │ └── gradle.yml ├── spockito-junit4 ├── src │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── tools4j │ │ │ └── spockito │ │ │ ├── Operation.java │ │ │ ├── UnrollClassDataToFieldsTest.java │ │ │ ├── UnrollClassDataToMethodTest.java │ │ │ ├── UnrollClassDataToConstructorTest.java │ │ │ ├── UnrollMethodDataTest.java │ │ │ ├── SpockitoBeforeAfterTest.java │ │ │ ├── CustomConverterTest.java │ │ │ ├── FaqTest.java │ │ │ └── AdvancedDataTypesTest.java │ └── main │ │ └── java │ │ └── org │ │ └── tools4j │ │ └── spockito │ │ ├── AbstractSpockitoTestRunner.java │ │ ├── MethodLevelFilter.java │ │ ├── UnrolledTestMethod.java │ │ ├── SingleTestMultiRowRunner.java │ │ └── TableRowConverters.java └── README.md ├── gradlew.bat ├── README.md └── gradlew /version.txt: -------------------------------------------------------------------------------- 1 | 2.1-SNAPSHOT 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ( 2 | 'spockito-table', 3 | 'spockito-junit4', 4 | 'spockito-junit5') 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .gradle 3 | .project 4 | .settings 5 | .idea 6 | bin 7 | build 8 | gradle.properties 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tools4j/spockito/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /spockito-junit4-idea-testrun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tools4j/spockito/HEAD/spockito-junit4-idea-testrun.png -------------------------------------------------------------------------------- /spockito-junit5-idea-testrun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tools4j/spockito/HEAD/spockito-junit5-idea-testrun.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: java 3 | jdk: 4 | - openjdk8 5 | - openjdk11 6 | 7 | after_success: 8 | - gradle jacocoTestReport coveralls 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /spockito-junit5/README.md: -------------------------------------------------------------------------------- 1 | # spockito-junit5 2 | Java JUnit 5.x runner for parameterized tests where the test cases are defined in a table-like manner. 3 | 4 | See [main documentation](https://github.com/tools4j/spockito/blob/master/README.md) for more information. 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 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. -------------------------------------------------------------------------------- /etc/LICENSE.template: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-${year} tools4j.org (Marco Terzer) 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. -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/TableConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | @FunctionalInterface 27 | public interface TableConverter { 28 | Object convert(Table table); 29 | } 30 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/TableRowConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | @FunctionalInterface 27 | public interface TableRowConverter { 28 | Object convert(TableRow tableRow); 29 | } 30 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/TableJoiner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | public interface TableJoiner { 27 | 28 | JoinBuilder onAllCommonColumns(); 29 | JoinBuilder on(int child, int parent); 30 | JoinBuilder on(String child, String parent); 31 | JoinBuilder on(String common); 32 | 33 | interface JoinBuilder { 34 | JoinBuilder and(int child, int parent); 35 | JoinBuilder and(String child, String parent); 36 | JoinBuilder and(String common); 37 | Table apply(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | /** 27 | * Exception thrown when spockito fails to inject data for instance due to failed value conversion. 28 | */ 29 | public class SpockitoException extends RuntimeException { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | public SpockitoException(final String message) { 34 | super(message); 35 | } 36 | 37 | public SpockitoException(final String message, final Throwable cause) { 38 | super(message, cause); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | repository_dispatch: 4 | types: run-commit-tests 5 | push: 6 | branches: 7 | - master 8 | pull_request: 9 | types: [opened, synchronize] 10 | branches: 11 | - master 12 | 13 | env: 14 | GRADLE_OPTS: "-Dorg.gradle.daemon=false" 15 | 16 | jobs: 17 | java-build: 18 | name: Java ${{ matrix.java }} (${{ matrix.os }}) 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | java: [ '8', '11', '17' ] 24 | os: ['ubuntu-latest', 'windows-latest'] 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v2 28 | - name: Cache Gradle dependencies 29 | uses: actions/cache@v1 30 | with: 31 | path: ~/.gradle/caches 32 | key: ${{ runner.os }}-gradle-caches-${{ hashFiles('**/*.gradle') }} 33 | restore-keys: | 34 | ${{ runner.os }}-gradle-caches- 35 | - name: Cache Gradle wrappers 36 | uses: actions/cache@v1 37 | with: 38 | path: ~/.gradle/wrapper 39 | key: ${{ runner.os }}-gradle-wrapper 40 | - name: Setup java 41 | uses: actions/setup-java@v1 42 | with: 43 | java-version: ${{ matrix.java }} 44 | - name: Build with Gradle 45 | run: ./gradlew 46 | - name: Copy crash logs 47 | id: copy_crash_logs 48 | if: failure() 49 | run: | 50 | echo "::set-output name=dir::build/crash_logs" 51 | ./gradlew copyCrashLogs 52 | - name: Upload crash logs 53 | if: always() && steps.copy_crash_logs.outputs.dir == 'build/crash_logs' 54 | uses: actions/upload-artifact@v1 55 | with: 56 | name: crash-logs-${{ matrix.os }}-java-${{ matrix.java }} 57 | path: ${{ steps.copy_crash_logs.outputs.dir }} 58 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/Row.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.annotation.Documented; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 32 | import static java.lang.annotation.ElementType.FIELD; 33 | import static java.lang.annotation.ElementType.PARAMETER; 34 | 35 | /** 36 | * Annotation for fields or parameters referencing a whole {@link TableRow} of a {@link Table}. If the target type is 37 | * int, the row index will be assigned instead. 38 | */ 39 | @Retention(RetentionPolicy.RUNTIME) 40 | @Target(value = {ANNOTATION_TYPE, FIELD, PARAMETER}) 41 | @Documented 42 | public @interface Row { 43 | } -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/AggregateTableRow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.params.aggregator.AggregateWith; 27 | import org.tools4j.spockito.table.TableRow; 28 | 29 | import java.lang.annotation.Documented; 30 | import java.lang.annotation.Retention; 31 | import java.lang.annotation.Target; 32 | 33 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 34 | import static java.lang.annotation.ElementType.PARAMETER; 35 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 36 | 37 | /** 38 | * Annotation to reference a whole {@link TableRow} of a {@link TableSource}. 39 | * 40 | *

This is a shorter version equivalent to {@code @AggregateWith(TableRowAggregator.class)}. 41 | */ 42 | @Target({ANNOTATION_TYPE, PARAMETER}) 43 | @Retention(RUNTIME) 44 | @Documented 45 | @AggregateWith(TableRowAggregator.class) 46 | public @interface AggregateTableRow { 47 | } 48 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | /** 27 | * Class that can be extended and whose {@link Data data} provider elements will be automatically initialised upon 28 | * construction. 29 | * 30 | *

For instance a table can be inlined in a (test) method as follows: 31 | * 32 | *

33 |    void printPersons() {
34 |      final class Person {
35 |        String name;
36 |        int age;
37 |      }
38 |      final class MyData extends SpockitoData {
39 |        {@code @TableData}({
40 |          "| Name  | Age |",
41 |          "| Henry |  27 |",
42 |          "| Frank |  29 |"
43 |        })
44 |       {@code List persons;}
45 |      }
46 | 
47 |      final MyData data = new MyData();
48 |      data.persons.forEach(person -> System.out.println(person.name + " is " + person.age + " years old"));
49 |    }
50 |  * 
51 | */ 52 | public class SpockitoData { 53 | 54 | /** 55 | * Default constructor; invokes {@link SpockitoAnnotations#initData(Object)} with itself. 56 | */ 57 | public SpockitoData() { 58 | SpockitoAnnotations.initData(this); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/SpockitoExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.api.extension.BeforeAllCallback; 27 | import org.junit.jupiter.api.extension.ExtensionContext; 28 | import org.junit.jupiter.api.extension.TestInstancePostProcessor; 29 | import org.tools4j.spockito.table.SpockitoAnnotations; 30 | 31 | /** 32 | * Test extension to initialise {@linkplain org.tools4j.spockito.table.DataProvider data providers} such as fields 33 | * annotated with {@link org.tools4j.spockito.table.Data @Data} or {@link TableSource @TableSource}. 34 | * 35 | * @see SpockitoAnnotations#initData(Object) 36 | * @see SpockitoAnnotations#initStaticData(Class) 37 | */ 38 | public class SpockitoExtension implements BeforeAllCallback, TestInstancePostProcessor { 39 | 40 | @Override 41 | public void beforeAll(final ExtensionContext context) { 42 | SpockitoAnnotations.initStaticData(context.getRequiredTestClass()); 43 | } 44 | 45 | @Override 46 | public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { 47 | SpockitoAnnotations.initData(testInstance); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /spockito-table/src/test/java/org/tools4j/spockito/table/UnescapeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import org.junit.jupiter.api.Test; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; 29 | 30 | /** 31 | * Unit test for {@link Strings#unescape(String)} 32 | */ 33 | public class UnescapeTest { 34 | 35 | @Test 36 | public void shouldUnescape() { 37 | assertEquals(",", Strings.unescape("\\,")); 38 | assertEquals(";", Strings.unescape("\\;")); 39 | assertEquals("|", Strings.unescape("\\|")); 40 | assertEquals("=", Strings.unescape("\\=")); 41 | assertEquals("\\", Strings.unescape("\\\\")); 42 | assertEquals("'", Strings.unescape("\\'")); 43 | assertEquals("123|456", Strings.unescape("123\\|456")); 44 | } 45 | 46 | @Test 47 | public void shouldNotUnescape() { 48 | assertEquals("\\blabla", Strings.unescape("\\blabla")); 49 | assertEquals("\\123", Strings.unescape("\\123")); 50 | } 51 | 52 | @Test 53 | public void shouldUnescapeMultiple() { 54 | assertEquals("\\,123|456;\\a\\;\\8bla'\\'xxx", Strings.unescape("\\\\,123\\|456\\;\\a\\\\\\;\\8bla\\'\\\\'xxx")); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/TableRow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.Type; 27 | import java.util.Iterator; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.stream.Stream; 31 | import java.util.stream.StreamSupport; 32 | 33 | /** 34 | * Represents a single row of a {@link Table}; cell values are kept as strings but convenience methods exist to convert 35 | * them to a value of any desired type. 36 | */ 37 | public interface TableRow extends Iterable { 38 | 39 | Table getTable(); 40 | int getColumnCount(); 41 | int getRowIndex(); 42 | boolean isSeparatorRow(); 43 | 44 | String get(int index); 45 | String get(String name); 46 | int indexOf(String value); 47 | 48 | String[] toArray(); 49 | List toList(); 50 | Map toMap(); 51 | T to(Class type); 52 | T to(Class type, ValueConverter valueConverter); 53 | T to(Class type, Type genericType, ValueConverter valueConverter); 54 | 55 | @Override 56 | Iterator iterator(); 57 | default Stream stream() { 58 | return StreamSupport.stream(spliterator(), false); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/JoinOn.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import java.lang.annotation.Documented; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.Target; 29 | 30 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 31 | import static java.lang.annotation.ElementType.PARAMETER; 32 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 33 | 34 | /** 35 | * Annotation for a method parameter to join the parameter's table data with the parent table that defines the 36 | * parameterized test data. Both the test method and the parameter are typically annotated with {@link TableSource} to 37 | * define parent and child table, respectively. 38 | */ 39 | @Target({ANNOTATION_TYPE, PARAMETER}) 40 | @Retention(RUNTIME) 41 | @Documented 42 | public @interface JoinOn { 43 | /** @return join fields that are common in the parent and the child table*/ 44 | String[] value() default {}; 45 | /** @return join fields in the parent table; must have a counterpart in {@link #child()}*/ 46 | String[] parent() default {}; 47 | /** @return join fields in the child table; must have a counterpart in {@link #parent()}*/ 48 | String[] child() default {}; 49 | } 50 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/DataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | /** 27 | * A {@code DataProvider} is responsible for {@linkplain #provideData providing} data to be injected to a field or to 28 | * parameters of a method. 29 | * 30 | *

A {@code DataProvider} can be registered via the {@link Data @Data} annotation. 31 | * 32 | *

Implementations must provide a no-args constructor. 33 | */ 34 | public interface DataProvider { 35 | 36 | /** 37 | * Returns true if the data provider is applicable for the given injection context. The method returns true by 38 | * default but some implementations may return false in certain circumstances for instance to prevent double 39 | * injection of data when running methods as tests. 40 | * 41 | * @param context the injection context 42 | * @return true by default 43 | */ 44 | default boolean applicable(final InjectionContext context) { 45 | return true; 46 | } 47 | 48 | /** 49 | * Provides the data to be injected to a field or to method parameters. 50 | * 51 | * @param context the current injection context; never {@code null} 52 | * @return the data to inject; never {@code null} 53 | */ 54 | Object provideData(InjectionContext context); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/Operation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | public enum Operation { 27 | Add { 28 | public int evaluate(final int operand1, final int operand2) { 29 | return operand1 + operand2; 30 | } 31 | @Override 32 | public int neutralSecondOperand() { 33 | return 0; 34 | } 35 | }, 36 | Subtract { 37 | public int evaluate(final int operand1, final int operand2) { 38 | return operand1 - operand2; 39 | } 40 | @Override 41 | public int neutralSecondOperand() { 42 | return 0; 43 | } 44 | }, 45 | Multiply { 46 | public int evaluate(final int operand1, final int operand2) { 47 | return operand1 * operand2; 48 | } 49 | @Override 50 | public int neutralSecondOperand() { 51 | return 1; 52 | } 53 | }, 54 | Divide { 55 | public int evaluate(final int operand1, final int operand2) { 56 | return operand1 / operand2; 57 | } 58 | @Override 59 | public int neutralSecondOperand() { 60 | return 1; 61 | } 62 | }; 63 | abstract public int evaluate(int operand1, int operand2); 64 | abstract public int neutralSecondOperand(); 65 | } 66 | -------------------------------------------------------------------------------- /spockito-junit5/src/test/java/org/tools4j/spockito/jupiter/Operation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | public enum Operation { 27 | Add { 28 | public int evaluate(final int operand1, final int operand2) { 29 | return operand1 + operand2; 30 | } 31 | @Override 32 | public int neutralSecondOperand() { 33 | return 0; 34 | } 35 | }, 36 | Subtract { 37 | public int evaluate(final int operand1, final int operand2) { 38 | return operand1 - operand2; 39 | } 40 | @Override 41 | public int neutralSecondOperand() { 42 | return 0; 43 | } 44 | }, 45 | Multiply { 46 | public int evaluate(final int operand1, final int operand2) { 47 | return operand1 * operand2; 48 | } 49 | @Override 50 | public int neutralSecondOperand() { 51 | return 1; 52 | } 53 | }, 54 | Divide { 55 | public int evaluate(final int operand1, final int operand2) { 56 | return operand1 / operand2; 57 | } 58 | @Override 59 | public int neutralSecondOperand() { 60 | return 1; 61 | } 62 | }; 63 | abstract public int evaluate(int operand1, int operand2); 64 | abstract public int neutralSecondOperand(); 65 | } 66 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/Data.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.annotation.Documented; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 32 | import static java.lang.annotation.ElementType.FIELD; 33 | import static java.lang.annotation.ElementType.METHOD; 34 | import static java.lang.annotation.ElementType.PARAMETER; 35 | 36 | /** 37 | * {@code @Data} is an annotation that is used to register {@linkplain DataProvider data providers} for the annotated 38 | * field, method or method parameter. 39 | * 40 | *

{@code @Data} may also be used as a meta-annotation in order to create a custom composed annotation 41 | * that inherits the semantics of {@code @Data}. 42 | * 43 | *

{@code @Data} are injected via {@link SpockitoAnnotations#initData(Object)} or when creating subclass instances of 44 | * {@link SpockitoData}. 45 | */ 46 | @Target({ANNOTATION_TYPE, METHOD, FIELD, PARAMETER}) 47 | @Retention(RetentionPolicy.RUNTIME) 48 | @Documented 49 | public @interface Data { 50 | /** 51 | * The type of {@link DataProvider} to be used. 52 | * @return the data provider type 53 | */ 54 | Class value(); 55 | } 56 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/Column.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.annotation.Documented; 27 | import java.lang.annotation.Retention; 28 | import java.lang.annotation.RetentionPolicy; 29 | import java.lang.annotation.Target; 30 | 31 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 32 | import static java.lang.annotation.ElementType.FIELD; 33 | import static java.lang.annotation.ElementType.PARAMETER; 34 | 35 | /** 36 | * Annotation for fields or parameters referencing a column of a {@link TableRow}. Fields need only be annotated if the 37 | * field name differs from the column name. Parameters need to be annotated if the index does not match the column 38 | * index in the table. 39 | * 40 | *

{@code @Column} may also be used as a meta-annotation in order to create a custom composed annotation 41 | * that inherits the semantics of {@code @Column}. 42 | */ 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @Target(value = {ANNOTATION_TYPE, FIELD, PARAMETER}) 45 | @Documented 46 | public @interface Column { 47 | /** 48 | * Returns the name to reference a column in the table. The value can be omitted when annotating a field whose name 49 | * matches the column name. 50 | * 51 | * @return the column name, empty to match field name 52 | */ 53 | String value() default ""; 54 | } -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableValueConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.api.extension.ParameterContext; 27 | import org.junit.jupiter.params.converter.ArgumentConversionException; 28 | import org.junit.jupiter.params.converter.ArgumentConverter; 29 | import org.tools4j.spockito.table.SpockitoValueConverter; 30 | import org.tools4j.spockito.table.TableData; 31 | import org.tools4j.spockito.table.ValueConverter; 32 | 33 | import java.lang.reflect.Executable; 34 | import java.lang.reflect.Parameter; 35 | 36 | public class TableValueConverter implements ArgumentConverter { 37 | 38 | @Override 39 | public Object convert(final Object source, final ParameterContext context) throws ArgumentConversionException { 40 | final Parameter parameter = context.getParameter(); 41 | final ValueConverter valueConverter = valueConverter(context); 42 | return valueConverter.convert(parameter.getType(), parameter.getParameterizedType(), String.valueOf(source)); 43 | } 44 | 45 | static ValueConverter valueConverter(final ParameterContext context) { 46 | final Executable executable = context.getDeclaringExecutable(); 47 | final TableData tableSource = executable.getAnnotation(TableData.class); 48 | final Class valueConverterClass = tableSource != null ? tableSource.valueConverter() : 49 | SpockitoValueConverter.class; 50 | return ValueConverter.create(valueConverterClass); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableRowAggregator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.api.extension.ParameterContext; 27 | import org.junit.jupiter.params.aggregator.ArgumentsAccessor; 28 | import org.junit.jupiter.params.aggregator.ArgumentsAggregationException; 29 | import org.junit.jupiter.params.aggregator.ArgumentsAggregator; 30 | import org.tools4j.spockito.table.Table; 31 | import org.tools4j.spockito.table.TableRow; 32 | import org.tools4j.spockito.table.ValueConverter; 33 | 34 | import java.lang.reflect.Parameter; 35 | 36 | import static org.tools4j.spockito.jupiter.TableValueConverter.valueConverter; 37 | 38 | /** 39 | * Aggregator used by {@linkplain AggregateTableRow @AggregateTableRow} or if used with the 40 | * {@linkplain org.junit.jupiter.params.aggregator.AggregateWith @AggregateWith} annotation. 41 | * 42 | *

The annotated method parameter references a whole {@link TableRow} of a {@link Table}. 43 | */ 44 | public class TableRowAggregator implements ArgumentsAggregator { 45 | 46 | @Override 47 | public Object aggregateArguments(final ArgumentsAccessor accessor, final ParameterContext context) throws ArgumentsAggregationException { 48 | final Parameter parameter = context.getParameter(); 49 | final ValueConverter valueConverter = valueConverter(context); 50 | final TableRow tableRow = accessor.get(context.getIndex(), TableRow.class); 51 | return tableRow.to(parameter.getType(), parameter.getParameterizedType(), valueConverter); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/Primitives.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | /** 27 | * Contains static helper methods related to primitive types. 28 | */ 29 | enum Primitives { 30 | ; 31 | /** 32 | * Returns the boxing type given a primitive type. 33 | * 34 | * @param type a primitive type such as {@code int.class} 35 | * @param the type parameter 36 | * @return the boxing type such as {@code Integer.class} 37 | */ 38 | @SuppressWarnings("unchecked") //this is a bit dodgy but boxing allows assignment hence it works in practice 39 | static Class boxingTypeFor(final Class type) { 40 | if (double.class.equals(type)) { 41 | return (Class)Double.class; 42 | } 43 | if (float.class.equals(type)) { 44 | return (Class)Float.class; 45 | } 46 | if (long.class.equals(type)) { 47 | return (Class)Long.class; 48 | } 49 | if (int.class.equals(type)) { 50 | return (Class)Integer.class; 51 | } 52 | if (short.class.equals(type)) { 53 | return (Class)Short.class; 54 | } 55 | if (byte.class.equals(type)) { 56 | return (Class)Byte.class; 57 | } 58 | if (boolean.class.equals(type)) { 59 | return (Class)Boolean.class; 60 | } 61 | if (char.class.equals(type)) { 62 | return (Class)Character.class; 63 | } 64 | if (void.class.equals(type)) { 65 | return (Class)Void.class; 66 | } 67 | throw new IllegalArgumentException("Not a primitive type: " + type.getName()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/UnrollClassDataToFieldsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | import org.tools4j.spockito.Spockito.Name; 30 | import org.tools4j.spockito.Spockito.Unroll; 31 | 32 | @Unroll({ 33 | "| Operation | Sign | Operand1 | Operand2 | Result | NeutralOperand |", 34 | "|-----------|------|----------|----------|--------|----------------|", 35 | "| Add | + | 4 | 7 | 11 | 0 |", 36 | "| Subtract | - | 111 | 12 | 99 | 0 |", 37 | "| Multiply | * | 24 | 5 | 120 | 1 |", 38 | "| Divide | / | 24 | 3 | 8 | 1 |" 39 | }) 40 | @Name("[{row}]: {Operation}") 41 | @RunWith(Spockito.class) 42 | public class UnrollClassDataToFieldsTest { 43 | 44 | @Spockito.Ref 45 | private Operation operation; 46 | @Spockito.Ref 47 | private char sign; 48 | @Spockito.Ref 49 | private int operand1; 50 | @Spockito.Ref 51 | private int operand2; 52 | @Spockito.Ref 53 | private int result; 54 | @Spockito.Ref 55 | private int neutralOperand; 56 | 57 | @Test 58 | @Spockito.Name("[{row}]: {Operand1} {Sign} {Operand2} = {Result}") 59 | public void testOperation() { 60 | Assert.assertEquals("Result is wrong!", result, operation.evaluate(operand1, operand2)); 61 | } 62 | 63 | @Test 64 | @Spockito.Name("[{row}]: {Operand1} {Sign} {NeutralOperand} = {Operand1}") 65 | public void testNeutralOperand() { 66 | Assert.assertEquals("Result with neutral operand is wrong!", 67 | operand1, operation.evaluate(operand1, neutralOperand)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableArgumentsProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.api.extension.ExtensionContext; 27 | import org.junit.jupiter.params.provider.Arguments; 28 | import org.junit.jupiter.params.provider.ArgumentsProvider; 29 | import org.tools4j.spockito.table.InjectionContext; 30 | import org.tools4j.spockito.table.InjectionContext.Phase; 31 | import org.tools4j.spockito.table.SpockitoException; 32 | import org.tools4j.spockito.table.Table; 33 | import org.tools4j.spockito.table.TableData; 34 | import org.tools4j.spockito.table.TableDataProvider; 35 | 36 | import java.lang.reflect.Method; 37 | import java.util.Arrays; 38 | import java.util.stream.Stream; 39 | 40 | /** 41 | * Provides arguments defined by {@link TableData} using {@link Table}. 42 | */ 43 | final class TableArgumentsProvider implements ArgumentsProvider { 44 | 45 | @Override 46 | public Stream provideArguments(final ExtensionContext context) { 47 | final Method testMethod = context.getRequiredTestMethod(); 48 | final InjectionContext injectionContext = InjectionContext.create(Phase.TEST, testMethod); 49 | final TableDataProvider tableDataProvider = TableSourceDataProvider.DEFAULT_INSTANCE; 50 | if (!tableDataProvider.applicable(injectionContext)) { 51 | //should not happen as it should always be applicable for TEST phase 52 | throw new SpockitoException("Not applicable: " + tableDataProvider); 53 | } 54 | final Object data = tableDataProvider.provideData(injectionContext); 55 | if (data instanceof Object[][]) { 56 | return Arrays.stream((Object[][])data).map(Arguments::of); 57 | } 58 | throw new SpockitoException("Table data provider " + tableDataProvider + " should return value of type Object[][], but found " + data); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/UnrollClassDataToMethodTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | import org.tools4j.spockito.Spockito.Unroll; 30 | 31 | @Unroll({ 32 | "| Operation | Sign | Operand1 | Operand2 | Result | NeutralOperand |", 33 | "|-----------|------|----------|----------|--------|----------------|", 34 | "| Add | + | 4 | 7 | 11 | 0 |", 35 | "| Subtract | - | 111 | 12 | 99 | 0 |", 36 | "| Multiply | * | 24 | 5 | 120 | 1 |", 37 | "| Divide | / | 24 | 3 | 8 | 1 |" 38 | }) 39 | @RunWith(Spockito.class) 40 | public class UnrollClassDataToMethodTest { 41 | 42 | @Test 43 | @Spockito.Name(value = ": {Operand1} {Sign} {Operand2} = {Result}", shortFormat = true) 44 | public void testOperation(@Spockito.Ref("Operation") final Operation operation, 45 | @Spockito.Ref("Operand1") final int operand1, 46 | @Spockito.Ref("Operand2") final int operand2, 47 | @Spockito.Ref("Result") final int result) { 48 | Assert.assertEquals("Result is wrong!", result, operation.evaluate(operand1, operand2)); 49 | } 50 | 51 | @Test 52 | @Spockito.Name(value = ": {Operand1} {Sign} {NeutralOperand} = {Operand1}", shortFormat = true) 53 | public void testNeutralOperand(@Spockito.Ref("Operation") final Operation operation, 54 | @Spockito.Ref("Operand1") final int operand1, 55 | @Spockito.Ref("NeutralOperand") final int neutralOperand) { 56 | Assert.assertEquals("Result with neutral operand is wrong!", 57 | operand1, operation.evaluate(operand1, neutralOperand)); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /spockito-junit4/src/main/java/org/tools4j/spockito/AbstractSpockitoTestRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.runner.Description; 27 | import org.junit.runner.manipulation.Filter; 28 | import org.junit.runner.manipulation.NoTestsRemainException; 29 | import org.junit.runner.notification.RunNotifier; 30 | import org.junit.runners.BlockJUnit4ClassRunner; 31 | import org.junit.runners.model.FrameworkMethod; 32 | import org.junit.runners.model.InitializationError; 33 | import org.junit.runners.model.Statement; 34 | 35 | import java.lang.annotation.Annotation; 36 | import java.util.List; 37 | 38 | /** 39 | * Common base for spockito test runners. 40 | */ 41 | abstract public class AbstractSpockitoTestRunner extends BlockJUnit4ClassRunner { 42 | 43 | public AbstractSpockitoTestRunner(final Class clazz) throws InitializationError { 44 | super(clazz); 45 | } 46 | 47 | @Override 48 | protected Statement classBlock(RunNotifier notifier) { 49 | return childrenInvoker(notifier); 50 | } 51 | 52 | @Override 53 | protected Annotation[] getRunnerAnnotations() { 54 | return new Annotation[0]; 55 | } 56 | 57 | @Override 58 | public void filter(final Filter filter) throws NoTestsRemainException { 59 | if (filter instanceof MethodLevelFilter) { 60 | //Spockito does the necessary filtering with MethodLevelFilter 61 | super.filter(Filter.ALL); 62 | } else { 63 | super.filter(filter); 64 | } 65 | } 66 | 67 | @Override 68 | protected Description describeChild(final FrameworkMethod method) { 69 | final Spockito.Name name = Spockito.nameAnnotationOrNull(method.getMethod()); 70 | if (name != null && name.shortFormat()) { 71 | return Description.createSuiteDescription(testName(method), method.getAnnotations()); 72 | } else { 73 | return super.describeChild(method); 74 | } 75 | } 76 | 77 | @Override 78 | protected void collectInitializationErrors(final List errors) { 79 | //don't do here, do validation in our own constructor 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /spockito-junit4/src/main/java/org/tools4j/spockito/MethodLevelFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.runner.Description; 27 | import org.junit.runner.manipulation.Filter; 28 | 29 | import java.util.Objects; 30 | 31 | /** 32 | * Method filter wrapped around an original filter in {@link Spockito#filter(Filter)} to work around an Intellij problem 33 | * when re-running individual tests. 34 | */ 35 | public final class MethodLevelFilter extends Filter { 36 | 37 | private final Filter delegate; 38 | 39 | public MethodLevelFilter(final Filter delegate) { 40 | this.delegate = Objects.requireNonNull(delegate); 41 | } 42 | 43 | @Override 44 | public boolean shouldRun(final Description description) { 45 | if (description.isTest()) { 46 | return delegate.shouldRun(description); 47 | } 48 | 49 | // explicitly check if any children want to run 50 | for (Description child : description.getChildren()) { 51 | if (shouldRun(child)) { 52 | return true; 53 | } 54 | //Intellij bug, we get the wrong description, let us test with a slightly modified one now 55 | final Description relaxed = Description.createTestDescription( 56 | child.getTestClass(), getMethodName(child.getDisplayName()) 57 | ); 58 | if (shouldRun(relaxed)) { 59 | return true; 60 | } 61 | } 62 | return false; 63 | } 64 | 65 | private static String getMethodName(final String name) { 66 | return name.substring(0, findFirstNonNameChar(name)); 67 | } 68 | 69 | private static int findFirstNonNameChar(final String name) { 70 | int index = 0; 71 | if (index < name.length() && Character.isJavaIdentifierStart(name.charAt(index))) { 72 | index++; 73 | while (index < name.length() && Character.isJavaIdentifierPart(name.charAt(index))) { 74 | index++; 75 | } 76 | } 77 | return index; 78 | } 79 | 80 | @Override 81 | public String describe() { 82 | return delegate.describe(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/UnrollClassDataToConstructorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | import org.tools4j.spockito.Spockito.Name; 30 | import org.tools4j.spockito.Spockito.Unroll; 31 | 32 | @Unroll({ 33 | "| Operation | Sign | Operand1 | Operand2 | Result | NeutralOperand |", 34 | "|-----------|------|----------|----------|--------|----------------|", 35 | "| Add | + | 4 | 7 | 11 | 0 |", 36 | "| Subtract | - | 111 | 12 | 99 | 0 |", 37 | "| Multiply | * | 24 | 5 | 120 | 1 |", 38 | "| Divide | / | 24 | 3 | 8 | 1 |" 39 | }) 40 | @Name("[{row}]: {Operation}") 41 | @RunWith(Spockito.class) 42 | public class UnrollClassDataToConstructorTest { 43 | 44 | private final Operation operation; 45 | private final char sign; 46 | private final int operand1; 47 | private final int operand2; 48 | private final int result; 49 | private final int neutralOperand; 50 | 51 | public UnrollClassDataToConstructorTest(final Operation operation, final char sign, 52 | final int operand1, final int operand2, final int result, final int neutralOperand) { 53 | this.operation = operation; 54 | this.sign = sign; 55 | this.operand1 = operand1; 56 | this.operand2 = operand2; 57 | this.result = result; 58 | this.neutralOperand = neutralOperand; 59 | } 60 | 61 | @Test 62 | @Spockito.Name("[{row}]: {Operand1} {Sign} {Operand2} = {Result}") 63 | public void testOperation() { 64 | Assert.assertEquals("Result is wrong!", result, operation.evaluate(operand1, operand2)); 65 | } 66 | 67 | @Test 68 | @Spockito.Name("[{row}]: {Operand1} {Sign} {NeutralOperand} = {Operand1}") 69 | public void testNeutralOperand() { 70 | Assert.assertEquals("Result with neutral operand is wrong!", 71 | operand1, operation.evaluate(operand1, neutralOperand)); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /spockito-junit4/src/main/java/org/tools4j/spockito/UnrolledTestMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.runners.model.FrameworkMethod; 27 | import org.tools4j.spockito.table.TableRow; 28 | import org.tools4j.spockito.table.ValueConverter; 29 | 30 | import java.lang.reflect.Method; 31 | 32 | import static java.util.Objects.requireNonNull; 33 | 34 | /** 35 | * Extension of framework method representing a test method with a data row. 36 | */ 37 | public class UnrolledTestMethod extends FrameworkMethod { 38 | 39 | private final TableRow tableRow; 40 | private final ValueConverter valueConverter; 41 | 42 | public UnrolledTestMethod(final Method method, final TableRow tableRow, final ValueConverter valueConverter) { 43 | super(method); 44 | this.tableRow = requireNonNull(tableRow); 45 | this.valueConverter = requireNonNull(valueConverter); 46 | } 47 | 48 | @Override 49 | public Object invokeExplosively(final Object target, final Object... params) throws Throwable { 50 | return super.invokeExplosively(target, getTestArgs()); 51 | } 52 | 53 | protected TableRow getTableRow() { 54 | return tableRow; 55 | } 56 | 57 | protected Object[] getTestArgs() { 58 | return TableRowConverters.convert(tableRow, getMethod(), valueConverter); 59 | } 60 | 61 | @Override 62 | public String getName() { 63 | return super.getName(); 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | int code = super.hashCode(); 69 | code = 31 * code + tableRow.getTable().hashCode(); 70 | code = 31 * code + tableRow.getRowIndex(); 71 | return code; 72 | } 73 | 74 | @Override 75 | public boolean equals(final Object obj) { 76 | if (obj == this) { 77 | return true; 78 | } 79 | if (obj == null || obj.getClass() != getClass() || !super.equals(obj)) { 80 | return false; 81 | } 82 | final UnrolledTestMethod other = (UnrolledTestMethod)obj; 83 | return this.tableRow.getTable().equals(other.tableRow.getTable()) && 84 | this.tableRow.getRowIndex() == other.tableRow.getRowIndex(); 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return super.toString() + "[" + tableRow.getRowIndex() + "]"; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/TableData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.annotation.Documented; 27 | import java.lang.annotation.Inherited; 28 | import java.lang.annotation.Retention; 29 | import java.lang.annotation.Target; 30 | 31 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 32 | import static java.lang.annotation.ElementType.FIELD; 33 | import static java.lang.annotation.ElementType.METHOD; 34 | import static java.lang.annotation.ElementType.PARAMETER; 35 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 36 | 37 | /** 38 | * Annotation to inject table data into a field, method or method parameter; the table data is define2 in a 39 | * structure as follows: 40 | *

41 |  * | ColumnA   | ColumnB   | ColumnC   |
42 |  * |-----------|-----------|-----------|
43 |  * | value_1_A | value_1_B | value_1_C |
44 |  * | value_2_A | value_2_B | value_2_C |
45 |  * etc...
46 |  * 
47 | * The separator row after the column headers is optional and = instead of - can be used. Separator rows can be 48 | * placed anywhere in the table and are ignored when the table is parsed. 49 | */ 50 | @Target({ANNOTATION_TYPE, METHOD, FIELD, PARAMETER}) 51 | @Retention(RUNTIME) 52 | @Documented 53 | @Inherited 54 | @Data(TableDataProvider.class) 55 | public @interface TableData { 56 | /** 57 | * Test case data for parameterized tests structured as follows: 58 | *
59 |      * | ColumnA   | ColumnB   | ColumnC   |
60 |      * |-----------|-----------|-----------|
61 |      * | value_1_A | value_1_B | value_1_C |
62 |      * | value_2_A | value_2_B | value_2_C |
63 |      * etc...
64 |      * 
65 | * The separator row after the column headers is optional and = instead of - can be used. Separator rows can be 66 | * placed anywhere in the table and are ignored when the table is parsed. 67 | * 68 | * @return An array of strings represented as table data; string[0] contains the header 69 | * row with column names 70 | */ 71 | String[] value(); 72 | 73 | /** 74 | * Converter to use for individual values; conversion is done purely based on the value type. 75 | * Returned classes must have a public no-argument constructor. 76 | * 77 | * @return the value converter to use 78 | * @see SpockitoValueConverter 79 | */ 80 | Class valueConverter() default SpockitoValueConverter.class; 81 | } 82 | -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.params.provider.ArgumentsSource; 27 | import org.tools4j.spockito.table.Data; 28 | import org.tools4j.spockito.table.SpockitoValueConverter; 29 | import org.tools4j.spockito.table.ValueConverter; 30 | 31 | import java.lang.annotation.Documented; 32 | import java.lang.annotation.Retention; 33 | import java.lang.annotation.Target; 34 | 35 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 36 | import static java.lang.annotation.ElementType.FIELD; 37 | import static java.lang.annotation.ElementType.METHOD; 38 | import static java.lang.annotation.ElementType.PARAMETER; 39 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 40 | 41 | /** 42 | * Source for {@link org.junit.jupiter.params.ParameterizedTest ParameterizedTest} data declared in a table like 43 | * structure as follows: 44 | *
45 |  * | ColumnA   | ColumnB   | ColumnC   |
46 |  * |-----------|-----------|-----------|
47 |  * | value_1_A | value_1_B | value_1_C |
48 |  * | value_2_A | value_2_B | value_2_C |
49 |  * etc...
50 |  * 
51 | * The separator row after the column headers is optional and = instead of - can be used. Separator rows can be 52 | * placed anywhere in the table and are ignored when the table is parsed. 53 | */ 54 | @Target({ANNOTATION_TYPE, METHOD, FIELD, PARAMETER}) 55 | @Retention(RUNTIME) 56 | @Documented 57 | @ArgumentsSource(TableArgumentsProvider.class) 58 | @Data(TableSourceDataProvider.class) 59 | public @interface TableSource { 60 | /** 61 | * Test case data for parameterized tests structured as follows: 62 | *
63 |      * | ColumnA   | ColumnB   | ColumnC   |
64 |      * |-----------|-----------|-----------|
65 |      * | value_1_A | value_1_B | value_1_C |
66 |      * | value_2_A | value_2_B | value_2_C |
67 |      * etc...
68 |      * 
69 | * The separator row after the column headers is optional and = instead of - can be used. Separator rows can be 70 | * placed anywhere in the table and are ignored when the table is parsed. 71 | * 72 | * @return An array of strings represented as table data; string[0] contains the header 73 | * row with column names 74 | */ 75 | String[] value(); 76 | 77 | /** 78 | * Converter to use for individual values; conversion is done purely based on the value type. 79 | * Returned classes must have a public no-argument constructor. 80 | * 81 | * @return the value converter to use 82 | * @see SpockitoValueConverter 83 | */ 84 | Class valueConverter() default SpockitoValueConverter.class; 85 | } 86 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/UnrollMethodDataTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | import java.time.LocalDate; 31 | 32 | @RunWith(Spockito.class) 33 | public class UnrollMethodDataTest { 34 | 35 | @Test 36 | @Spockito.Unroll({ 37 | "| Last Name | First Name |", 38 | "| Jones | David |", 39 | "| Jensen | Astrid |" 40 | }) 41 | public void testUnrollNames(String lastName, String firstName) { 42 | Assert.assertTrue("Last Name should start with J", lastName.startsWith("J")); 43 | Assert.assertTrue("First Name should end with id", firstName.endsWith("id")); 44 | } 45 | 46 | @Test 47 | @Spockito.Unroll({ 48 | "| Name | Year | Birthday |", 49 | "|-------|------|------------|", 50 | "| Henry | 1981 | 1981-11-28 |", 51 | "| Jessy | 1965 | 1965-03-28 |" 52 | }) 53 | @Spockito.Name("[{row}]: Name={0}") 54 | public void testUnrollBirthdays(String name, int year, LocalDate birthday) { 55 | Assert.assertEquals("Name should have 5 characters", 5, name.length()); 56 | Assert.assertTrue("Year is before 1990", 1990 > year); 57 | Assert.assertEquals("Day is 28th", 28, birthday.getDayOfMonth()); 58 | Assert.assertEquals("Year is consistent with birthday", year, birthday.getYear()); 59 | } 60 | 61 | @Test 62 | @Spockito.Unroll({ 63 | "| Name | Year | Birthday |", 64 | "|-------|------|------------|", 65 | "| Henry | 1981 | 1981-11-28 |", 66 | "| Jessy | 1965 | 1965-03-28 |" 67 | }) 68 | @Spockito.Name("[{row}]: Name={0}") 69 | public void testUnrollNameAndYearOnly(String name, int year) { 70 | Assert.assertEquals("Name should have 5 characters", 5, name.length()); 71 | Assert.assertTrue("Year is before 1990", 1990 > year); 72 | } 73 | 74 | @Test 75 | @Spockito.Unroll({ 76 | "| Object | Vertices | Angle sum |", 77 | "|==========|==========|===========|", 78 | "| Triangle | 3 | 180 |", 79 | "| Square | 4 | 360 |", 80 | "| Pentagon | 5 | 540 |", 81 | "|----------|----------|-----------|", 82 | }) 83 | @Spockito.Name("[{Object}]: ({Vertices}-2)*180 = {Angle sum}") 84 | public void testUnrollAngularSums(@Spockito.Ref("Vertices") int n, 85 | @Spockito.Ref("Angle sum") int degrees, 86 | @Spockito.Ref("Object") String name) { 87 | Assert.assertTrue("There should be 3 or more vertices", 3 <= n); 88 | Assert.assertEquals("Angular sum is wrong for: " + name, degrees, (n-2)*180); 89 | } 90 | } -------------------------------------------------------------------------------- /spockito-junit5/src/test/java/org/tools4j/spockito/jupiter/TableSourceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.params.ParameterizedTest; 27 | import org.tools4j.spockito.table.Column; 28 | 29 | import java.time.LocalDate; 30 | 31 | import static org.junit.jupiter.api.Assertions.assertEquals; 32 | import static org.junit.jupiter.api.Assertions.assertTrue; 33 | 34 | public class TableSourceTest { 35 | 36 | @TableSource({ 37 | "| Last Name | First Name |", 38 | "| Jones | David |", 39 | "| Jensen | Astrid |" 40 | }) 41 | @ParameterizedTest(name = "[{index}] {1} {0}") 42 | public void testUnrollNames(String lastName, String firstName) { 43 | assertTrue(lastName.startsWith("J"), "Last Name should start with J"); 44 | assertTrue(firstName.endsWith("id"), "First Name should end with id"); 45 | } 46 | 47 | @TableSource({ 48 | "| Name | Year | Birthday |", 49 | "|-------|------|------------|", 50 | "| Henry | 1981 | 1981-11-28 |", 51 | "| Jessy | 1965 | 1965-03-28 |" 52 | }) 53 | @ParameterizedTest(name = "[{index}] {0}") 54 | public void testUnrollBirthdays(String name, int year, LocalDate birthday) { 55 | assertEquals(5, name.length(), "Name should have 5 characters"); 56 | assertTrue(1990 > year, "Year is before 1990"); 57 | assertEquals(28, birthday.getDayOfMonth(), "Day is 28th"); 58 | assertEquals(year, birthday.getYear(), "Year is consistent with birthday"); 59 | } 60 | 61 | @TableSource({ 62 | "| Name | Year | Birthday |", 63 | "|-------|------|------------|", 64 | "| Henry | 1981 | 1981-11-28 |", 65 | "| Jessy | 1965 | 1965-03-28 |" 66 | }) 67 | @ParameterizedTest(name = "[{index}] {0}") 68 | public void testUnrollNameAndYearOnly(String name, int year) { 69 | assertEquals(5, name.length(), "Name should have 5 characters"); 70 | assertTrue(1990 > year, "Year is before 1990"); 71 | } 72 | 73 | @TableSource({ 74 | "| Object | Vertices | Angle sum |", 75 | "|==========|==========|===========|", 76 | "| Triangle | 3 | 180 |", 77 | "| Square | 4 | 360 |", 78 | "| Pentagon | 5 | 540 |", 79 | "|----------|----------|-----------|", 80 | }) 81 | @ParameterizedTest(name = "{2}: ({0}-2)*180 = {1}") 82 | public void testUnrollAngularSums(@Column("Vertices") int n, 83 | @Column("Angle sum") int degrees, 84 | @Column("Object") String name) { 85 | assertTrue(3 <= n, "There should be 3 or more vertices"); 86 | assertEquals(degrees, (n-2)*180, "Angular sum is wrong for: " + name); 87 | } 88 | } -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableSourceDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.platform.commons.annotation.Testable; 27 | import org.tools4j.spockito.table.InjectionContext; 28 | import org.tools4j.spockito.table.InjectionContext.Phase; 29 | import org.tools4j.spockito.table.Table; 30 | import org.tools4j.spockito.table.TableDataProvider; 31 | import org.tools4j.spockito.table.TableRowConverter; 32 | import org.tools4j.spockito.table.ValueConverter; 33 | 34 | import java.lang.reflect.AnnotatedElement; 35 | import java.lang.reflect.Method; 36 | import java.lang.reflect.Parameter; 37 | 38 | import static org.tools4j.spockito.table.SpockitoAnnotations.annotationDirectOrMeta; 39 | 40 | /** 41 | * Provides values defined by a {@link TableSource @TableSource} annotation. 42 | * 43 | *

The implementation is based on {@link TableDataProvider} but annotated {@linkplain Testable testable} methods are 44 | * not invoked. The test framework invokes testable methods using {@link TableArgumentsProvider}. 45 | */ 46 | public class TableSourceDataProvider extends TableDataProvider { 47 | 48 | public static final TableSourceDataProvider DEFAULT_INSTANCE = new TableSourceDataProvider(); 49 | 50 | public TableSourceDataProvider() { 51 | super(TableSourceDataProvider::table, TableSourceDataProvider::valueConverter); 52 | } 53 | 54 | private static Table table(final InjectionContext injectionContext) { 55 | final TableSource tableSource = injectionContext.annotatedElement().getAnnotation(TableSource.class); 56 | return Table.parse(tableSource.value()); 57 | } 58 | 59 | private static Class valueConverter(final InjectionContext injectionContext) { 60 | final TableSource tableSource = injectionContext.annotatedElement().getAnnotation(TableSource.class); 61 | return tableSource.valueConverter(); 62 | } 63 | 64 | @Override 65 | public boolean applicable(final InjectionContext context) { 66 | if (context.phase() == Phase.INIT) { 67 | final AnnotatedElement element = context.annotatedElement(); 68 | if (element instanceof Method) { 69 | final Testable testable = annotationDirectOrMeta(element, Testable.class); 70 | //invoke test methods during TEST phase 71 | return testable == null; 72 | } 73 | } 74 | return true; 75 | } 76 | 77 | @Override 78 | protected TableRowConverter tableRowConverter(final InjectionContext context, 79 | final Parameter parameter, 80 | final int index, 81 | final ValueConverter valueConverter) { 82 | return TableRowConverters.create(context, parameter, index, valueConverter); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/ValueConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.Type; 27 | import java.util.Collection; 28 | import java.util.Map; 29 | 30 | /** 31 | * Handles the conversion of string values to arbitrary target types; supports conversion into generic types such as 32 | * {@code List}. 33 | */ 34 | public interface ValueConverter { 35 | /** 36 | * Converts the given string value into the target type specified by raw and generic type. 37 | * 38 | * @param type the target type in raw form, for instance {@code int.class}, {@code List.class} etc. 39 | * @param genericType the generic target type, same as type for non-generic types; generic type examples are 40 | * {@code List}, {@code Map} etc. 41 | * @param value the value to convert, may be null 42 | * @param the target type parameter 43 | * @return the converted value 44 | */ 45 | T convert(Class type, Type genericType, String value); 46 | 47 | /** 48 | * Converts the given string value into the target type. Use this method for non-generic types, and 49 | * {@link #convert(Class, Type, String)} for generic types. 50 | * 51 | * @param type the target type, a non-generic type such as {@code String.class} 52 | * @param value the value to convert, may be null 53 | * @param the target type parameter 54 | * @return the converted value 55 | */ 56 | default T convert(final Class type, final String value) { 57 | return convert(type, type, value); 58 | } 59 | 60 | /** 61 | * Returns true if the given type supports multi-value conversion, such as maps, collections or arrays. 62 | * 63 | * @param type the target type in raw form, for instance {@code int.class}, {@code List.class} etc. 64 | * @param genericType the generic target type, same as type for non-generic types; generic type examples are 65 | * {@code List}, {@code Map} etc. 66 | * @return true if multi-value conversion is supported by this converter; default implementation returns true for 67 | * collection types, map types and arrays 68 | */ 69 | default boolean isMultiValueType(final Class type, final Type genericType) { 70 | return type.isArray() || Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type); 71 | } 72 | 73 | static ValueConverter create(final Class type) { 74 | if (SpockitoValueConverter.class.equals(type)) { 75 | return SpockitoValueConverter.DEFAULT_INSTANCE; 76 | } 77 | try { 78 | return type.newInstance(); 79 | } catch (final Exception e) { 80 | throw new SpockitoException("Could not create value converter instance of type " + type.getName(), e); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/Table.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.Type; 27 | import java.util.Comparator; 28 | import java.util.Iterator; 29 | import java.util.List; 30 | import java.util.function.Predicate; 31 | import java.util.stream.Collectors; 32 | import java.util.stream.Stream; 33 | import java.util.stream.StreamSupport; 34 | 35 | import static java.util.Objects.requireNonNull; 36 | 37 | /** 38 | * Represents a table with a header row and zero to many data or separator rows; all table elements are strings. 39 | */ 40 | public interface Table extends Iterable { 41 | 42 | int getColumnCount(); 43 | int getRowCount(); 44 | 45 | List getColumnNames(); 46 | String getColumnName(int index); 47 | boolean hasColumn(String columnName); 48 | int getColumnIndexByName(String columnName); 49 | 50 | TableRow getRow(int rowIndex); 51 | int getRowIndex(TableRow row); 52 | 53 | String getValue(int rowIndex, int columnIndex); 54 | String getValue(int rowIndex, String columnName); 55 | 56 | @Override 57 | Iterator iterator(); 58 | default Stream stream() { 59 | return StreamSupport.stream(spliterator(), false); 60 | } 61 | 62 | Table filter(Predicate filter); 63 | Table sort(Comparator comparator); 64 | TableJoiner join(TableRow row); 65 | 66 | default T to(final Class type) { 67 | return to(type, type); 68 | } 69 | 70 | default T to(final Class type, final Type genericType) { 71 | return to(type, genericType, SpockitoValueConverter.DEFAULT_INSTANCE); 72 | } 73 | 74 | default T to(final Class type, final Type genericType, final ValueConverter valueConverter) { 75 | final TableConverter tableConverter = new SpockitoTableConverter(type, genericType, valueConverter); 76 | return type.cast(tableConverter.convert(this)); 77 | } 78 | 79 | default List toRowList() { 80 | return stream().collect(Collectors.toList()); 81 | } 82 | 83 | default List toList() { 84 | return stream().map(TableRow::toArray).collect(Collectors.toList()); 85 | } 86 | 87 | default List toList(final Class rowType) { 88 | return toList(rowType, SpockitoValueConverter.DEFAULT_INSTANCE); 89 | } 90 | 91 | default List toList(final Class rowType, final ValueConverter valueConverter) { 92 | requireNonNull(rowType); 93 | requireNonNull(valueConverter); 94 | return stream().map(row -> row.to(rowType, valueConverter)).collect(Collectors.toList()); 95 | } 96 | default List toList(final Class rowType, final Type genericType, final ValueConverter valueConverter) { 97 | requireNonNull(rowType); 98 | requireNonNull(genericType); 99 | requireNonNull(valueConverter); 100 | return stream().map(row -> row.to(rowType, genericType, valueConverter)).collect(Collectors.toList()); 101 | } 102 | 103 | static Table parse(final String[] headerAndRows) { 104 | return SpockitoTable.parse(headerAndRows); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/SpockitoBeforeAfterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.After; 27 | import org.junit.AfterClass; 28 | import org.junit.Assert; 29 | import org.junit.Before; 30 | import org.junit.BeforeClass; 31 | import org.junit.Rule; 32 | import org.junit.Test; 33 | import org.junit.rules.TestName; 34 | import org.tools4j.spockito.Spockito.Unroll; 35 | 36 | import java.util.ArrayList; 37 | import java.util.Arrays; 38 | import java.util.Collections; 39 | import java.util.List; 40 | 41 | import static org.junit.Assert.assertEquals; 42 | 43 | public class SpockitoBeforeAfterTest extends UnrollMethodDataTest { 44 | 45 | private static final List BEFORE_TESTS = new ArrayList<>(); 46 | private static final List AFTER_TESTS = new ArrayList<>(); 47 | private static final List EXPECTED_TESTS = Arrays.asList( 48 | "oneMore", 49 | "oneMoreUnroll[0]", "oneMoreUnroll[1]", 50 | "testUnrollAngularSums[Pentagon]", "testUnrollAngularSums[Square]", "testUnrollAngularSums[Triangle]", 51 | "testUnrollBirthdays[0]", "testUnrollBirthdays[1]", 52 | "testUnrollNameAndYearOnly[0]", "testUnrollNameAndYearOnly[1]", 53 | "testUnrollNames[0]", "testUnrollNames[1]" 54 | ); 55 | 56 | @Rule 57 | public final TestName testName = new TestName(); 58 | 59 | private String testMethodNameWithIndex() { 60 | final String methodName = testName.getMethodName(); 61 | final int index = methodName == null ? -1 : methodName.indexOf(']'); 62 | return index < 0 ? methodName : methodName.substring(0, index + 1); 63 | } 64 | 65 | @BeforeClass 66 | public static void beforeClass() { 67 | System.out.println("before-class"); 68 | } 69 | 70 | @AfterClass 71 | public static void afterClass() { 72 | System.out.println("after-class"); 73 | final List exp = new ArrayList<>(EXPECTED_TESTS); 74 | final List bef = new ArrayList<>(BEFORE_TESTS); 75 | final List aft = new ArrayList<>(AFTER_TESTS); 76 | Collections.sort(exp); 77 | Collections.sort(bef); 78 | Collections.sort(aft); 79 | assertEquals("Unexpected before test invocations", exp, bef); 80 | assertEquals("Unexpected after test invocations", exp, aft); 81 | } 82 | 83 | @Before 84 | public void beforeEach() { 85 | System.out.println("before-each: " + testName.getMethodName()); 86 | BEFORE_TESTS.add(testMethodNameWithIndex()); 87 | } 88 | 89 | @After 90 | public void afterEach() { 91 | System.out.println("after-each: " + testName.getMethodName()); 92 | AFTER_TESTS.add(testMethodNameWithIndex()); 93 | } 94 | 95 | @Test 96 | public void oneMore() { 97 | Assert.assertTrue("hello world one more time", true); 98 | } 99 | 100 | @Test 101 | @Unroll({ 102 | "| Name |", 103 | "| Test 1 |", 104 | "| Test 2 |" 105 | }) 106 | public void oneMoreUnroll(final String name) { 107 | Assert.assertNotNull("name should not be null", name); 108 | Assert.assertTrue("name should start with Test", name.startsWith("Test ")); 109 | } 110 | } -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/CustomConverterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | import org.tools4j.spockito.table.ValueConverter; 30 | 31 | import java.lang.reflect.Type; 32 | 33 | @RunWith(Spockito.class) 34 | @Spockito.UseValueConverter(CustomConverterTest.MyIntegerConverter.class) 35 | public class CustomConverterTest { 36 | 37 | static class MyInteger { 38 | int intValue; 39 | static MyInteger parse(final String s) { 40 | final int intValue = Integer.parseInt(s.trim()); 41 | final MyInteger myInteger = new MyInteger(); 42 | myInteger.intValue = intValue; 43 | return myInteger; 44 | } 45 | } 46 | static class Point { 47 | int x; 48 | int y; 49 | static Point parse(final String s) { 50 | final String trimmed = s.trim(); 51 | final int comma = trimmed.indexOf(','); 52 | if (comma >= 0 && trimmed.startsWith("(") && trimmed.endsWith(")")) { 53 | final int x = Integer.parseInt(trimmed.substring(1, comma)); 54 | final int y = Integer.parseInt(trimmed.substring(comma + 1, trimmed.length() - 1)); 55 | final Point p = new Point(); 56 | p.x = x; 57 | p.y = y; 58 | return p; 59 | } 60 | throw new IllegalArgumentException("Cannot convert string intValue to Point: " + s); 61 | } 62 | } 63 | 64 | static class MyIntegerConverter implements ValueConverter { 65 | @Override 66 | public T convert(final Class type, final Type genericType, final String value) { 67 | if (type == MyInteger.class) { 68 | return type.cast(MyInteger.parse(value)); 69 | } 70 | throw new IllegalArgumentException("Conversion not supported for type: " + type); 71 | } 72 | } 73 | 74 | static class PointConverter implements ValueConverter { 75 | @Override 76 | public T convert(final Class type, final Type genericType, final String value) { 77 | if (type == Point.class) { 78 | return type.cast(Point.parse(value)); 79 | } 80 | throw new IllegalArgumentException("Conversion not supported for type: " + type); 81 | } 82 | } 83 | 84 | @Test 85 | @Spockito.Unroll({ 86 | "| MyInteger |", 87 | "|-----------|", 88 | "| 1 |", 89 | "| 2 |" 90 | }) 91 | @Spockito.Name("[{row}]: myInteger={0}") 92 | public void testConverterOnClassLevel(final MyInteger myInteger) { 93 | Assert.assertNotNull("myInteger should not be null", myInteger); 94 | Assert.assertTrue("myInteger.inValue should be greater than 0", myInteger.intValue > 0); 95 | } 96 | 97 | @Test 98 | @Spockito.Unroll({ 99 | "| Point |", 100 | "|-------|", 101 | "| (1,2) |", 102 | "| (3,4) |" 103 | }) 104 | @Spockito.Name("[{row}]: point={0}") 105 | @Spockito.UseValueConverter(CustomConverterTest.PointConverter.class) 106 | public void testConverterOnMethodLevel(final Point point) { 107 | Assert.assertNotNull("point should not be null", point); 108 | Assert.assertTrue("point.x should be greater than 0", point.x > 0); 109 | Assert.assertTrue("point.y should be greater than 0", point.y > 0); 110 | } 111 | } -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoTableConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import org.tools4j.spockito.table.Converters.CollectionConverter; 27 | import org.tools4j.spockito.table.GenericTypes.ActualType; 28 | 29 | import java.lang.reflect.Array; 30 | import java.lang.reflect.Field; 31 | import java.lang.reflect.Parameter; 32 | import java.lang.reflect.Type; 33 | import java.util.Collection; 34 | import java.util.List; 35 | 36 | import static java.util.Objects.requireNonNull; 37 | import static org.tools4j.spockito.table.GenericTypes.actualTypeForTypeParam; 38 | import static org.tools4j.spockito.table.GenericTypes.genericComponentType; 39 | 40 | public class SpockitoTableConverter implements TableConverter { 41 | 42 | private final Class targetClass; 43 | private final Type targetType; 44 | private final ValueConverter valueConverter; 45 | 46 | @SuppressWarnings("unused") 47 | public SpockitoTableConverter(final Class targetClass, final Type targetType) { 48 | this(targetClass, targetType, SpockitoValueConverter.DEFAULT_INSTANCE); 49 | } 50 | 51 | public SpockitoTableConverter(final Class targetClass, final Type targetType, final ValueConverter valueConverter) { 52 | this.targetClass = requireNonNull(targetClass); 53 | this.targetType = requireNonNull(targetType); 54 | this.valueConverter = requireNonNull(valueConverter); 55 | } 56 | 57 | public static TableConverter create(final Parameter parameter, final ValueConverter valueConverter) { 58 | return new SpockitoTableConverter(parameter.getType(), parameter.getParameterizedType(), valueConverter); 59 | } 60 | 61 | public static TableConverter create(final Field field, final ValueConverter valueConverter) { 62 | return new SpockitoTableConverter(field.getType(), field.getGenericType(), valueConverter); 63 | } 64 | 65 | @Override 66 | public Object convert(final Table table) { 67 | requireNonNull(table); 68 | if (targetClass.isInstance(table)) { 69 | return table; 70 | } 71 | final ActualType rowType; 72 | if (Collection.class.isAssignableFrom(targetClass)) { 73 | rowType = actualTypeForTypeParam(targetType, 0, 1); 74 | } else if (targetClass.isArray()) { 75 | rowType = genericComponentType(targetClass, targetType); 76 | } else { 77 | throw new IllegalArgumentException("No known conversion from Table to " + targetType); 78 | } 79 | final List rows = table.toList(rowType.rawType(), rowType.genericType(), valueConverter); 80 | if (List.class.isAssignableFrom(targetClass)) { 81 | return targetClass.cast(rows); 82 | } 83 | if (Collection.class.isAssignableFrom(targetClass)) { 84 | return new CollectionConverter(valueConverter).convert(targetClass, targetType, rows); 85 | } 86 | if (targetClass.isArray()) { 87 | return toArray(rows, rowType.rawType()); 88 | } 89 | //should not get here 90 | throw new IllegalArgumentException("No known conversion from Table to " + targetType); 91 | } 92 | 93 | private static Object toArray(final List rows, final Class componentType) { 94 | final int n = rows.size(); 95 | final Object array = Array.newInstance(componentType, n); 96 | for (int i = 0; i < n; i++) { 97 | final Object val = rows.get(i); 98 | Array.set(array, i, val); 99 | } 100 | return array; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /spockito-table/src/test/java/org/tools4j/spockito/table/TableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import org.junit.jupiter.api.Test; 27 | 28 | import java.util.Comparator; 29 | import java.util.List; 30 | 31 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 32 | import static org.junit.jupiter.api.Assertions.assertEquals; 33 | 34 | /** 35 | * Unit test for {@link Table} 36 | */ 37 | public class TableTest { 38 | 39 | static final class Row { 40 | int index; 41 | List listOfInteger; 42 | String emptyString; 43 | } 44 | 45 | @Test 46 | public void parseAndAssert() { 47 | //when 48 | final Table table = Table.parse(new String[] { 49 | "| index | listOfInteger | emptyString |", 50 | "| 0 | [1;2;3;4;5;6] | |", 51 | "| 1 | [9;8;7;6;5;4] | '' |" 52 | }); 53 | 54 | //then 55 | assertEquals(2, table.getRowCount(), "unexpected row count"); 56 | assertEquals(3, table.getColumnCount(), "unexpected column count"); 57 | assertEquals("0", table.getValue(0, 0), "value[0][0]"); 58 | assertEquals("0", table.getValue(0, "index"), "value[0]['index']"); 59 | assertEquals("1", table.getValue(1, "index"), "value[1]['index']"); 60 | assertEquals("", table.getValue(0, "emptyString"), "value[1]['index']"); 61 | assertEquals("", table.getValue(1, "emptyString"), "value[1]['index']"); 62 | assertEquals(table.getRow(0), table.toRowList().get(0)); 63 | assertEquals(table.getRow(1), table.toRowList().get(1)); 64 | assertArrayEquals(new String[] {"0", "[1;2;3;4;5;6]", ""}, table.toList().get(0)); 65 | assertArrayEquals(new String[] {"1", "[9;8;7;6;5;4]", ""}, table.toList().get(1)); 66 | 67 | //when 68 | final List rows = table.toList(Row.class); 69 | 70 | //then 71 | assertEquals(2, rows.size(), "unexpected row count"); 72 | for (int row = 0; row < rows.size(); row++) { 73 | assertEquals(row, rows.get(row).index, "unexpected index"); 74 | assertEquals(6, rows.get(row).listOfInteger.size(), "unexpected list size"); 75 | assertEquals("", rows.get(row).emptyString, "string should be empty"); 76 | } 77 | } 78 | 79 | @Test 80 | public void filterAndSort() { 81 | //given 82 | final Table table = Table.parse(new String[]{ 83 | "| index | listOfInteger | emptyString |", 84 | "| 0 | [1;1;2;2;3;3] | |", 85 | "| 1 | [3;3;4;4;5;5] | '' |", 86 | "| 2 | [4;4;4;5;5;5] | '' |", 87 | "| 3 | [5;5;5;6;7;8] | '' |" 88 | }); 89 | 90 | //when 91 | final Table filtered = table.filter(row -> row.to(Row.class).listOfInteger.contains(5)); 92 | 93 | //then 94 | assertEquals(3, filtered.getRowCount()); 95 | assertEquals(1, filtered.getRow(0).to(Row.class).index); 96 | assertEquals(2, filtered.getRow(1).to(Row.class).index); 97 | assertEquals(3, filtered.getRow(2).to(Row.class).index); 98 | 99 | //when 100 | final Table reversed = filtered.sort(Comparator.comparing(row -> row.get("listOfInteger")).reversed()); 101 | 102 | //then 103 | assertEquals(3, reversed.getRowCount()); 104 | assertEquals(3, reversed.getRow(0).to(Row.class).index); 105 | assertEquals(2, reversed.getRow(1).to(Row.class).index); 106 | assertEquals(1, reversed.getRow(2).to(Row.class).index); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/InjectionContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.annotation.Annotation; 27 | import java.lang.reflect.AnnotatedElement; 28 | 29 | import static java.util.Objects.requireNonNull; 30 | import static org.tools4j.spockito.table.SpockitoAnnotations.annotationDirectOrMeta; 31 | 32 | /** 33 | * Context passed to {@link DataProvider#provideData(InjectionContext)} in the process of initialising the 34 | * {@linkplain #annotatedElement() annotated element}. 35 | */ 36 | public interface InjectionContext { 37 | /** 38 | * The phase during which the injection occurs. 39 | */ 40 | enum Phase { 41 | /** 42 | * Initialisation phase if injection is triggered at initialisation time, for instance when using the Junit-5 43 | * {@code SpockitoExtension}, when using {@link SpockitoData} or when directly invoking 44 | * {@link SpockitoAnnotations#initData(Object)}. 45 | */ 46 | INIT, 47 | /** 48 | * Test phase if injection is triggered via a test framework such as junit when invoking the test. 49 | */ 50 | TEST 51 | } 52 | 53 | /** 54 | * The phase in which the injection occurs 55 | * @return the injection phase 56 | */ 57 | Phase phase(); 58 | 59 | /** 60 | * The annotated element which is in process of being injected with a value. 61 | * @return the injection target annotated with the value to inject 62 | */ 63 | AnnotatedElement annotatedElement(); 64 | 65 | /** 66 | * Static factory method for injection context. 67 | * 68 | * @param phase the injecting phase 69 | * @param annotatedElement the annotated element 70 | * @return a new injection context instance for the provided arguments 71 | * @throws NullPointerException if any of the arguments is null 72 | */ 73 | static InjectionContext create(final Phase phase, final AnnotatedElement annotatedElement) { 74 | requireNonNull(phase); 75 | requireNonNull(annotatedElement); 76 | return new InjectionContext() { 77 | @Override 78 | public Phase phase() { 79 | return phase; 80 | } 81 | 82 | @Override 83 | public AnnotatedElement annotatedElement() { 84 | return annotatedElement; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return "InjectionContext{phase=" + phase + ", annotatedElement=" + annotatedElement + "}"; 90 | } 91 | }; 92 | } 93 | 94 | /** 95 | * Creates a sub-context of this context for the provided element using the same phase as this context, but only 96 | * if the element has annotated with an annotation of the provided type (via direct or meta); otherwise null is 97 | * returned. 98 | * 99 | * @param element the element for which to return a sub-context 100 | * @param annotationClass the type of annotation to look for on the provided element 101 | * @return a sub-context if element has the required annotation, and null otherwise 102 | */ 103 | default InjectionContext createSubContextOrNull(final AnnotatedElement element, 104 | final Class annotationClass) { 105 | final Annotation annotation = annotationDirectOrMeta(element, annotationClass); 106 | if (annotation != null) { 107 | return InjectionContext.create(phase(), element); 108 | } 109 | return null; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /spockito-junit4/src/main/java/org/tools4j/spockito/SingleTestMultiRowRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Test; 27 | import org.junit.runners.model.FrameworkMethod; 28 | import org.junit.runners.model.InitializationError; 29 | import org.tools4j.spockito.Spockito.Unroll; 30 | import org.tools4j.spockito.table.Table; 31 | import org.tools4j.spockito.table.TableRow; 32 | import org.tools4j.spockito.table.ValueConverter; 33 | 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | import java.util.Objects; 37 | 38 | /** 39 | * A runner for the situation where a test method is to be run multiple times with all the rows of an unroll table. 40 | * This case applies if the {@link Unroll} annotation is present at test method level. 41 | */ 42 | public class SingleTestMultiRowRunner extends AbstractSpockitoTestRunner { 43 | 44 | private final FrameworkMethod testMethod; 45 | private final ValueConverter methodValueConverter; 46 | 47 | public SingleTestMultiRowRunner(final Class clazz, 48 | final FrameworkMethod testMethod, 49 | final ValueConverter methodValueConverter) throws InitializationError { 50 | super(clazz); 51 | this.testMethod = Objects.requireNonNull(testMethod); 52 | this.methodValueConverter = Objects.requireNonNull(methodValueConverter); 53 | validate(); 54 | } 55 | 56 | @Override 57 | protected String getName() { 58 | return testMethod.getName(); 59 | } 60 | 61 | @Override 62 | protected String testName(final FrameworkMethod method) { 63 | if (method instanceof UnrolledTestMethod) { 64 | return method.getName() + Spockito.getName(method.getMethod(), ((UnrolledTestMethod)method).getTableRow()); 65 | } 66 | return super.testName(method); 67 | } 68 | 69 | @Override 70 | protected List computeTestMethods() { 71 | final List testMethods = new ArrayList<>(); 72 | final Spockito.Unroll unroll = testMethod.getAnnotation(Spockito.Unroll.class); 73 | if (unroll == null) { 74 | testMethods.add(testMethod); 75 | } else { 76 | final Table table = Table.parse(unroll.value()); 77 | testMethods.addAll(unroll(table)); 78 | } 79 | return testMethods; 80 | } 81 | 82 | private List unroll(final Table table) { 83 | final List unrolled = new ArrayList<>(table.getRowCount()); 84 | for (final TableRow row : table) { 85 | final UnrolledTestMethod unrolledTestMethod = new UnrolledTestMethod(testMethod.getMethod(), row, methodValueConverter); 86 | unrolled.add(unrolledTestMethod); 87 | } 88 | return unrolled; 89 | } 90 | 91 | protected void validate() throws InitializationError { 92 | final List errors = new ArrayList<>(); 93 | try { 94 | super.collectInitializationErrors(errors); 95 | } catch (final Exception e) { 96 | errors.add(e); 97 | } 98 | if (!errors.isEmpty()) { 99 | throw new InitializationError(testMethod + ": " + errors.get(0)); 100 | } 101 | } 102 | 103 | @Override 104 | protected void validateTestMethods(final List errors) { 105 | final List methods = getTestClass().getAnnotatedMethods(Test.class); 106 | for (final FrameworkMethod method : methods) { 107 | final Spockito.Unroll unroll = method.getAnnotation(Spockito.Unroll.class); 108 | if (unroll == null) { 109 | method.validatePublicVoidNoArg(false, errors); 110 | } else { 111 | method.validatePublicVoid(false, errors); 112 | method.validateNoTypeParametersOnArgs(errors); 113 | } 114 | } 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Continuous Integration](https://github.com/tools4j/spockito/workflows/Continuous%20Integration/badge.svg)](https://github.com/tools4j/spockito/actions?query=workflow%3A%22Continuous+Integration%22) 2 | [![Maven Central](https://img.shields.io/maven-central/v/org.tools4j/spockito-junit5.svg)](https://maven.org/search?q=spockito) 3 | [![Javadocs](http://www.javadoc.io/badge/org.tools4j/spockito-junit5.svg)](http://www.javadoc.io/doc/org.tools4j/spockito-junit5) 4 | # spockito 5 | Simple Java library to define data in a table-like manner. The library also provides a Junit 5 6 | [@TableSource](https://github.com/tools4j/spockito/blob/master/spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableSource.java) 7 | annotation to define arguments for parameterized tests in a simple table structure. The 8 | [SpockitoExtension](https://github.com/tools4j/spockito/blob/master/spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/SpockitoExtension.java) 9 | can be used to automatically propagate fields in a test class with table data. 10 | 11 | We also support parameterized test data in table format for Junit 4 tests through the classic 12 | [Spockito](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/main/java/org/tools4j/spockito/Spockito.java) 13 | test runner (see [here](https://github.com/tools4j/spockito/blob/master/spockito-junit4/README.md) for more information and 14 | examples for spockito with Junit 4). 15 | 16 | ### Parameterized tests with @TableSource 17 | 18 | Arguments for parameterized tests can be defined via ``@TableSource`` annotation as follows: 19 | 20 | ```java 21 | public class TableSourceTest { 22 | 23 | @TableSource({ 24 | "| Last Name | First Name |", 25 | "| Jones | David |", 26 | "| Jensen | Astrid |" 27 | }) 28 | @ParameterizedTest(name = "[{index}] {1} {0}") 29 | public void testUnrollNames(String lastName, String firstName) { 30 | assertTrue(lastName.startsWith("J"), "Last Name should start with J"); 31 | assertTrue(firstName.endsWith("id"), "First Name should end with id"); 32 | } 33 | 34 | @TableSource({ 35 | "| Name | Year | Birthday |", 36 | "|-------|------|------------|", 37 | "| Henry | 1981 | 1981-11-28 |", 38 | "| Jessy | 1965 | 1965-03-28 |" 39 | }) 40 | @ParameterizedTest(name = "[{index}] {0}") 41 | public void testUnrollBirthdays(String name, int year, LocalDate birthday) { 42 | assertEquals(5, name.length(), "Name should have 5 characters"); 43 | assertTrue(1990 > year, "Year is before 1990"); 44 | assertEquals(28, birthday.getDayOfMonth(), "Day is 28th"); 45 | assertEquals(year, birthday.getYear(), "Year is consistent with birthday"); 46 | } 47 | 48 | @TableSource({ 49 | "| Name | Year | Birthday |", 50 | "|-------|------|------------|", 51 | "| Henry | 1981 | 1981-11-28 |", 52 | "| Jessy | 1965 | 1965-03-28 |" 53 | }) 54 | @ParameterizedTest(name = "[{index}] {0}") 55 | public void testUnrollNameAndYearOnly(String name, int year) { 56 | assertEquals(5, name.length(), "Name should have 5 characters"); 57 | assertTrue(1990 > year, "Year is before 1990"); 58 | } 59 | 60 | @TableSource({ 61 | "| Object | Vertices | Angle sum |", 62 | "|==========|==========|===========|", 63 | "| Triangle | 3 | 180 |", 64 | "| Square | 4 | 360 |", 65 | "| Pentagon | 5 | 540 |", 66 | "|----------|----------|-----------|", 67 | }) 68 | @ParameterizedTest(name = "{2}: ({0}-2)*180 = {1}") 69 | public void testUnrollAngularSums(@Column("Vertices") int n, 70 | @Column("Angle sum") int degrees, 71 | @Column("Object") String name) { 72 | assertTrue(3 <= n, "There should be 3 or more vertices"); 73 | assertEquals(degrees, (n-2)*180, "Angular sum is wrong for: " + name); 74 | } 75 | } 76 | ``` 77 | This and other examples can be found [here](https://github.com/tools4j/spockito/blob/master/spockito-junit5/src/test/java/org/tools4j/spockito/jupiter). 78 | 79 | #### Run above test in IDE (here: IntelliJ) 80 | ![spockito-junit5-idea-testrun.png](https://github.com/tools4j/spockito/blob/master/spockito-junit5-idea-testrun.png) 81 | 82 | This and other examples can be found [here](https://github.com/tools4j/spockito/blob/master/spockito-junit5/src/test/java/org/tools4j/spockito/jupiter). 83 | 84 | ### Maven 85 | Add the following dependency to your maven pom.xml file: 86 | 87 | ```xml 88 | 89 | org.tools4j 90 | spockito-junit5 91 | 2.0 92 | test 93 | 94 | ``` 95 | 96 | ### Download 97 | Sources and binaries can be downloaded from maven central: 98 | * [spockito](https://maven.org/search?q=spockito) in Maven Central 99 | 100 | ### FAQ 101 | * [Frequently asked Questions](https://github.com/tools4j/spockito/issues?q=label:question) 102 | * [Bugs and Issues](https://github.com/tools4j/spockito/issues?q=label:bug) 103 | 104 | ### More Information 105 | * [MIT License](https://github.com/tools4j/spockito/blob/master/LICENSE.md) 106 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/Strings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.util.Arrays; 27 | import java.util.regex.Pattern; 28 | 29 | /** 30 | * Contains static utility methods dealing with strings. 31 | */ 32 | enum Strings { 33 | ; 34 | static final String[] EMPTY_STRING_ARRAY = new String[0]; 35 | static final Pattern UNESCAPED_COMMA = Pattern.compile("(?<=[^\\\\]),"); 36 | static final Pattern UNESCAPED_SEMICOLON = Pattern.compile("(?<=[^\\\\]);"); 37 | static final Pattern UNESCAPED_COLON = Pattern.compile("(?<=[^\\\\]):"); 38 | static final Pattern UNESCAPED_EQUAL = Pattern.compile("(?<=[^\\\\])="); 39 | static final Pattern UNESCAPED_PIPE = Pattern.compile("(?<=[^\\\\])\\|"); 40 | 41 | static String firstCharToUpperCase(final String value) { 42 | return value.length() > 0 ? Character.toUpperCase(value.charAt(0)) + value.substring(1) : value; 43 | } 44 | 45 | static String removeSurroundingPipes(final String s) { 46 | return removeStartAndEndChars(s, '|', '|'); 47 | } 48 | 49 | static String removeStartAndEndChars(final String s, final char startQuoteChar, final char endQuoteChar) { 50 | final int len = s.length(); 51 | if (len >= 2 && s.charAt(0) == startQuoteChar && s.charAt(len - 1) == endQuoteChar) { 52 | return s.substring(1, len - 1); 53 | } 54 | return s; 55 | } 56 | 57 | @SuppressWarnings("SameParameterValue") 58 | static boolean allCharsMatchingAnyOf(final String s, final char ch1, final char ch2) { 59 | final int len = s.length(); 60 | for (int i = 0; i < len; i++) { 61 | if (s.charAt(i) != ch1 && s.charAt(i) != ch2) { 62 | return false; 63 | } 64 | } 65 | return true; 66 | } 67 | 68 | static String unescape(final String s) { 69 | return unescape(s, ',', ';', '|', '=', '\\','\''); 70 | } 71 | 72 | static String unescape(final String s, final char... escapedChars) { 73 | int index = s.indexOf('\\'); 74 | if (index < 0) { 75 | return s; 76 | } 77 | final StringBuilder sb = new StringBuilder(s); 78 | while (index >= 0 && index + 1 < sb.length()) { 79 | final char ch = sb.charAt(index + 1); 80 | if (isCharAnyOf(ch, escapedChars)) { 81 | sb.delete(index, index + 1); 82 | index = sb.indexOf("\\", index + 1); 83 | } else { 84 | index = sb.indexOf("\\", index + 2); 85 | } 86 | } 87 | return s.length() == sb.length() ? s : sb.toString(); 88 | } 89 | 90 | static boolean isCharAnyOf(final char ch, final char... chars) { 91 | for (final char c : chars) { 92 | if (ch == c) { 93 | return true; 94 | } 95 | } 96 | return false; 97 | } 98 | 99 | static String[] split(final String s, final Pattern delimRegex1, final Pattern delimRegex2) { 100 | String[] parts = delimRegex1.split(s); 101 | if (parts.length == 1 && s.equals(parts[0])) { 102 | //delimiter was not contained in string, try second delimiter 103 | parts = delimRegex2.split(s); 104 | if (parts.length == 1 && s.equals(parts[0])) { 105 | //delimiter was not contained in string, we're done 106 | return parts; 107 | } 108 | } 109 | if (!s.startsWith(parts[0])) { 110 | //must be delimiter at the start of the string, meaning we have an empty string at the start 111 | final String[] newParts = new String[parts.length + 1]; 112 | System.arraycopy(parts, 0, newParts, 1, parts.length); 113 | newParts[0] = ""; 114 | parts = newParts; 115 | } 116 | if (!s.endsWith(parts[parts.length - 1])) { 117 | //must be delimiter at the end of the string, meaning we have an empty string at the end 118 | parts = Arrays.copyOf(parts, parts.length + 1); 119 | parts[parts.length - 1] = ""; 120 | } 121 | return parts; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/TableDataProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.AnnotatedElement; 27 | import java.lang.reflect.Field; 28 | import java.lang.reflect.Method; 29 | import java.lang.reflect.Parameter; 30 | import java.util.function.Function; 31 | 32 | import static java.util.Objects.requireNonNull; 33 | 34 | /** 35 | * Provides values defined by a {@link TableData @TableData} annotation. 36 | */ 37 | public class TableDataProvider implements DataProvider { 38 | 39 | private final Function tableFactory; 40 | private final Function> valueConverterTypeLookup; 41 | 42 | @SuppressWarnings("unused") //used by @TableData 43 | public TableDataProvider() { 44 | this(TableDataProvider::table, TableDataProvider::valueConverter); 45 | } 46 | 47 | public TableDataProvider(final Function tableFactory, 48 | final Function> valueConverterTypeLookup) { 49 | this.tableFactory = requireNonNull(tableFactory); 50 | this.valueConverterTypeLookup = requireNonNull(valueConverterTypeLookup); 51 | } 52 | 53 | private static Table table(final InjectionContext injectionContext) { 54 | final TableData tableData = injectionContext.annotatedElement().getAnnotation(TableData.class); 55 | return Table.parse(tableData.value()); 56 | } 57 | 58 | private static Class valueConverter(final InjectionContext injectionContext) { 59 | final TableData tableData = injectionContext.annotatedElement().getAnnotation(TableData.class); 60 | return tableData.valueConverter(); 61 | } 62 | 63 | public Table provideTable(final InjectionContext context) { 64 | return tableFactory.apply(context); 65 | } 66 | 67 | public ValueConverter provideValueConverter(final InjectionContext context) { 68 | return ValueConverter.create(valueConverterTypeLookup.apply(context)); 69 | } 70 | 71 | @Override 72 | public Object provideData(final InjectionContext context) { 73 | return provideData(context, provideTable(context), provideValueConverter(context)); 74 | } 75 | 76 | public Object provideData(final InjectionContext context, final Table table, final ValueConverter valueConverter) { 77 | final AnnotatedElement element = context.annotatedElement(); 78 | if (element instanceof Field) { 79 | final Field field = (Field)element; 80 | return table.to(field.getType(), field.getGenericType(), valueConverter); 81 | } else if (element instanceof Method) { 82 | final Method method = (Method)element; 83 | final Parameter[] parameters = method.getParameters(); 84 | final Object[][] values = new Object[table.getRowCount()][parameters.length]; 85 | for (int i = 0; i < table.getRowCount(); i++) { 86 | final TableRow row = table.getRow(i); 87 | for (int j = 0; j < parameters.length; j++) { 88 | values[i][j] = tableRowConverter(context, parameters[j], j, valueConverter).convert(row); 89 | } 90 | } 91 | return values; 92 | } else if (element instanceof Parameter) { 93 | final Parameter parameter = (Parameter)element; 94 | return table.to(parameter.getType(), parameter.getParameterizedType(), valueConverter); 95 | } 96 | throw new SpockitoException("Annotated element is not supported: " + element); 97 | } 98 | 99 | protected TableRowConverter tableRowConverter(final InjectionContext context, 100 | final Parameter parameter, 101 | final int index, 102 | final ValueConverter valueConverter) { 103 | return SpockitoTableRowConverter.create(context, parameter, index, valueConverter); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoTableJoiner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.LinkedHashSet; 29 | import java.util.List; 30 | import java.util.function.Predicate; 31 | 32 | import static java.util.Objects.requireNonNull; 33 | 34 | public class SpockitoTableJoiner implements TableJoiner { 35 | private final Table child; 36 | private final TableRow parent; 37 | 38 | public SpockitoTableJoiner(final Table child, final TableRow parent) { 39 | this.child = requireNonNull(child); 40 | this.parent = requireNonNull(parent); 41 | } 42 | 43 | @Override 44 | public JoinBuilder onAllCommonColumns() { 45 | final LinkedHashSet common = new LinkedHashSet<>(parent.getTable().getColumnNames()); 46 | common.retainAll(child.getColumnNames()); 47 | JoinBuilder result = null; 48 | for (final String name : common) { 49 | result = result == null ? on(name, name) : result.and(name, name); 50 | } 51 | if (result != null) { 52 | return result; 53 | } 54 | throw new IllegalStateException("No common columns found in parent and child table: " + 55 | parent.getTable().getColumnNames() + " / " + child.getColumnNames()); 56 | } 57 | 58 | @Override 59 | public JoinBuilder on(final int child, final int parent) { 60 | return new Builder().and(child, parent); 61 | } 62 | 63 | @Override 64 | public JoinBuilder on(final String child, final String parent) { 65 | return new Builder().and(child, parent); 66 | } 67 | 68 | @Override 69 | public JoinBuilder on(final String common) { 70 | return on(common, common); 71 | } 72 | 73 | private class Builder implements JoinBuilder { 74 | 75 | final List children = new ArrayList<>(); 76 | final List parents = new ArrayList<>(); 77 | 78 | @Override 79 | public JoinBuilder and(final int childColumn, final int parentColumn) { 80 | if (childColumn < 0 || childColumn >= child.getColumnCount()) { 81 | throw new IllegalArgumentException("Invalid child column index: " + childColumn); 82 | } 83 | if (parentColumn < 0 || parentColumn >= parent.getColumnCount()) { 84 | throw new IllegalArgumentException("Invalid parent column index: " + parentColumn); 85 | } 86 | children.add(child.getColumnName(childColumn)); 87 | parents.add(parent.getTable().getColumnName(parentColumn)); 88 | return this; 89 | } 90 | 91 | @Override 92 | public JoinBuilder and(final String childColumn, final String parentColumn) { 93 | if (!child.hasColumn(childColumn)) { 94 | throw new IllegalArgumentException("Invalid child column name: " + childColumn); 95 | } 96 | if (!parent.getTable().hasColumn(parentColumn)) { 97 | throw new IllegalArgumentException("Invalid parent column name: " + parentColumn); 98 | } 99 | children.add(childColumn); 100 | parents.add(parentColumn); 101 | return this; 102 | } 103 | 104 | @Override 105 | public JoinBuilder and(final String common) { 106 | return and(common, common); 107 | } 108 | 109 | @Override 110 | public Table apply() { 111 | final String[] parentValues = parents.stream().map(parent::get).toArray(String[]::new); 112 | final Predicate matcher = childRow -> { 113 | final String[] childValues = children.stream().map(childRow::get).toArray(String[]::new); 114 | return Arrays.equals(parentValues, childValues); 115 | }; 116 | final int childRows = child.getRowCount(); 117 | final List> rows = new ArrayList<>(); 118 | for (int i = 0; i < childRows; i++) { 119 | final TableRow row = child.getRow(i); 120 | if (matcher.test(row)) { 121 | rows.add(row.toList()); 122 | } 123 | } 124 | return new SpockitoTable(child.getColumnNames(), rows); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /spockito-junit4/src/main/java/org/tools4j/spockito/TableRowConverters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.tools4j.spockito.Spockito.Ref; 27 | import org.tools4j.spockito.table.Data; 28 | import org.tools4j.spockito.table.InjectionContext; 29 | import org.tools4j.spockito.table.InjectionContext.Phase; 30 | import org.tools4j.spockito.table.SpockitoTableRowConverter; 31 | import org.tools4j.spockito.table.TableRow; 32 | import org.tools4j.spockito.table.TableRowConverter; 33 | import org.tools4j.spockito.table.ValueConverter; 34 | 35 | import java.lang.reflect.AnnotatedElement; 36 | import java.lang.reflect.Executable; 37 | import java.lang.reflect.Field; 38 | import java.lang.reflect.Parameter; 39 | import java.lang.reflect.Type; 40 | 41 | import static org.tools4j.spockito.Spockito.fieldRefOrName; 42 | import static org.tools4j.spockito.Spockito.parameterRefOrNameOrNull; 43 | import static org.tools4j.spockito.table.SpockitoAnnotations.annotationDirectOrMeta; 44 | 45 | /** 46 | * Similar to the converters created by the factory methods in {@link TableRowConverter} but this time the parameters or 47 | * fields can be annotated to override the default name or index. 48 | */ 49 | enum TableRowConverters { 50 | ; 51 | static TableRowConverter create(final InjectionContext context, final Parameter parameter, final int index, final ValueConverter valueConverter) { 52 | final TableRowConverter special = specialRefConverterOrNull(parameter.getAnnotation(Ref.class), 53 | parameter.getType(), parameter.getParameterizedType(), valueConverter); 54 | if (special != null) { 55 | return special; 56 | } 57 | return new SpockitoTableRowConverter(dataSubContextOrNull(context, parameter), parameter, 58 | parameterRefOrNameOrNull(parameter), index, parameter.getType(), parameter.getParameterizedType(), 59 | valueConverter); 60 | } 61 | 62 | static TableRowConverter create(final Field field, final ValueConverter valueConverter) { 63 | final TableRowConverter special = specialRefConverterOrNull(field.getAnnotation(Ref.class), field.getType(), 64 | field.getGenericType(), valueConverter); 65 | if (special != null) { 66 | return special; 67 | } 68 | return new SpockitoTableRowConverter(null, field, fieldRefOrName(field), -1, field.getType(), 69 | field.getGenericType(), valueConverter); 70 | } 71 | 72 | private static TableRowConverter specialRefConverterOrNull(final Ref ref, final Class type, final Type genericType, final ValueConverter valueConverter) { 73 | if (ref != null) { 74 | if (Ref.ALL_COLUMNS.equals(ref.value())) { 75 | return tableRow -> valueConverter.convert(type, genericType, tableRow.toMap().toString()); 76 | } 77 | if (Ref.ROW_INDEX.equals(ref.value())) { 78 | return tableRow -> Integer.toString(tableRow.getRowIndex()); 79 | } 80 | } 81 | return null; 82 | } 83 | 84 | static Object[] convert(final TableRow tableRow, final Executable executable, final ValueConverter valueConverter) { 85 | final Parameter[] parameters = executable.getParameters(); 86 | final Object[] values = new Object[parameters.length]; 87 | for (int i = 0; i < values.length; i++) { 88 | values[i] = create(null, parameters[i], i, valueConverter).convert(tableRow); 89 | } 90 | return values; 91 | } 92 | 93 | static Object[] convert(final TableRow tableRow, final Field[] fields, final ValueConverter valueConverter) { 94 | final Object[] values = new Object[fields.length]; 95 | for (int i = 0; i < values.length; i++) { 96 | values[i] = create(fields[i], valueConverter).convert(tableRow); 97 | } 98 | return values; 99 | } 100 | 101 | private static InjectionContext dataSubContextOrNull(final InjectionContext context, 102 | final AnnotatedElement annotatedElement) { 103 | final Data data = annotationDirectOrMeta(annotatedElement, Data.class); 104 | if (data != null) { 105 | return InjectionContext.create(context == null ? Phase.TEST : context.phase(), annotatedElement); 106 | } 107 | return null; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/GenericTypes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.GenericArrayType; 27 | import java.lang.reflect.ParameterizedType; 28 | import java.lang.reflect.Type; 29 | import java.lang.reflect.WildcardType; 30 | import java.util.List; 31 | import java.util.Properties; 32 | 33 | import static java.util.Objects.requireNonNull; 34 | 35 | /** 36 | * Contains static helper methods to inspect and specify generic types. 37 | */ 38 | public enum GenericTypes { 39 | ; 40 | public interface ActualType { 41 | Class rawType(); 42 | Type genericType(); 43 | 44 | static ActualType create(final Class rawType, final Type genericType) { 45 | requireNonNull(rawType); 46 | requireNonNull(genericType); 47 | return new ActualType() { 48 | @Override 49 | public Class rawType() { 50 | return rawType; 51 | } 52 | 53 | @Override 54 | public Type genericType() { 55 | return genericType; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "ActualType{rawType=" + rawType + ", genericType=" + genericType + "}"; 61 | } 62 | }; 63 | } 64 | } 65 | 66 | public static ActualType actualTypeForTypeParam(final Type type, final int paramIndex, final int paramCount) { 67 | if (type instanceof ParameterizedType) { 68 | final Type[] actualTypeArgs = ((ParameterizedType) type).getActualTypeArguments(); 69 | if (actualTypeArgs.length == paramCount) { 70 | Type actualType = actualTypeArgs[paramIndex]; 71 | if (actualType instanceof WildcardType) { 72 | final Type[] bounds = ((WildcardType) actualType).getUpperBounds(); 73 | if (bounds.length == 1) { 74 | actualType = bounds[0]; 75 | } 76 | } 77 | if (actualType instanceof Class) { 78 | return ActualType.create((Class) actualType, actualType); 79 | } 80 | if (actualType instanceof ParameterizedType) { 81 | final ParameterizedType parameterizedType = (ParameterizedType)actualType; 82 | if (parameterizedType.getRawType() instanceof Class) { 83 | return ActualType.create((Class)parameterizedType.getRawType(), parameterizedType); 84 | } 85 | } 86 | } 87 | } 88 | if (Properties.class.equals(type) && paramCount == 2) { 89 | return ActualType.create(String.class, String.class); 90 | } 91 | throw new IllegalArgumentException("Could not derive actual generic type [" + paramIndex + "] for " + type); 92 | } 93 | 94 | public static ActualType genericComponentType(final Class arrayType, final Type genericType) { 95 | final Class componentType = arrayType.getComponentType(); 96 | if (componentType == null) { 97 | throw new IllegalArgumentException("Must be an array type: " + arrayType); 98 | } 99 | if (genericType instanceof GenericArrayType) { 100 | return ActualType.create(componentType, ((GenericArrayType)genericType).getGenericComponentType()); 101 | } 102 | return ActualType.create(componentType, componentType); 103 | } 104 | 105 | public static ParameterizedType genericListType(final Type listElementType) { 106 | requireNonNull(listElementType); 107 | return new ParameterizedType() { 108 | @Override 109 | public Type[] getActualTypeArguments() { 110 | return new Type[]{listElementType}; 111 | } 112 | @Override 113 | public Type getRawType() { 114 | return List.class; 115 | } 116 | @Override 117 | public Type getOwnerType() { 118 | return null; 119 | } 120 | @Override 121 | public String toString() { 122 | return List.class.getName() + "<" + listElementType + ">"; 123 | } 124 | }; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/FaqTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.AfterClass; 27 | import org.junit.Assert; 28 | import org.junit.BeforeClass; 29 | import org.junit.Test; 30 | import org.junit.runner.RunWith; 31 | 32 | import java.math.BigInteger; 33 | import java.util.concurrent.atomic.AtomicInteger; 34 | 35 | @RunWith(Spockito.class) 36 | public class FaqTest { 37 | 38 | private static final AtomicInteger SPECIAL_STRING_COUNT = new AtomicInteger(); 39 | 40 | @BeforeClass 41 | public static void initClass() { 42 | SPECIAL_STRING_COUNT.set(0); 43 | } 44 | 45 | @AfterClass 46 | public static void assertSpecialStringTestCount() { 47 | if (SPECIAL_STRING_COUNT.get() > 0) { 48 | Assert.assertEquals("Expected 4 runs of testSpecialStrings()", 4, SPECIAL_STRING_COUNT.get()); 49 | } 50 | } 51 | 52 | /** Example related to issue #1 */ 53 | @Test(expected = IllegalArgumentException.class) 54 | @Spockito.Unroll({ 55 | "|Input|", 56 | "||", 57 | "|''|", 58 | "|'|", 59 | "|' '|" 60 | }) 61 | @Spockito.Name("[row]: Input=<{Input}>") 62 | public void testSpecialStrings(String input) { 63 | SPECIAL_STRING_COUNT.incrementAndGet(); 64 | if (input.isEmpty()) throw new IllegalArgumentException("empty"); 65 | if (input.trim().isEmpty()) throw new IllegalArgumentException("spaces"); 66 | if (input.equals("'")) throw new IllegalArgumentException("single quote"); 67 | Assert.fail("should have thrown an exception"); 68 | } 69 | 70 | /** Example related to issue #2 */ 71 | @Test 72 | @Spockito.Unroll({ 73 | "|Input|Location ID|Event ID|", 74 | "|123\\|321|123|321|", 75 | "|123%7c321|123|321|" 76 | }) 77 | public void testWithPipe(String input, BigInteger locationId, BigInteger eventId) { 78 | Assert.assertTrue(!input.contains("\\")); 79 | Assert.assertTrue(input.contains("|") || input.startsWith("123") && input.endsWith("321")); 80 | } 81 | 82 | /** Example related to issue #6 */ 83 | @Test 84 | @Spockito.Unroll({ 85 | "|Character|", 86 | "|'\u0001'|", 87 | "|'\u0002'|", 88 | "|'\u0003'|", 89 | "|'\u0004'|", 90 | "|'\u0005'|", 91 | "|'\u0006'|", 92 | "|'\u0007'|", 93 | "|'\u0008'|", 94 | "|'\u0009'|", 95 | "|'\u000b'|", 96 | "|'\u000c'|", 97 | "|'\u000e'|", 98 | "|'\u000f'|", 99 | "|'\u0010'|", 100 | "|'\u0011'|", 101 | "|'\u0012'|", 102 | "|'\u0013'|", 103 | "|'\u0014'|", 104 | "|'\u0015'|", 105 | "|'\u0016'|", 106 | "|'\u0017'|", 107 | "|'\u0018'|", 108 | "|'\u0019'|", 109 | "|'\u001a'|", 110 | "|'\u001b'|", 111 | "|'\u001c'|", 112 | "|'\u001d'|", 113 | "|'\u001e'|", 114 | "|'\u001f'|", 115 | "|'\u0020'|" 116 | }) 117 | public void testNonPrintableOrWhitespaceChar(char ch) { 118 | Assert.assertTrue("Char should be non-printable or whitespace", ch <= '\u0021'); 119 | } 120 | 121 | /** Example related to issue #6 */ 122 | @Test 123 | @Spockito.Unroll({ 124 | "|Character|", 125 | "|'\u0001'|", 126 | "|'\u0002'|", 127 | "|'\u0003'|", 128 | "|'\u0004'|", 129 | "|'\u0005'|", 130 | "|'\u0006'|", 131 | "|'\u0007'|", 132 | "|'\u0008'|", 133 | "|'\u0009'|", 134 | "|'\u000b'|", 135 | "|'\u000c'|", 136 | "|'\u000e'|", 137 | "|'\u000f'|", 138 | "|'\u0010'|", 139 | "|'\u0011'|", 140 | "|'\u0012'|", 141 | "|'\u0013'|", 142 | "|'\u0014'|", 143 | "|'\u0015'|", 144 | "|'\u0016'|", 145 | "|'\u0017'|", 146 | "|'\u0018'|", 147 | "|'\u0019'|", 148 | "|'\u001a'|", 149 | "|'\u001b'|", 150 | "|'\u001c'|", 151 | "|'\u001d'|", 152 | "|'\u001e'|", 153 | "|'\u001f'|", 154 | "|'\u0020'|" 155 | }) 156 | public void testNonPrintableOrWhitespaceCharsAsString(String s) { 157 | Assert.assertEquals("String length should be 1", 1, s.length()); 158 | Assert.assertTrue("Char should be non-printable or whitespace", s.charAt(0) <= '\u0021'); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /spockito-junit4/src/test/java/org/tools4j/spockito/AdvancedDataTypesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito; 25 | 26 | import org.junit.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | @RunWith(Spockito.class) 34 | public class AdvancedDataTypesTest { 35 | 36 | public static class FieldBean { 37 | public int index; 38 | public String name; 39 | } 40 | 41 | public static class AccessorBean { 42 | private int index; 43 | private String name; 44 | public void setIndex(int index) { 45 | this.index = index; 46 | } 47 | 48 | private int getIndex() { 49 | return index; 50 | } 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | private String getName() { 56 | return name; 57 | } 58 | } 59 | 60 | @Test 61 | @Spockito.Unroll({ 62 | "| List | Count |", 63 | "| 1,2,3,4,5 | 5 |", 64 | "| 17,99,101 | 3 |" 65 | }) 66 | @Spockito.Name("[{row}]") 67 | public void testUnrollSingleColumnsIntoList(final List list, final int count) { 68 | Assert.assertNotNull("list should not be null", list); 69 | Assert.assertEquals("list has wrong number of elements", count, list.size()); 70 | } 71 | 72 | @Test 73 | @Spockito.Unroll({ 74 | "| Map | Count |", 75 | "| 1:blue, 2:green, 3: yellow | 3 |", 76 | "| 1:sky, 2:hello world | 2 |" 77 | }) 78 | @Spockito.Name("[{row}]") 79 | public void testUnrollSingleColumnsIntoMap(final Map map, final int count) { 80 | Assert.assertNotNull("map should not be null", map); 81 | Assert.assertEquals("map has wrong number of elements", count, map.size()); 82 | } 83 | 84 | @Test 85 | @Spockito.Unroll({ 86 | "| FieldBean | Name |", 87 | "| index:1,name:Test 1 | Test 1 |", 88 | "| index:2,name:Test 2 | Test 2 |", 89 | }) 90 | @Spockito.Name("[{row}]: {1}") 91 | public void testUnrollSingleColumnsIntoFieldBean(final FieldBean bean, final String testName) { 92 | Assert.assertNotNull("bean should not be null", bean); 93 | Assert.assertEquals("bean.name not as expected", testName, bean.name); 94 | } 95 | 96 | @Test 97 | @Spockito.Unroll({ 98 | "| Index | Name |", 99 | "| 1 | Test 1 |", 100 | "| 2 | Test 2 |" 101 | }) 102 | @Spockito.Name("[{row}]: {1}") 103 | public void testUnrollAllColumnsIntoFieldBean(final @Spockito.Ref("*") FieldBean bean) { 104 | Assert.assertNotNull("bean should not be null", bean); 105 | Assert.assertTrue("bean.index should be greater than zero", bean.index > 0); 106 | Assert.assertNotNull("bean.name should not be null", bean.name); 107 | } 108 | 109 | @Test 110 | @Spockito.Unroll({ 111 | "| Index | Name |", 112 | "| 1 | Test 1 |", 113 | "| 2 | Test 2 |" 114 | }) 115 | @Spockito.Name("[{row}]: {1}") 116 | public void testUnrollAllColumnsIntoAccessorBean(final @Spockito.Ref("*") AccessorBean bean) { 117 | Assert.assertNotNull("bean should not be null", bean); 118 | Assert.assertTrue("bean.index should be greater than zero", bean.index > 0); 119 | Assert.assertNotNull("bean.name should not be null", bean.getName()); 120 | } 121 | 122 | @Test 123 | @Spockito.Unroll({ 124 | "| Index | Age |", 125 | "| 1 | 44 |", 126 | "| 2 | 55 |" 127 | }) 128 | @Spockito.Name("[{row}]: {0}:Age={1}") 129 | public void testUnrollAllColumnsIntoMap(final @Spockito.Ref("*") Map map) { 130 | Assert.assertNotNull("map should not be null", map); 131 | Assert.assertTrue("map.Index should be greater than zero", map.get("Index") > 0); 132 | Assert.assertTrue("map.Age should be greater than zero", map.get("Age") > 40); 133 | } 134 | 135 | @Test 136 | @Spockito.Unroll({ 137 | "| List |", 138 | "| [ { index=1 ; name=cherry }, { index=2 ; name=apple } ] |", 139 | "| [ { index=10 ; name=rose }, { index=20 ; name=tulip } , { index=30 ; name=erika } ] |" 140 | }) 141 | public void testUnrollListOfBeans(final @Spockito.Ref("List") List list) { 142 | Assert.assertNotNull("list should not be null", list); 143 | Assert.assertTrue("list size should be at least 2", 2 <= list.size()); 144 | } 145 | } -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoTableRow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.Type; 27 | import java.util.ArrayList; 28 | import java.util.Iterator; 29 | import java.util.LinkedHashMap; 30 | import java.util.List; 31 | import java.util.Map; 32 | import java.util.Spliterator; 33 | import java.util.stream.Stream; 34 | 35 | import static java.util.Collections.unmodifiableList; 36 | import static java.util.Objects.requireNonNull; 37 | import static org.tools4j.spockito.table.Strings.EMPTY_STRING_ARRAY; 38 | import static org.tools4j.spockito.table.Strings.UNESCAPED_PIPE; 39 | 40 | public class SpockitoTableRow implements TableRow { 41 | 42 | private final Table table; 43 | private final List values; 44 | 45 | public SpockitoTableRow(final Table table) { 46 | this(table, new ArrayList<>()); 47 | } 48 | 49 | SpockitoTableRow(final Table table, final List values) { 50 | this.table = requireNonNull(table); 51 | this.values = requireNonNull(values); 52 | } 53 | 54 | public static SpockitoTableRow empty(final Table table) { 55 | return new SpockitoTableRow(table); 56 | } 57 | 58 | public static SpockitoTableRow parse(final Table table, final String rowString) { 59 | final String noBars = Strings.removeSurroundingPipes(rowString); 60 | final String[] parts = UNESCAPED_PIPE.split(noBars); 61 | final SpockitoTableRow tableRow = new SpockitoTableRow(table); 62 | for (final String part : parts) { 63 | tableRow.values.add(Converters.STRING_CONVERTER.apply(Strings.unescape(part.trim()))); 64 | } 65 | for (int i = parts.length; i < table.getColumnCount(); i++) { 66 | tableRow.values.add(null); 67 | } 68 | return tableRow; 69 | } 70 | 71 | @Override 72 | public Table getTable() { 73 | return table; 74 | } 75 | 76 | @Override 77 | public boolean isSeparatorRow() { 78 | return values.stream().anyMatch(s -> s.contains("-") || s.contains("=")) && 79 | values.stream().allMatch(s -> Strings.allCharsMatchingAnyOf(s, '-', '=')); 80 | } 81 | 82 | @Override 83 | public int getColumnCount() { 84 | return values.size(); 85 | } 86 | 87 | @Override 88 | public String get(final int index) { 89 | return values.get(index); 90 | } 91 | 92 | public String get(final String column) { 93 | final int index = table.getColumnIndexByName(column); 94 | if (index >= 0) { 95 | return get(index); 96 | } 97 | throw new IllegalArgumentException("No such column: " + column); 98 | } 99 | 100 | @Override 101 | public int indexOf(final String value) { 102 | return values.indexOf(value); 103 | } 104 | 105 | @Override 106 | public int getRowIndex() { 107 | return table.getRowIndex(this); 108 | } 109 | 110 | @Override 111 | public String[] toArray() { 112 | return values.toArray(EMPTY_STRING_ARRAY); 113 | } 114 | 115 | @Override 116 | public List toList() { 117 | return new ArrayList<>(values); 118 | } 119 | 120 | @Override 121 | public Map toMap() { 122 | final int cols = getColumnCount(); 123 | final Map map = new LinkedHashMap<>(); 124 | for (int i = 0; i < cols; i++) { 125 | map.put(table.getColumnName(i), get(i)); 126 | } 127 | return map; 128 | } 129 | 130 | @Override 131 | public T to(final Class type) { 132 | return to(type, SpockitoValueConverter.DEFAULT_INSTANCE); 133 | } 134 | 135 | @Override 136 | public T to(final Class type, final ValueConverter valueConverter) { 137 | return to(type, type, valueConverter); 138 | } 139 | 140 | @Override 141 | public T to(final Class type, final Type genericType, final ValueConverter valueConverter) { 142 | if (values.size() < 1 || valueConverter.isMultiValueType(type, genericType)) { 143 | return valueConverter.convert(type, genericType, toMap().toString()); 144 | } 145 | return valueConverter.convert(type, genericType, get(0)); 146 | } 147 | 148 | @Override 149 | public Iterator iterator() { 150 | return unmodifiableList(values).iterator(); 151 | } 152 | 153 | @Override 154 | public Spliterator spliterator() { 155 | return unmodifiableList(values).spliterator(); 156 | } 157 | 158 | @Override 159 | public Stream stream() { 160 | return unmodifiableList(values).stream(); 161 | } 162 | 163 | @Override 164 | public String toString() { 165 | return "row(" + getRowIndex() + ")=" + values; 166 | } 167 | } -------------------------------------------------------------------------------- /spockito-junit4/README.md: -------------------------------------------------------------------------------- 1 | # spockito-junit4 2 | Java JUnit 4.x runner for parameterized tests where the test cases are defined in a table-like manner. 3 | 4 | ### Unroll at method level 5 | 6 | Test cases are defined via ``@Spockito.Unroll`` annotation directly on the test method. The best explanation are 7 | probably a few simple examples: 8 | 9 | ```java 10 | @RunWith(Spockito.class) 11 | public class UnrollMethodDataTest { 12 | 13 | @Test 14 | @Spockito.Unroll({ 15 | "| Last Name | First Name |", 16 | "| Jones | David |", 17 | "| Jensen | Astrid |" 18 | }) 19 | public void testUnrollNames(String lastName, String firstName) { 20 | Assert.assertTrue("Last Name should start with J", lastName.startsWith("J")); 21 | Assert.assertTrue("First Name should end with id", firstName.endsWith("id")); 22 | } 23 | 24 | @Test 25 | @Spockito.Unroll({ 26 | "| Name | Year | Birthday |", 27 | "|-------|------|------------|", 28 | "| Henry | 1981 | 1981-11-28 |", 29 | "| Jessy | 1965 | 1965-03-28 |" 30 | }) 31 | @Spockito.Name("[{row}]: Name={0}") 32 | public void testUnrollBirthdays(String name, int year, LocalDate birthday) { 33 | Assert.assertEquals("Name should have 5 characters", 5, name.length()); 34 | Assert.assertTrue("Year is before 1990", 1990 > year); 35 | Assert.assertEquals("Day is 28th", 28, birthday.getDayOfMonth()); 36 | } 37 | 38 | @Test 39 | @Spockito.Unroll({ 40 | "| Object | Vertices | Angle sum |", 41 | "|==========|==========|===========|", 42 | "| Triangle | 3 | 180 |", 43 | "| Square | 4 | 360 |", 44 | "| Pentagon | 5 | 540 |", 45 | "|----------|----------|-----------|", 46 | }) 47 | @Spockito.Name("[{Object}]: ({Vertices}-2)*180 = {Angle sum}") 48 | public void testUnrollAngularSums(@Spockito.Ref("Vertices") int n, 49 | @Spockito.Ref("Angle sum") int degrees, 50 | @Spockito.Ref("Object") String name) { 51 | Assert.assertTrue("There should be 3 or more vertices", 3 <= n); 52 | Assert.assertEquals("Angular sum is wrong for: " + name, degrees, (n-2)*180); 53 | } 54 | } 55 | ``` 56 | This and other examples can be found [here](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/). 57 | 58 | #### Run above test in IDE (here: IntelliJ) 59 | ![spockito-junit4-idea-testrun.png](https://github.com/tools4j/spockito/blob/master/spockito-junit4-idea-testrun.png) 60 | 61 | ### Unroll at class level 62 | 63 | Alternatively, the test data can be defined at class level. All methods can then use the same test cases. The values 64 | are either 65 | * directly injected to the method as method parameters 66 | * passed to the single test constructor where they are usually assigned to a member variable 67 | * directly assigned to a field annotated with ``@Spockito.Ref`` 68 | 69 | An example with field injection is shown next: 70 | 71 | ```java 72 | @Spockito.Unroll({ 73 | "| Operation | Sign | Operand1 | Operand2 | Result | NeutralOperand |", 74 | "|-----------|------|----------|----------|--------|----------------|", 75 | "| Add | + | 4 | 7 | 11 | 0 |", 76 | "| Subtract | - | 111 | 12 | 99 | 0 |", 77 | "| Multiply | * | 24 | 5 | 120 | 1 |", 78 | "| Divide | / | 24 | 3 | 8 | 1 |" 79 | }) 80 | @Spockito.Name("[{row}]: {Operation}") 81 | @RunWith(Spockito.class) 82 | public class UnrollClassDataToFieldsTest { 83 | 84 | @Spockito.Ref 85 | private Operation operation; 86 | @Spockito.Ref 87 | private char sign; 88 | @Spockito.Ref 89 | private int operand1; 90 | @Spockito.Ref 91 | private int operand2; 92 | @Spockito.Ref 93 | private int result; 94 | @Spockito.Ref 95 | private int neutralOperand; 96 | 97 | @Test 98 | @Spockito.Name("[{row}]: {Operand1} {Sign} {Operand2} = {Result}") 99 | public void testOperation() { 100 | Assert.assertEquals("Result is wrong!", result, operation.evaluate(operand1, operand2)); 101 | } 102 | 103 | @Test 104 | @Spockito.Name("[{row}]: {Operand1} {Sign} {NeutralOperand} = {Operand1}") 105 | public void testNeutralOperand() { 106 | Assert.assertEquals("Result with neutral operand is wrong!", 107 | operand1, operation.evaluate(operand1, neutralOperand)); 108 | } 109 | } 110 | ``` 111 | This and other examples can be found [here](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/). 112 | 113 | ### More examples 114 | * [UnrollMethodDataTest.java](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/UnrollMethodDataTest.java) 115 | * [UnrollClassDataToFieldsTest.java](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/UnrollClassDataToFieldsTest.java) 116 | * [UnrollClassDataToConstructorTest.java](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/UnrollClassDataToConstructorTest.java) 117 | * [UnrollClassDataToMethodTest.java](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/UnrollClassDataToMethodTest.java) 118 | * [all tests](https://github.com/tools4j/spockito/blob/master/spockito-junit4/src/test/java/org/tools4j/spockito/) 119 | 120 | ### Maven 121 | Add the following dependency to your maven pom.xml file: 122 | 123 | ```xml 124 | 125 | org.tools4j 126 | spockito-junit4 127 | 2.0 128 | test 129 | 130 | ``` 131 | 132 | ### Download 133 | Sources and binaries can be downloaded from maven central: 134 | * [spockito-junit4](https://search.maven.org/search?q=spockito-junit4) in Maven Central -------------------------------------------------------------------------------- /spockito-junit5/src/test/java/org/tools4j/spockito/jupiter/SpockitoExtensionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.api.AfterAll; 27 | import org.junit.jupiter.api.AfterEach; 28 | import org.junit.jupiter.api.Test; 29 | import org.junit.jupiter.api.extension.ExtendWith; 30 | import org.junit.jupiter.params.ParameterizedTest; 31 | import org.junit.jupiter.params.provider.Arguments; 32 | import org.junit.jupiter.params.provider.MethodSource; 33 | import org.tools4j.spockito.table.Column; 34 | import org.tools4j.spockito.table.Row; 35 | 36 | import java.util.Arrays; 37 | import java.util.LinkedHashMap; 38 | import java.util.Map; 39 | import java.util.stream.Stream; 40 | 41 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 42 | import static org.junit.jupiter.api.Assertions.assertEquals; 43 | import static org.junit.jupiter.api.Assertions.assertTrue; 44 | 45 | @ExtendWith(SpockitoExtension.class) 46 | public class SpockitoExtensionTest { 47 | 48 | public static class DataRow { 49 | public Operation operation; 50 | public char sign; 51 | public int operand1; 52 | public int operand2; 53 | public int result; 54 | public int neutralOperand; 55 | 56 | @Override 57 | public String toString() { 58 | return operation.name(); 59 | } 60 | } 61 | 62 | @TableSource({ 63 | "| Operation | Sign | Operand1 | Operand2 | Result | NeutralOperand |", 64 | "|-----------|------|----------|----------|--------|----------------|", 65 | "| Add | + | 4 | 7 | 11 | 0 |", 66 | "| Subtract | - | 111 | 12 | 99 | 0 |", 67 | "| Multiply | * | 24 | 5 | 120 | 1 |", 68 | "| Divide | / | 24 | 3 | 8 | 1 |" 69 | }) 70 | private static DataRow[] staticTestData; 71 | 72 | @TableSource({ 73 | "| Operation |", 74 | "|-----------|", 75 | "| Add |", 76 | "| Subtract |", 77 | "| Multiply |", 78 | "| Divide |" 79 | }) 80 | private Operation[] operation; 81 | 82 | @Test 83 | public void testCoversAllOperations() { 84 | final Operation[] constants = Operation.values(); 85 | assertArrayEquals(constants, operation); 86 | assertEquals(staticTestData.length, operation.length); 87 | assertArrayEquals(Arrays.stream(staticTestData).map(row -> row.operation).toArray(), operation); 88 | } 89 | 90 | @ParameterizedTest(name = "[{index}]: {0}") 91 | @MethodSource("staticTestData") 92 | public void testOperation(DataRow data) { 93 | assertEquals(data.result, data.operation.evaluate(data.operand1, data.operand2), 94 | "" + data.operand1 + data.sign + data.operand2); 95 | } 96 | 97 | @ParameterizedTest(name = "[{index}]: {0}") 98 | @MethodSource("staticTestData") 99 | public void testNeutralOperand(DataRow data) { 100 | assertEquals(data.operand1, data.operation.evaluate(data.operand1, data.neutralOperand), 101 | "" + data.operand1 + data.sign + data.neutralOperand); 102 | assertEquals(data.operand2, data.operation.evaluate(data.operand2, data.neutralOperand), 103 | "" + data.operand2 + data.sign + data.neutralOperand); 104 | } 105 | 106 | @ParameterizedTest(name = "[{index}]: {1}") 107 | @TableSource({ 108 | "| Value |", 109 | "| Row 1 |", 110 | "| Row 2 |" 111 | }) 112 | public void independentTableSourceTest(final @Row int rowIndex, @Column("Value") final String value) { 113 | assertTrue(value.startsWith("Row")); 114 | assertEquals("Row " + (rowIndex + 1), value); 115 | } 116 | 117 | @TableSource({ 118 | "| Name | Age |", 119 | "| Harry | 12 |", 120 | "| Maya | 14 |" 121 | }) 122 | public static void staticInject(final String name, final int age) { 123 | nameToAge.put(name, age); 124 | } 125 | private static final Map nameToAge = new LinkedHashMap<>(); 126 | 127 | @AfterAll 128 | static void validateNameToAge() { 129 | assertEquals(2, nameToAge.size()); 130 | } 131 | 132 | @TableSource({ 133 | "| Object | Vertices |", 134 | "| Triangle | 3 |", 135 | "| Square | 4 |", 136 | "| Rectangle | 4 |" 137 | }) 138 | public void instanceInject(final String object, final int vertices) { 139 | vertexSum += vertices; 140 | } 141 | private int vertexSum; 142 | 143 | @AfterEach 144 | void validateVertexSum() { 145 | assertEquals(11, vertexSum); 146 | } 147 | 148 | static Stream staticTestData() { 149 | return Arrays.stream(staticTestData).map(Arguments::of); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoTableRowConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import org.tools4j.spockito.table.InjectionContext.Phase; 27 | 28 | import java.lang.reflect.AnnotatedElement; 29 | import java.lang.reflect.Field; 30 | import java.lang.reflect.Parameter; 31 | import java.lang.reflect.Type; 32 | 33 | import static java.util.Objects.requireNonNull; 34 | import static org.tools4j.spockito.table.SpockitoAnnotations.annotationDirectOrMeta; 35 | 36 | public class SpockitoTableRowConverter implements TableRowConverter { 37 | 38 | private final InjectionContext dataSubContextOrNull; 39 | private final AnnotatedElement annotatedElementOrNull; 40 | private final String nameOrNull; 41 | private final int index; 42 | private final Class targetClass; 43 | private final Type targetType; 44 | private final ValueConverter valueConverter; 45 | 46 | public SpockitoTableRowConverter(final InjectionContext dataSubContextOrNull, 47 | final AnnotatedElement annotatedElementOrNull, 48 | final String nameOrNull, 49 | final int index, 50 | final Class targetClass, 51 | final Type targetType, 52 | final ValueConverter valueConverter) { 53 | this.dataSubContextOrNull = dataSubContextOrNull; 54 | this.annotatedElementOrNull = annotatedElementOrNull; 55 | this.nameOrNull = nameOrNull; 56 | this.index = index; 57 | this.targetClass = requireNonNull(targetClass); 58 | this.targetType = requireNonNull(targetType); 59 | this.valueConverter = requireNonNull(valueConverter); 60 | } 61 | 62 | public static TableRowConverter create(final InjectionContext context, final Parameter parameter, final int index, final ValueConverter valueConverter) { 63 | return new SpockitoTableRowConverter(context.createSubContextOrNull(parameter, Data.class), parameter, 64 | parameterNameIfPresent(parameter), index, parameter.getType(), parameter.getParameterizedType(), 65 | valueConverter); 66 | } 67 | 68 | public static TableRowConverter create(final Field field, final ValueConverter valueConverter) { 69 | return new SpockitoTableRowConverter(null, field, field.getName(), -1, field.getType(), 70 | field.getGenericType(), valueConverter); 71 | } 72 | 73 | public static TableRowConverter create(final Class rowType, final ValueConverter valueConverter) { 74 | return new SpockitoTableRowConverter(null, null, null, -1, 75 | rowType, rowType, valueConverter); 76 | } 77 | 78 | private static String parameterNameIfPresent(final Parameter parameter) { 79 | return parameter.isNamePresent() ? parameter.getName() : null; 80 | } 81 | 82 | @Override 83 | public Object convert(final TableRow tableRow) { 84 | requireNonNull(tableRow); 85 | if (dataSubContextOrNull != null) { 86 | final Object value = dataForAnnotatedElement(dataSubContextOrNull.annotatedElement()); 87 | if (value != null) { 88 | return value; 89 | } 90 | } 91 | if (targetClass.isInstance(tableRow)) { 92 | return tableRow; 93 | } 94 | if (targetClass.isInstance(tableRow.getTable())) { 95 | return tableRow.getTable(); 96 | } 97 | String name = nameOrNull; 98 | if (annotatedElementOrNull != null) { 99 | final Row row = annotationDirectOrMeta(annotatedElementOrNull, Row.class); 100 | if (row != null) { 101 | if (int.class == targetClass) { 102 | return tableRow.getRowIndex(); 103 | } 104 | return convert(tableRow.toMap().toString(), "row(" + tableRow.getRowIndex() + ")"); 105 | } 106 | final Column column = annotationDirectOrMeta(annotatedElementOrNull, Column.class); 107 | if (column != null) { 108 | name = column.value(); 109 | } 110 | } 111 | if (name != null) { 112 | return convert(valueByName(tableRow, name), name); 113 | } 114 | if (index == -1) { 115 | return tableRow; 116 | } 117 | return convert(valueByIndex(tableRow, index), index); 118 | } 119 | 120 | private String valueByName(final TableRow tableRow, final String name) { 121 | final int column; 122 | try { 123 | column = tableRow.getTable().getColumnIndexByName(name); 124 | } catch (final Exception e) { 125 | throw new IllegalArgumentException("Could not access table column " + name, e); 126 | } 127 | return valueByIndex(tableRow, column); 128 | } 129 | 130 | private String valueByIndex(final TableRow tableRow, final int index) { 131 | try { 132 | return tableRow.get(index); 133 | } catch (final Exception e) { 134 | throw new IllegalArgumentException("Could not access table value for column index " + index, e); 135 | } 136 | } 137 | 138 | private Object convert(final String value, final Object column) { 139 | try { 140 | return valueConverter.convert(targetClass, targetType, value); 141 | } catch (final Exception e) { 142 | throw new IllegalArgumentException("Conversion to " + targetClass + " failed for column '" + column + 143 | "': " + value, e); 144 | } 145 | } 146 | 147 | private static Object dataForAnnotatedElement(final AnnotatedElement element) { 148 | requireNonNull(element); 149 | final Data data = annotationDirectOrMeta(element, Data.class); 150 | try { 151 | final DataProvider dataProvider = data.value().newInstance(); 152 | final InjectionContext context = InjectionContext.create(Phase.INIT, element); 153 | return dataProvider.applicable(context) ? dataProvider.provideData(context) : null; 154 | } catch (final Exception e) { 155 | throw new SpockitoException("Cannot provide data for " + element + " annotated with @" 156 | + Data.class.getSimpleName() + " (or meta annotation)", e); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /spockito-table/src/main/java/org/tools4j/spockito/table/SpockitoTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.table; 25 | 26 | import java.lang.reflect.Array; 27 | import java.util.ArrayList; 28 | import java.util.Collections; 29 | import java.util.Comparator; 30 | import java.util.Iterator; 31 | import java.util.List; 32 | import java.util.function.Predicate; 33 | import java.util.stream.Collectors; 34 | 35 | /** 36 | * Spockito's default implementation of a {@link Table}. 37 | */ 38 | public class SpockitoTable implements Table { 39 | 40 | private static final SpockitoTable EMPTY = new SpockitoTable(); 41 | 42 | private final TableRow headers; 43 | private final List data = new ArrayList<>(); 44 | 45 | private SpockitoTable() { 46 | this.headers = SpockitoTableRow.empty(this); 47 | } 48 | 49 | private SpockitoTable(final String headerString) { 50 | this.headers = parseRow(this, 0, headerString); 51 | if (headers.stream().distinct().count() < headers.getColumnCount()) { 52 | throw new IllegalArgumentException("Duplicate column headers: " + headers); 53 | } 54 | } 55 | 56 | SpockitoTable(final List headers, final List> rows) { 57 | this.headers = new SpockitoTableRow(this, headers); 58 | for (final List row : rows) { 59 | data.add(new SpockitoTableRow(this, row)); 60 | } 61 | } 62 | 63 | public int getColumnCount() { 64 | //null in constructor when parsing header row 65 | return headers == null ? 0 : headers.getColumnCount(); 66 | } 67 | 68 | public int getColumnIndexByName(final String columnName) { 69 | int columnIndex = headers.indexOf(columnName); 70 | if (columnIndex < 0) { 71 | if (columnName.length() > 0 && Character.isLowerCase(columnName.charAt(0))) { 72 | columnIndex = headers.indexOf(Strings.firstCharToUpperCase(columnName)); 73 | } 74 | } 75 | if (columnIndex < 0) { 76 | throw new IllegalArgumentException("No such column: " + columnName); 77 | } 78 | return columnIndex; 79 | } 80 | 81 | public boolean hasColumn(final String columnName) { 82 | return 0 <= headers.indexOf(columnName); 83 | } 84 | 85 | public int getRowCount() { 86 | return data.size(); 87 | } 88 | 89 | @Override 90 | public List getColumnNames() { 91 | return headers.toList(); 92 | } 93 | 94 | public String getColumnName(final int index) { 95 | return headers.get(index); 96 | } 97 | 98 | public TableRow getRow(final int rowIndex) { 99 | return data.get(rowIndex); 100 | } 101 | 102 | public int getRowIndex(final TableRow row) { 103 | return data.indexOf(row); 104 | } 105 | 106 | public String getValue(final int rowIndex, final int columnIndex) { 107 | return data.get(rowIndex).get(columnIndex); 108 | } 109 | 110 | public String getValue(final int rowIndex, final String columnName) { 111 | final int columnIndex = getColumnIndexByName(columnName); 112 | return data.get(rowIndex).get(columnIndex); 113 | } 114 | 115 | @Override 116 | public Iterator iterator() { 117 | return Collections.unmodifiableList(data).iterator(); 118 | } 119 | 120 | @Override 121 | public Table filter(final Predicate filter) { 122 | return new SpockitoTable(headers.toList(), stream().filter(filter).map(TableRow::toList).collect(Collectors.toList())); 123 | } 124 | 125 | @Override 126 | public Table sort(final Comparator comparator) { 127 | return new SpockitoTable(headers.toList(), stream().sorted(comparator).map(TableRow::toList).collect(Collectors.toList())); 128 | } 129 | 130 | @Override 131 | public TableJoiner join(final TableRow row) { 132 | return new SpockitoTableJoiner(this, row); 133 | } 134 | 135 | public static T[] parse(final Class rowType, final String[] headerAndRows) { 136 | return parse(rowType, headerAndRows, new SpockitoValueConverter()); 137 | } 138 | 139 | public static T[] parse(final Class rowType, final String[] headerAndRows, final ValueConverter valueConverter) { 140 | final SpockitoTable table = parse(headerAndRows); 141 | final int rows = table.getRowCount(); 142 | @SuppressWarnings("unchecked") 143 | final T[] result = (T[])Array.newInstance(rowType, rows); 144 | for (int row = 0; row < rows; row++) { 145 | result[row] = valueConverter.convert(rowType, rowType, table.getRow(row).toMap().toString()); 146 | } 147 | return result; 148 | } 149 | 150 | public static SpockitoTable parse(final String[] headerAndRows) { 151 | if (headerAndRows.length > 0) { 152 | final SpockitoTable table = new SpockitoTable(headerAndRows[0]); 153 | for (int i = 1; i < headerAndRows.length; i++) { 154 | final TableRow tableRow = parseRow(table, i, headerAndRows[i]); 155 | if (!tableRow.isSeparatorRow()) { 156 | table.data.add(tableRow); 157 | } 158 | } 159 | return table; 160 | } 161 | return SpockitoTable.EMPTY; 162 | } 163 | 164 | private static TableRow parseRow(final SpockitoTable table, final int row, final String rowString) { 165 | final String trimmed = rowString.trim(); 166 | if (trimmed.length() < 2 || trimmed.charAt(0) != '|' || trimmed.charAt(trimmed.length() - 1) != '|') { 167 | throw new IllegalArgumentException("Invalid table data: row " + row + " must start and end with '|'"); 168 | } 169 | final TableRow tableRow = SpockitoTableRow.parse(table, trimmed); 170 | if (row != 0) { 171 | if (tableRow.getColumnCount() > table.getColumnCount()) { 172 | throw new IllegalArgumentException("Invalid table data: row " + row + " has more columns than header row: " + tableRow.getColumnCount() + " > " + table.getColumnCount()); 173 | } 174 | } 175 | return tableRow; 176 | } 177 | 178 | @Override 179 | public String toString() { 180 | return "SpockitoTable{" + 181 | "headers=" + headers + 182 | (data.isEmpty() ? "" : data.toString()) + 183 | '}'; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /spockito-junit5/src/main/java/org/tools4j/spockito/jupiter/TableRowConverters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2022 tools4j.org (Marco Terzer) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package org.tools4j.spockito.jupiter; 25 | 26 | import org.junit.jupiter.params.aggregator.AggregateWith; 27 | import org.junit.jupiter.params.converter.ConvertWith; 28 | import org.tools4j.spockito.table.Data; 29 | import org.tools4j.spockito.table.DataProvider; 30 | import org.tools4j.spockito.table.InjectionContext; 31 | import org.tools4j.spockito.table.InjectionContext.Phase; 32 | import org.tools4j.spockito.table.SpockitoException; 33 | import org.tools4j.spockito.table.SpockitoTableRowConverter; 34 | import org.tools4j.spockito.table.Table; 35 | import org.tools4j.spockito.table.TableData; 36 | import org.tools4j.spockito.table.TableDataProvider; 37 | import org.tools4j.spockito.table.TableJoiner; 38 | import org.tools4j.spockito.table.TableJoiner.JoinBuilder; 39 | import org.tools4j.spockito.table.TableRow; 40 | import org.tools4j.spockito.table.TableRowConverter; 41 | import org.tools4j.spockito.table.ValueConverter; 42 | 43 | import java.lang.reflect.Parameter; 44 | import java.util.Arrays; 45 | 46 | import static java.util.Objects.requireNonNull; 47 | import static org.tools4j.spockito.table.SpockitoAnnotations.annotationDirectOrMeta; 48 | 49 | /** 50 | * Similar to the converters created by the factory methods in {@link SpockitoTableRowConverter} but with added support 51 | * for the following parameter-level annotations: 52 | *

    53 | *
  • {@linkplain ConvertWith @ConvertWith} for parameter specific conversion
  • 54 | *
  • {@linkplain AggregateWith @AggregateWith} for parameter level aggregation, with special consideration of 55 | * {@link TableRowAggregator}
  • 56 | *
  • {@linkplain JoinOn @JoinOn} for parameter level child tables joined to the test level parent table
  • 57 | *
58 | */ 59 | enum TableRowConverters { 60 | ; 61 | static TableRowConverter create(final InjectionContext context, 62 | final Parameter parameter, 63 | final int index, 64 | final ValueConverter valueConverter) { 65 | if (annotationDirectOrMeta(parameter, ConvertWith.class) != null) { 66 | return objConverter(context, parameter, index, valueConverter); 67 | } 68 | final AggregateWith aggregateWith = annotationDirectOrMeta(parameter, AggregateWith.class); 69 | if (aggregateWith != null) { 70 | final Class agg = aggregateWith.value(); 71 | if (TableRowAggregator.class.isAssignableFrom(agg)) { 72 | return tableRow -> tableRow; 73 | } 74 | return objConverter(context, parameter, index, valueConverter); 75 | } 76 | final JoinOn joinOn = annotationDirectOrMeta(parameter, JoinOn.class); 77 | if (joinOn != null) { 78 | final Data data = annotationDirectOrMeta(parameter, Data.class); 79 | if (data != null) { 80 | final Class dataProvider = data.value(); 81 | if (TableDataProvider.class.isAssignableFrom(dataProvider)) { 82 | final TableRowConverter joinedConverter = joinedConverterOrNull(parameter, joinOn); 83 | if (joinedConverter != null) { 84 | return joinedConverter; 85 | } 86 | } 87 | } 88 | } 89 | return SpockitoTableRowConverter.create(context, parameter, index, valueConverter); 90 | } 91 | 92 | private static TableRowConverter objConverter(final InjectionContext context, 93 | final Parameter parameter, 94 | final int index, 95 | final ValueConverter valueConverter) { 96 | return new SpockitoTableRowConverter(context.createSubContextOrNull(parameter, Data.class), parameter, 97 | parameter.getName(), index, Object.class, Object.class, valueConverter); 98 | } 99 | 100 | private static TableRowConverter joinedConverterOrNull(final Parameter parameter, final JoinOn joinedOn) { 101 | requireNonNull(parameter); 102 | requireNonNull(joinedOn); 103 | final Data data = annotationDirectOrMeta(parameter, Data.class); 104 | try { 105 | final DataProvider dataProvider = data.value().newInstance(); 106 | if (dataProvider instanceof TableDataProvider) { 107 | final TableDataProvider tableDataProvider = (TableDataProvider)dataProvider; 108 | final InjectionContext ctxt = InjectionContext.create(Phase.INIT, parameter); 109 | if (dataProvider.applicable(ctxt)) { 110 | final Table table = tableDataProvider.provideTable(ctxt); 111 | final ValueConverter valueConverter = tableDataProvider.provideValueConverter(ctxt); 112 | return tableRow -> tableDataProvider.provideData(ctxt, join(table, tableRow, joinedOn), valueConverter); 113 | } 114 | } 115 | return null; 116 | } catch (final Exception e) { 117 | throw new SpockitoException("Cannot provide data for " + parameter + " annotated with @" 118 | + TableData.class.getSimpleName() + " (or meta annotation)", e); 119 | } 120 | } 121 | 122 | private static Table join(final Table child, final TableRow parent, final JoinOn joinOn) { 123 | final String[] children = joinOn.child(); 124 | final String[] parents = joinOn.parent(); 125 | if (children.length != parents.length) { 126 | throw new IllegalArgumentException("JoinOn.parent=" + Arrays.toString(joinOn.parent()) + " and JoinOn.child=" 127 | + Arrays.toString(joinOn.parent()) + " must have matching number of entries"); 128 | } 129 | final TableJoiner joiner = child.join(parent); 130 | JoinBuilder builder = null; 131 | for (final String common : joinOn.value()) { 132 | builder = builder == null ? joiner.on(common) : builder.and(common); 133 | } 134 | for (int i = 0; i < children.length; i++) { 135 | builder = builder == null ? joiner.on(children[i], parents[i]) : 136 | builder.and(children[i], parents[i]); 137 | } 138 | if (builder != null) { 139 | return builder.apply(); 140 | } 141 | throw new IllegalArgumentException("JoinOn must define at least one value"); 142 | } 143 | } 144 | --------------------------------------------------------------------------------