├── .travis.yml ├── .githooks ├── license-maintainer │ ├── .gitignore │ ├── .gitattributes │ ├── LICENSE-hash │ ├── LICENSE-javadoc │ ├── install │ ├── license.pm │ ├── README.md │ ├── pre-commit │ └── LICENSE └── pre-commit ├── .gitattributes ├── core-utils ├── .settings │ ├── org.eclipse.core.runtime.prefs │ ├── org.eclipse.core.resources.prefs │ └── org.eclipse.jdt.ui.prefs ├── src │ └── main │ │ └── java │ │ └── com │ │ └── nitorcreations │ │ └── core │ │ └── utils │ │ ├── Templater.java │ │ └── KillProcess.java └── pom.xml ├── junit-runners ├── .settings │ ├── org.eclipse.core.runtime.prefs │ ├── org.eclipse.m2e.core.prefs │ ├── org.eclipse.core.resources.prefs │ ├── org.moreunit.prefs │ └── org.eclipse.jdt.core.prefs ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── nitorcreations │ │ │ └── junit │ │ │ ├── runner │ │ │ ├── StandaloneJUnitRunnerTest.java │ │ │ ├── SuccessTestNotForMaven.java │ │ │ ├── SimpleTestNotForMaven.java │ │ │ └── StandaloneJUnitRunnerMainTest.java │ │ │ └── runners │ │ │ ├── examples │ │ │ ├── FailingNestedRunnerTest.java │ │ │ ├── TestNicelyParameterized.java │ │ │ ├── WrappingParameterizedRunnerTest.java │ │ │ └── ListTest.java │ │ │ └── parameterized │ │ │ └── WrappingParameterizedRunnerTest.java │ └── main │ │ └── java │ │ └── com │ │ └── nitorcreations │ │ └── junit │ │ ├── runners │ │ ├── parameterized │ │ │ ├── ParameterizationStrategyNotAvailableException.java │ │ │ ├── WrappedRunWith.java │ │ │ ├── ParameterizedSuite.java │ │ │ ├── ParameterizationStrategy.java │ │ │ ├── DefaultParameterizationStrategy.java │ │ │ ├── DescriptionMappingRunNotifierProxy.java │ │ │ ├── ClassLoaderInjector.java │ │ │ ├── DescriptionMapper.java │ │ │ ├── PowermockParameterizationStrategy.java │ │ │ ├── ParameterizedSuiteBuilder.java │ │ │ └── WrappingParameterizedRunner.java │ │ ├── NicelyParameterized.java │ │ └── NestedRunner.java │ │ ├── runner │ │ ├── StandaloneJUnitRunnerMain.java │ │ ├── ThreadLocalStdOutErrCapturer.java │ │ ├── StandaloneJUnitRunner.java │ │ └── XmlJUnitFormatter.java │ │ └── rules │ │ └── SkipTestMethodsAfterFirstFailureRule.java ├── README.md ├── LICENSE └── pom.xml ├── README.md ├── .gitignore └── pom.xml /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.java licensefile=.githooks/license-maintainer/LICENSE-javadoc 2 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec .githooks/license-maintainer/pre-commit "$@" 4 | -------------------------------------------------------------------------------- /core-utils/.settings/org.eclipse.core.runtime.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | line.separator=\n 3 | -------------------------------------------------------------------------------- /junit-runners/.settings/org.eclipse.core.runtime.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | line.separator=\n 3 | -------------------------------------------------------------------------------- /core-utils/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding/=UTF-8 4 | -------------------------------------------------------------------------------- /junit-runners/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /junit-runners/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CoreComponents 2 | ============== 3 | 4 | [Nitor Creations](http://www.nitorcreations.com) Core Components 5 | 6 | [JUnit Runners](https://github.com/NitorCreations/CoreComponents/tree/master/junit-runners) 7 | -------------------------------------------------------------------------------- /junit-runners/.settings/org.moreunit.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.moreunit.preferences.version=2 3 | org.moreunit.unitsourcefolder=junit-runners\:src/main/java\:junit-runners\:src/test/java 4 | org.moreunit.useprojectsettings=true 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | /lib 3 | /classes 4 | /checkouts 5 | *.jar 6 | *.class 7 | .lein-deps-sum 8 | .lein-failures 9 | .lein-plugins 10 | .lein-cljsbuild* 11 | *~ 12 | *jar 13 | /lib/ 14 | /classes/ 15 | .lein-deps-sum 16 | .project 17 | .classpath 18 | bin -------------------------------------------------------------------------------- /.githooks/license-maintainer/.gitattributes: -------------------------------------------------------------------------------- 1 | * -licensefile 2 | /license.pm licensefile=.githooks/license-maintainer/LICENSE-hash 3 | /pre-commit licensefile=.githooks/license-maintainer/LICENSE-hash 4 | /install licensefile=.githooks/license-maintainer/LICENSE-hash 5 | -------------------------------------------------------------------------------- /junit-runners/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 3 | org.eclipse.jdt.core.compiler.compliance=1.5 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.5 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.nitorcreations 4 | core-components 5 | 1.0-SNAPSHOT 6 | pom 7 | Core Components parent 8 | 9 | 2012 10 | 11 | junit-runners 12 | core-utils 13 | 14 | 15 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/LICENSE-hash: -------------------------------------------------------------------------------- 1 | # Copyright YEAR AUTHORS 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/LICENSE-javadoc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright YEAR AUTHORS 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runner/StandaloneJUnitRunnerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | 19 | public class StandaloneJUnitRunnerTest { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runner/SuccessTestNotForMaven.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import org.junit.Test; 19 | 20 | public class SuccessTestNotForMaven { 21 | @Test 22 | public void sucess() { 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/ParameterizationStrategyNotAvailableException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | public class ParameterizationStrategyNotAvailableException extends RuntimeException { 19 | private static final long serialVersionUID = 6377027695976359200L; 20 | 21 | public ParameterizationStrategyNotAvailableException(String message) { 22 | super(message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2015 Nitor Creations Oy 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -e 18 | 19 | if [ "$#" -gt 0 ]; then 20 | echo "usage: $0" 21 | echo 22 | echo "Installs the git hooks in this directory into .git/hooks/" 23 | echo "Any existing destination file is backed up first if necessary." 24 | exit 1 25 | fi 26 | 27 | cd "$(dirname "$0")/../.." 28 | 29 | for i in pre-commit ; do 30 | oldlink="$(readlink .git/hooks/${i} ||:)" 31 | newlink="../../.githooks/license-maintainer/${i}" 32 | if [ "$newlink" != "$oldlink" ]; then 33 | ln -snv --backup=numbered "${newlink}" .git/hooks/ 34 | fi 35 | done 36 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/WrappedRunWith.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Inherited; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | import org.junit.runner.Runner; 25 | 26 | /** 27 | * This annotation used to indicate which runner is to be wrapped when a 28 | * wrapping runner is given in the @RunWith(...) annotation. 29 | * 30 | * @see WrappingParameterizedRunner 31 | */ 32 | @Retention(RetentionPolicy.RUNTIME) 33 | @Target(ElementType.TYPE) 34 | @Inherited 35 | public @interface WrappedRunWith { 36 | Class value(); 37 | } 38 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/ParameterizedSuite.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014-2015 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import java.lang.annotation.ElementType; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.RetentionPolicy; 21 | import java.lang.annotation.Target; 22 | 23 | /** 24 | * This annotation is used by {@link WrappingParameterizedRunner} to detect the 25 | * method which configures the parameters with which the suite is to be run. The 26 | * method signature is expected to be (the name doesn't matter): 27 | * 28 | *
29 |  * @ParameterizedSuite
30 |  * public static void suite(ParameterizedSuiteBuilder builder) { ... }
31 |  * 
32 | */ 33 | @Retention(RetentionPolicy.RUNTIME) 34 | @Target(ElementType.METHOD) 35 | public @interface ParameterizedSuite { 36 | } 37 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runner/SimpleTestNotForMaven.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import static org.junit.Assert.fail; 19 | import static org.junit.Assume.assumeTrue; 20 | 21 | import org.junit.Ignore; 22 | import org.junit.Test; 23 | 24 | public class SimpleTestNotForMaven { 25 | @Test 26 | public void sucess() { 27 | } 28 | 29 | @Test 30 | public void error() { 31 | throw new RuntimeException("Testing exception reporting ]]> \"><"); 32 | } 33 | 34 | @Test 35 | public void failed() { 36 | fail("Testing assert failure ]]> \"><"); 37 | } 38 | 39 | @Test 40 | public void assumeFailed() { 41 | assumeTrue("Testing assume failure ]]> \"><", false); 42 | } 43 | 44 | @Test 45 | @Ignore 46 | public void ignore() { 47 | throw new RuntimeException("This is never thrown ]]> \"><"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/ParameterizationStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014-2015 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import org.objectweb.asm.ClassVisitor; 19 | import org.objectweb.asm.commons.GeneratorAdapter; 20 | 21 | import com.nitorcreations.junit.runners.parameterized.ParameterizedSuiteBuilder.TestInstantiatorBuilder; 22 | 23 | /** 24 | * See the 25 | * {@link WrappingParameterizedRunner#createParameterizedTestClass(int, TestInstantiatorBuilder)} 26 | * to see how this is used and 27 | * {@link WrappingParameterizedRunner#chooseParameterizationStrategy()} to see 28 | * how the correct implementation is chosen. 29 | */ 30 | public interface ParameterizationStrategy { 31 | void classCreationInProgress(String nameRaw, ClassVisitor cw); 32 | 33 | void loadConstructorArgs(GeneratorAdapter mv); 34 | 35 | void classConstructed(Class clazz, byte[] clazzBytes, 36 | Object[] constructorArgs); 37 | } 38 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runner/StandaloneJUnitRunnerMain.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import static java.lang.System.exit; 19 | 20 | import java.io.PrintStream; 21 | 22 | public class StandaloneJUnitRunnerMain { 23 | public static void main(String... args) { 24 | PrintStream err = System.err; 25 | if (args.length == 0) { 26 | err.println("Usage:"); 27 | err.println(" each argument is a comma separated list of junit testclass names to run in it's own thread"); 28 | err.println("Example:"); 29 | err.println(" Class1 ClassA,ClassB Class2"); 30 | err.println(" Will start 3 threads, where ClassA and ClassB are run in sequence in one thread"); 31 | err.println("System properties:"); 32 | err.println(" -Doutput.dir\tOutput directory for results"); 33 | exit(1); 34 | } 35 | try { 36 | new StandaloneJUnitRunner().main(args); 37 | } catch (Throwable t) { 38 | err.println(t.getMessage()); 39 | exit(1); 40 | } 41 | exit(0); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runners/examples/FailingNestedRunnerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.examples; 17 | 18 | import static org.hamcrest.CoreMatchers.is; 19 | import static org.junit.Assert.assertThat; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import org.junit.Before; 23 | import org.junit.Test; 24 | import org.junit.runner.JUnitCore; 25 | import org.junit.runner.Result; 26 | import org.junit.runner.RunWith; 27 | 28 | import com.nitorcreations.junit.runners.NestedRunner; 29 | 30 | public class FailingNestedRunnerTest { 31 | 32 | @Test 33 | public void ShouldFail() { 34 | Result result = JUnitCore.runClasses(Parent.class); 35 | assertThat(result.getFailureCount(), is(1)); 36 | assertTrue(result.getFailures().get(0).getException().getClass() == RuntimeException.class); 37 | } 38 | 39 | @RunWith(NestedRunner.class) 40 | public static class Parent { 41 | public class Failing { 42 | public Failing() { 43 | throw new RuntimeException(); 44 | } 45 | 46 | @Before 47 | public void setup() { 48 | } 49 | 50 | @Test 51 | public void isNeverExecuted() { 52 | } 53 | 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runners/examples/TestNicelyParameterized.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.examples; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | import org.junit.Test; 25 | import org.junit.runner.RunWith; 26 | import org.junit.runners.Parameterized.Parameters; 27 | 28 | import com.nitorcreations.junit.runners.NicelyParameterized; 29 | 30 | @RunWith(NicelyParameterized.class) 31 | public class TestNicelyParameterized { 32 | 33 | private final String str; 34 | private final int id; 35 | 36 | @Parameters 37 | public static List data() { 38 | return Arrays.asList(new Object[][] { { "kala", 1 }, { "foo", 42 } }); 39 | } 40 | 41 | public TestNicelyParameterized(String str, int id) { 42 | this.str = str; 43 | this.id = id; 44 | } 45 | 46 | @Test 47 | public void ensureStringNotNull() throws Exception { 48 | assertNotNull(str); 49 | } 50 | 51 | @Test 52 | public void ensureIdAtleast15() throws Exception { 53 | assertTrue("Id should be at least 15, but was " + id, id >= 15); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/DefaultParameterizationStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014-2015 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 19 | import static org.objectweb.asm.Opcodes.ACC_STATIC; 20 | import static org.objectweb.asm.Opcodes.GETSTATIC; 21 | 22 | import org.objectweb.asm.ClassVisitor; 23 | import org.objectweb.asm.commons.GeneratorAdapter; 24 | 25 | /** 26 | * Default strategy is to create a public static field "$constructorArgs" which 27 | * stores the set of parameters to run the tests with. 28 | */ 29 | public class DefaultParameterizationStrategy implements 30 | ParameterizationStrategy { 31 | 32 | private static final String FIELD_CONSTRUCTOR_ARGS = "$constructorArgs"; 33 | 34 | private String nameRaw; 35 | 36 | public void classCreationInProgress(String nameRaw, ClassVisitor cw) { 37 | this.nameRaw = nameRaw; 38 | cw.visitField(ACC_PUBLIC + ACC_STATIC, FIELD_CONSTRUCTOR_ARGS, 39 | "[Ljava/lang/Object;", null, null).visitEnd(); 40 | } 41 | 42 | public void loadConstructorArgs(GeneratorAdapter mv) { 43 | mv.visitFieldInsn(GETSTATIC, nameRaw, FIELD_CONSTRUCTOR_ARGS, 44 | "[Ljava/lang/Object;"); 45 | } 46 | 47 | public void classConstructed(Class clazz, byte[] clazzBytes, 48 | Object[] constructorArgs) { 49 | try { 50 | clazz.getField(FIELD_CONSTRUCTOR_ARGS).set(null, constructorArgs); 51 | } catch (Exception e) { 52 | throw new RuntimeException( 53 | "Error installing parameterized arguments", e); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runners/examples/WrappingParameterizedRunnerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.examples; 17 | 18 | import static org.junit.Assert.assertNotNull; 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import org.junit.Ignore; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | import org.mockito.Mock; 25 | import org.mockito.runners.MockitoJUnitRunner; 26 | 27 | import com.nitorcreations.junit.runners.parameterized.ParameterizedSuite; 28 | import com.nitorcreations.junit.runners.parameterized.ParameterizedSuiteBuilder; 29 | import com.nitorcreations.junit.runners.parameterized.WrappedRunWith; 30 | import com.nitorcreations.junit.runners.parameterized.WrappingParameterizedRunner; 31 | 32 | @RunWith(WrappingParameterizedRunner.class) 33 | @WrappedRunWith(MockitoJUnitRunner.class) 34 | public class WrappingParameterizedRunnerTest { 35 | private final String str; 36 | private final int id; 37 | 38 | @Mock 39 | private Runnable mock; 40 | 41 | @ParameterizedSuite 42 | public static void suite(ParameterizedSuiteBuilder builder) { 43 | builder.constructWith("kala", 1); 44 | builder.constructWith("foo", 42); 45 | } 46 | 47 | public WrappingParameterizedRunnerTest(String str, int id) { 48 | this.str = str; 49 | this.id = id; 50 | } 51 | 52 | @Test 53 | public void ensureStringNotNull() throws Exception { 54 | assertNotNull(str); 55 | } 56 | 57 | @Test 58 | public void ensureMockAvailable() throws Exception { 59 | assertNotNull(mock); 60 | } 61 | 62 | @Test 63 | @Ignore 64 | public void ensureIdAtleast15() throws Exception { 65 | assertTrue("Id should be at least 15, but was " + id, id >= 15); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/rules/SkipTestMethodsAfterFirstFailureRule.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.rules; 17 | 18 | import static org.junit.Assume.assumeTrue; 19 | 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.concurrent.ConcurrentMap; 22 | 23 | import org.junit.rules.TestRule; 24 | import org.junit.runner.Description; 25 | import org.junit.runners.model.Statement; 26 | 27 | /** 28 | * Fails all tests in the same test class after the first failure has been detected. 29 | * 30 | * Normally you should request deterministic ordering of test methods by annotating the test class with 31 | * {@link org.junit.FixMethodOrder}. 32 | */ 33 | public class SkipTestMethodsAfterFirstFailureRule implements TestRule { 34 | static final ConcurrentMap, String> failures = new ConcurrentHashMap, String>(); 35 | final Class testClass; 36 | 37 | public SkipTestMethodsAfterFirstFailureRule(Class testClass) { 38 | this.testClass = testClass; 39 | } 40 | 41 | @Override 42 | public Statement apply(final Statement base, final Description description) { 43 | return new Statement() { 44 | @Override 45 | public void evaluate() throws Throwable { 46 | String failedTestName = failures.get(testClass); 47 | if (failedTestName != null) { 48 | assumeTrue("Previous test '" + failedTestName + "' failed", false); 49 | } 50 | boolean ok = false; 51 | try { 52 | base.evaluate(); 53 | ok = true; 54 | } finally { 55 | if (!ok) { 56 | failures.put(testClass, description.getDisplayName()); 57 | } 58 | } 59 | } 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runner/StandaloneJUnitRunnerMainTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import static java.lang.System.setProperty; 19 | import static java.lang.System.setSecurityManager; 20 | 21 | import java.io.File; 22 | import java.security.Permission; 23 | 24 | import org.junit.After; 25 | import org.junit.Before; 26 | import org.junit.Rule; 27 | import org.junit.Test; 28 | import org.junit.rules.ExpectedException; 29 | 30 | public class StandaloneJUnitRunnerMainTest { 31 | public class StopExitSecurityManager extends SecurityManager { 32 | @Override 33 | public void checkExit(int status) { 34 | super.checkExit(status); 35 | throw new SecurityException("Exit disabled: " + status); 36 | } 37 | 38 | @Override 39 | public void checkPermission(Permission perm) { 40 | } 41 | } 42 | 43 | @Rule 44 | public ExpectedException expect = ExpectedException.none(); 45 | 46 | @Before 47 | public void disableSystemExit() { 48 | setProperty("output.dir", System.getProperty("user.dir") + File.separator + "target" + File.separator + "testrunner"); 49 | setSecurityManager(new StopExitSecurityManager()); 50 | } 51 | 52 | @After 53 | public void resetSecurityManager() { 54 | setSecurityManager(null); 55 | } 56 | 57 | @Test 58 | public void testUsage() { 59 | expect.expectMessage("Exit disabled: 1"); 60 | StandaloneJUnitRunnerMain.main(); 61 | } 62 | 63 | @Test 64 | public void testFailingTests() { 65 | expect.expectMessage("Exit disabled: 1"); 66 | StandaloneJUnitRunnerMain.main(SimpleTestNotForMaven.class.getName()); 67 | } 68 | 69 | @Test 70 | public void testSuccess() { 71 | expect.expectMessage("Exit disabled: 0"); 72 | StandaloneJUnitRunnerMain.main(SuccessTestNotForMaven.class.getName()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/DescriptionMappingRunNotifierProxy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import org.junit.runner.Description; 19 | import org.junit.runner.notification.Failure; 20 | import org.junit.runner.notification.RunListener; 21 | import org.junit.runner.notification.RunNotifier; 22 | 23 | /** 24 | * This is a helper class for {@link WrappingParameterizedRunner}. It rewrites 25 | * descriptions objects given to this instance and passes the rewritten 26 | * description objects on to the given targetNotifier instance. 27 | */ 28 | class DescriptionMappingRunNotifierProxy extends RunNotifier { 29 | DescriptionMappingRunNotifierProxy(final RunNotifier targetNotifier, 30 | final DescriptionMapper descriptionMapper) { 31 | addListener(new RunListener() { 32 | @Override 33 | public void testAssumptionFailure(Failure failure) { 34 | targetNotifier.fireTestAssumptionFailed(mapFailure(failure)); 35 | } 36 | 37 | @Override 38 | public void testFailure(Failure failure) throws Exception { 39 | targetNotifier.fireTestFailure(mapFailure(failure)); 40 | } 41 | 42 | @Override 43 | public void testFinished(Description description) throws Exception { 44 | targetNotifier.fireTestFinished(mapDescription(description)); 45 | } 46 | 47 | @Override 48 | public void testIgnored(Description description) throws Exception { 49 | targetNotifier.fireTestIgnored(mapDescription(description)); 50 | } 51 | 52 | @Override 53 | public void testStarted(Description description) throws Exception { 54 | targetNotifier.fireTestStarted(mapDescription(description)); 55 | } 56 | 57 | private Failure mapFailure(Failure failure) { 58 | return new Failure(mapDescription(failure.getDescription()), 59 | failure.getException()); 60 | } 61 | 62 | private Description mapDescription(Description orig) { 63 | return descriptionMapper.map(orig); 64 | } 65 | }); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/ClassLoaderInjector.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import java.lang.reflect.InvocationTargetException; 19 | import java.lang.reflect.Method; 20 | 21 | class ClassLoaderInjector { 22 | 23 | private static final Method DEFINE_CLASS_METHOD; 24 | static { 25 | DEFINE_CLASS_METHOD = getDefineClassMethod(); 26 | } 27 | 28 | private static Method getDefineClassMethod() { 29 | try { 30 | Method defineClassMethod = ClassLoader.class.getDeclaredMethod( 31 | "defineClass", new Class[] { String.class, byte[].class, 32 | int.class, int.class }); 33 | defineClassMethod.setAccessible(true); 34 | return defineClassMethod; 35 | } catch (NoSuchMethodException e) { 36 | throw new RuntimeException(e); 37 | } catch (SecurityException e) { 38 | throw new RuntimeException(e); 39 | } 40 | } 41 | 42 | /** 43 | * Inject a new class into the current context classloader. 44 | * 45 | * @param name 46 | * the name of the class "com.foo.Bar" 47 | * @param clazzBytes 48 | * the class in byte array format 49 | * @return a newly loaded class 50 | */ 51 | public static Class injectClass(String name, byte[] clazzBytes) { 52 | ClassLoader cl = Thread.currentThread().getContextClassLoader(); 53 | return injectClass(name, clazzBytes, cl); 54 | } 55 | 56 | /** 57 | * Inject a new class into a classloader. 58 | * 59 | * @param name 60 | * the name of the class "com.foo.Bar" 61 | * @param clazzBytes 62 | * the class in byte array format 63 | * @param classLoader 64 | * the classloader to inject the class into 65 | * @return a newly loaded class 66 | */ 67 | public static Class injectClass(String name, byte[] clazzBytes, 68 | ClassLoader classLoader) { 69 | try { 70 | return (Class) DEFINE_CLASS_METHOD.invoke(classLoader, name, 71 | clazzBytes, 0, clazzBytes.length); 72 | } catch (IllegalAccessException e) { 73 | throw new RuntimeException(e); 74 | } catch (IllegalArgumentException e) { 75 | throw new RuntimeException(e); 76 | } catch (InvocationTargetException e) { 77 | throw new RuntimeException(e); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core-utils/.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | cleanup.add_default_serial_version_id=true 2 | cleanup.add_generated_serial_version_id=false 3 | cleanup.add_missing_annotations=true 4 | cleanup.add_missing_deprecated_annotations=true 5 | cleanup.add_missing_methods=true 6 | cleanup.add_missing_nls_tags=false 7 | cleanup.add_missing_override_annotations=true 8 | cleanup.add_missing_override_annotations_interface_methods=true 9 | cleanup.add_serial_version_id=false 10 | cleanup.always_use_blocks=true 11 | cleanup.always_use_parentheses_in_expressions=false 12 | cleanup.always_use_this_for_non_static_field_access=false 13 | cleanup.always_use_this_for_non_static_method_access=false 14 | cleanup.convert_to_enhanced_for_loop=true 15 | cleanup.correct_indentation=true 16 | cleanup.format_source_code=true 17 | cleanup.format_source_code_changes_only=false 18 | cleanup.make_local_variable_final=false 19 | cleanup.make_parameters_final=false 20 | cleanup.make_private_fields_final=true 21 | cleanup.make_type_abstract_if_missing_method=false 22 | cleanup.make_variable_declarations_final=true 23 | cleanup.never_use_blocks=false 24 | cleanup.never_use_parentheses_in_expressions=true 25 | cleanup.organize_imports=true 26 | cleanup.qualify_static_field_accesses_with_declaring_class=false 27 | cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true 28 | cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true 29 | cleanup.qualify_static_member_accesses_with_declaring_class=true 30 | cleanup.qualify_static_method_accesses_with_declaring_class=false 31 | cleanup.remove_private_constructors=true 32 | cleanup.remove_trailing_whitespaces=true 33 | cleanup.remove_trailing_whitespaces_all=true 34 | cleanup.remove_trailing_whitespaces_ignore_empty=false 35 | cleanup.remove_unnecessary_casts=true 36 | cleanup.remove_unnecessary_nls_tags=true 37 | cleanup.remove_unused_imports=true 38 | cleanup.remove_unused_local_variables=false 39 | cleanup.remove_unused_private_fields=true 40 | cleanup.remove_unused_private_members=false 41 | cleanup.remove_unused_private_methods=true 42 | cleanup.remove_unused_private_types=true 43 | cleanup.sort_members=false 44 | cleanup.sort_members_all=false 45 | cleanup.use_blocks=true 46 | cleanup.use_blocks_only_for_return_and_throw=false 47 | cleanup.use_parentheses_in_expressions=false 48 | cleanup.use_this_for_non_static_field_access=true 49 | cleanup.use_this_for_non_static_field_access_only_if_necessary=true 50 | cleanup.use_this_for_non_static_method_access=true 51 | cleanup.use_this_for_non_static_method_access_only_if_necessary=true 52 | cleanup_profile=_Nitor Core 53 | cleanup_settings_version=2 54 | eclipse.preferences.version=1 55 | formatter_profile=_Nitor 56 | formatter_settings_version=12 57 | org.eclipse.jdt.ui.ignorelowercasenames=true 58 | org.eclipse.jdt.ui.importorder=java;javax;org;com; 59 | org.eclipse.jdt.ui.ondemandthreshold=99 60 | org.eclipse.jdt.ui.staticondemandthreshold=99 61 | -------------------------------------------------------------------------------- /core-utils/src/main/java/com/nitorcreations/core/utils/Templater.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.core.utils; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.io.PrintWriter; 21 | import java.util.Map.Entry; 22 | 23 | import org.apache.velocity.app.VelocityEngine; 24 | import org.apache.velocity.Template; 25 | import org.apache.velocity.VelocityContext; 26 | public class Templater { 27 | 28 | public static void main( String[] args ) { 29 | if (args.length < 1) { 30 | System.err.println("usage: Templater template"); 31 | System.exit(1); 32 | } 33 | File template = new File(args[0]); 34 | try { 35 | applyTemplate(template, new PrintWriter(System.out)); 36 | } catch (IOException e) { 37 | System.err.println("usage: Templater template"); 38 | System.err.println(" " + e.getMessage()); 39 | System.exit(1); 40 | } 41 | } 42 | 43 | public static void applyTemplate(File template, PrintWriter w) throws IOException { 44 | if (template == null) { 45 | throw new IllegalArgumentException("Template must not be null"); 46 | } 47 | if (w == null) { 48 | throw new IllegalArgumentException("Writer must not be null"); 49 | } 50 | if (!template.exists()) { 51 | throw new IOException("File " + template.getAbsolutePath() + " does not exist."); 52 | } 53 | if (!template.canRead()) { 54 | throw new IOException("File " + template.getAbsolutePath() + " is not readable."); 55 | } 56 | VelocityEngine ve = new VelocityEngine(); 57 | ve.setProperty("resource.loader", "file"); 58 | ve.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader"); 59 | ve.setProperty("file.resource.loader.path", ""); 60 | ve.init(); 61 | Template t = ve.getTemplate(template.getAbsolutePath()); 62 | VelocityContext context = new VelocityContext(); 63 | 64 | for (Entry next : System.getenv().entrySet()) { 65 | context.put(next.getKey(), next.getValue()); 66 | } 67 | 68 | for (Entry next : System.getProperties().entrySet()) { 69 | context.put((String)next.getKey(), (String)next.getValue()); 70 | } 71 | 72 | t.merge( context, w); 73 | w.flush(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runner/ThreadLocalStdOutErrCapturer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import static java.lang.System.setErr; 19 | import static java.lang.System.setOut; 20 | 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.IOException; 23 | import java.io.OutputStream; 24 | import java.io.PrintStream; 25 | import java.io.UnsupportedEncodingException; 26 | 27 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 28 | 29 | public class ThreadLocalStdOutErrCapturer { 30 | public static final ThreadLocal stdoutCapture = new CaptureStreamThreadLocal(); 31 | public static final ThreadLocal stderrCapture = new CaptureStreamThreadLocal(); 32 | 33 | public static void captureStdOutAndErrStreams() { 34 | setOut(new RedirectingPrintStream(stdoutCapture)); 35 | setErr(new RedirectingPrintStream(stderrCapture)); 36 | } 37 | 38 | static final class CaptureStreamThreadLocal extends InheritableThreadLocal { 39 | @Override 40 | protected CaptureStreams initialValue() { 41 | return new CaptureStreams(); 42 | } 43 | } 44 | 45 | public static class CaptureStreams { 46 | final ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024); 47 | final PrintStream writer; 48 | 49 | CaptureStreams() { 50 | try { 51 | writer = new PrintStream(bytes, true, "UTF-8"); 52 | } catch (UnsupportedEncodingException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | public String toString() { 58 | try { 59 | return bytes.toString("UTF-8"); 60 | } catch (UnsupportedEncodingException e) { 61 | throw new RuntimeException(e); 62 | } 63 | } 64 | } 65 | 66 | static class RedirectingPrintStream extends PrintStream { 67 | private ThreadLocal capture; 68 | 69 | @SuppressFBWarnings(value = "DM_DEFAULT_ENCODING", justification = "PrintStream encoding is never used") 70 | public RedirectingPrintStream(ThreadLocal capture) { 71 | super(new FailingOutputStream()); 72 | this.capture = capture; 73 | } 74 | 75 | @Override 76 | public void println() { 77 | capture.get().writer.println(); 78 | } 79 | 80 | @Override 81 | public void println(String x) { 82 | capture.get().writer.println(x); 83 | } 84 | 85 | @Override 86 | public void write(byte buf[], int off, int len) { 87 | capture.get().writer.write(buf, off, len); 88 | } 89 | 90 | @Override 91 | public PrintStream printf(String format, Object... args) { 92 | return capture.get().writer.printf(format, args); 93 | } 94 | } 95 | 96 | static class FailingOutputStream extends OutputStream { 97 | @Override 98 | public void write(int b) throws IOException { 99 | throw new IllegalStateException("RedirectingPrintStream did not redirect this method"); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runners/examples/ListTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.examples; 17 | 18 | import static org.hamcrest.Matchers.equalTo; 19 | import static org.hamcrest.Matchers.greaterThan; 20 | import static org.hamcrest.Matchers.is; 21 | import static org.junit.Assert.assertFalse; 22 | import static org.junit.Assert.assertThat; 23 | import static org.junit.Assert.assertTrue; 24 | 25 | import java.util.ArrayList; 26 | 27 | import org.junit.Before; 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import com.nitorcreations.junit.runners.NestedRunner; 31 | 32 | @RunWith(NestedRunner.class) 33 | public class ListTest { 34 | 35 | // inner class for sharing common context 36 | public class WithArrayList { 37 | // some context for these tests 38 | ArrayList list = new ArrayList(); 39 | 40 | public class WhenEmpty { 41 | @Test 42 | public void itIsEmpty() { 43 | assertTrue(list.isEmpty()); 44 | } 45 | 46 | public class AfterAddingAnElement { 47 | // some more context for these tests 48 | String element = "Element"; 49 | // you can use instance initializer to initialize your context 50 | // it will be run once per test 51 | { 52 | // the list is still empty in here 53 | assertTrue(list.isEmpty()); 54 | list.add(element); 55 | } 56 | 57 | @Test 58 | public void itIsNotEmpty() { 59 | assertFalse(list.isEmpty()); 60 | } 61 | 62 | @Test 63 | public void itContainsTheElement() { 64 | assertTrue(list.contains(element)); 65 | } 66 | 67 | @Test 68 | public void addingAnotherElementIncreasesSize() { 69 | int sizeBeforeAdding = list.size(); 70 | list.add("AnotherElement"); 71 | assertThat(list.size(), is(greaterThan(sizeBeforeAdding))); 72 | } 73 | 74 | @Test 75 | public void listSizeIsStillOne() { 76 | assertThat(list.size(), is(equalTo(1))); 77 | 78 | } 79 | } 80 | 81 | @Test 82 | public void isStillEmpty() { 83 | assertTrue(list.isEmpty()); 84 | } 85 | } 86 | 87 | public class WithTwoElements { 88 | @Before 89 | public void init() { 90 | list.add("Element1"); 91 | list.add("Element2"); 92 | } 93 | 94 | @Test 95 | public void hasSizeOfTwo() { 96 | assertThat(list.size(), is(equalTo(2))); 97 | } 98 | 99 | public class WithTwoMoreElements { 100 | @Before 101 | public void init() { 102 | list.add("Element1"); 103 | list.add("Element2"); 104 | } 105 | 106 | @Test 107 | public void hasSizeOfTwo() { 108 | assertThat(list.size(), is(equalTo(4))); 109 | } 110 | } 111 | } 112 | 113 | public class WithTwoAndNoParentTestsElements { 114 | @Before 115 | public void init() { 116 | list.add("Element1"); 117 | list.add("Element2"); 118 | } 119 | 120 | public class WithTwoMoreElements { 121 | @Before 122 | public void init() { 123 | list.add("Element1"); 124 | list.add("Element2"); 125 | } 126 | 127 | @Test 128 | public void hasSizeOfTwo() { 129 | assertThat(list.size(), is(equalTo(4))); 130 | } 131 | } 132 | } 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runner/StandaloneJUnitRunner.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import java.io.PrintStream; 19 | import java.util.ArrayList; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | 24 | import org.junit.runner.Description; 25 | import org.junit.runner.JUnitCore; 26 | import org.junit.runner.Result; 27 | import org.junit.runner.notification.Failure; 28 | import org.junit.runner.notification.RunListener; 29 | 30 | public class StandaloneJUnitRunner { 31 | static final PrintStream out = System.out; 32 | 33 | public void main(String[] args) { 34 | Result[] results = runTests(args); 35 | List failures = collectFailures(results); 36 | if (failures.isEmpty()) { 37 | return; 38 | } 39 | for (Failure f : failures) { 40 | out.println(f.toString()); 41 | } 42 | throw new RuntimeException("Tests Failed"); 43 | } 44 | 45 | public Result[] runTests(String... args) { 46 | Class[][] classSets = parseClassSets(args); 47 | return runTests(classSets); 48 | } 49 | 50 | public List collectFailures(Result[] results) { 51 | List failures = new ArrayList(); 52 | for (Result result : results) { 53 | if (!result.wasSuccessful()) { 54 | failures.addAll(result.getFailures()); 55 | } 56 | } 57 | return failures; 58 | } 59 | 60 | private Class[][] parseClassSets(String... args) { 61 | Set seenClassNames = new HashSet(); 62 | Class[][] classSets = new Class[args.length][]; 63 | for (int i = 0; i < args.length; ++i) { 64 | String[] classNames = args[i].split(","); 65 | classSets[i] = new Class[classNames.length]; 66 | for (int j = 0; j < classNames.length; ++j) { 67 | String className = classNames[j].trim(); 68 | if (!seenClassNames.add(className)) { 69 | throw new RuntimeException("Duplicate class name specified: " + className); 70 | } 71 | try { 72 | classSets[i][j] = Class.forName(className); 73 | } catch (Throwable t) { 74 | throw new RuntimeException("Class " + className + " not available: " + t); 75 | } 76 | } 77 | } 78 | return classSets; 79 | } 80 | 81 | private Result[] runTests(final Class[][] classSets) { 82 | Result[] results = new Result[classSets.length]; 83 | Thread[] runners = createAndStartTestThreads(classSets, results); 84 | waitForTestThreadsToFinish(runners); 85 | return results; 86 | } 87 | 88 | private Thread[] createAndStartTestThreads(final Class[][] classSets, final Result[] results) { 89 | Thread[] runners = new Thread[classSets.length]; 90 | for (int i = 0; i < classSets.length; ++i) { 91 | final int testNum = i; 92 | runners[i] = new Thread("TestRunner-" + testNum) { 93 | @Override 94 | public void run() { 95 | JUnitCore junit = createJunit(testNum); 96 | results[testNum] = junit.run(classSets[testNum]); 97 | } 98 | }; 99 | runners[i].start(); 100 | } 101 | return runners; 102 | } 103 | 104 | static JUnitCore createJunit(final int testNum) { 105 | JUnitCore junit = new JUnitCore(); 106 | junit.addListener(new RunListener() { 107 | @Override 108 | public void testStarted(Description description) throws Exception { 109 | out.printf("Thread-%d: Running: %s%n", testNum, description.getDisplayName()); 110 | } 111 | 112 | @Override 113 | public void testFinished(Description description) throws Exception { 114 | out.printf("Thread-%d: Finished: %s%n", testNum, description.getDisplayName()); 115 | } 116 | }); 117 | junit.addListener(new XmlJUnitFormatter(testNum)); 118 | return junit; 119 | } 120 | 121 | private void waitForTestThreadsToFinish(Thread[] runners) { 122 | for (Thread runner : runners) { 123 | try { 124 | runner.join(); 125 | } catch (InterruptedException e) { 126 | // ignore interruption 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/NicelyParameterized.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners; 17 | 18 | import java.lang.reflect.Modifier; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import org.junit.runner.Runner; 23 | import org.junit.runner.notification.RunNotifier; 24 | import org.junit.runners.BlockJUnit4ClassRunner; 25 | import org.junit.runners.Parameterized; 26 | import org.junit.runners.model.FrameworkMethod; 27 | import org.junit.runners.model.InitializationError; 28 | import org.junit.runners.model.Statement; 29 | import org.junit.runners.model.TestClass; 30 | 31 | public class NicelyParameterized extends Parameterized { 32 | 33 | private class TestClassRunnerForParameters extends BlockJUnit4ClassRunner { 34 | private final int fParameterSetNumber; 35 | 36 | private final List fParameterList; 37 | 38 | private String name; 39 | 40 | TestClassRunnerForParameters(Class type, 41 | List parameterList, int i) throws InitializationError { 42 | super(type); 43 | fParameterList = parameterList; 44 | fParameterSetNumber = i; 45 | } 46 | 47 | @Override 48 | public Object createTest() throws Exception { 49 | return getTestClass().getOnlyConstructor().newInstance( 50 | computeParams()); 51 | } 52 | 53 | private Object[] computeParams() throws Exception { 54 | try { 55 | return fParameterList.get(fParameterSetNumber); 56 | } catch (ClassCastException e) { 57 | throw new Exception(String.format( 58 | "%s.%s() must return a Collection of arrays.", 59 | getTestClass().getName(), 60 | getParametersMethod(getTestClass()).getName())); 61 | } 62 | } 63 | 64 | @Override 65 | protected String getName() { 66 | if (name == null) { 67 | StringBuilder sb = new StringBuilder(); 68 | boolean first = true; 69 | for (Object parameter : fParameterList.get(fParameterSetNumber)) { 70 | if (first) { 71 | first = false; 72 | } else { 73 | sb.append(", "); 74 | } 75 | if (parameter instanceof String) { 76 | parameter = '"' + (String) parameter + '"'; 77 | } 78 | sb.append(parameter); 79 | } 80 | name = fParameterSetNumber + ". " + sb; 81 | } 82 | return name; 83 | } 84 | 85 | @Override 86 | protected String testName(final FrameworkMethod method) { 87 | /* 88 | * \u0000 and anything after "accidentally" gets stripped out in the 89 | * eclipse junit view but nevertheless avoids the test name from 90 | * clashing with other tests (which causes the junit view to lose 91 | * the results for all but one of the parameter sets) 92 | */ 93 | return method.getName() + " \u0000[" + name + "] " 94 | + getTestClass().getName(); 95 | } 96 | 97 | @Override 98 | protected void validateConstructor(List errors) { 99 | validateOnlyOneConstructor(errors); 100 | } 101 | 102 | @Override 103 | protected Statement classBlock(RunNotifier notifier) { 104 | return childrenInvoker(notifier); 105 | } 106 | } 107 | 108 | private final ArrayList runners = new ArrayList(); 109 | 110 | /** 111 | * Only called reflectively. Do not use programmatically. 112 | */ 113 | public NicelyParameterized(Class klass) throws Throwable { 114 | super(klass); 115 | List parametersList = getParametersList(getTestClass()); 116 | for (int i = 0; i < parametersList.size(); i++) 117 | runners.add(new TestClassRunnerForParameters(getTestClass() 118 | .getJavaClass(), parametersList, i)); 119 | } 120 | 121 | @Override 122 | protected List getChildren() { 123 | return runners; 124 | } 125 | 126 | @SuppressWarnings("unchecked") 127 | private List getParametersList(TestClass klass) throws Throwable { 128 | return (List) getParametersMethod(klass).invokeExplosively( 129 | null); 130 | } 131 | 132 | FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { 133 | List methods = testClass 134 | .getAnnotatedMethods(Parameters.class); 135 | if (methods.isEmpty()) { 136 | throw new RuntimeException("No @Parameters method in class " 137 | + testClass.getName()); 138 | } 139 | if (methods.size() > 1) { 140 | throw new RuntimeException( 141 | "Multiple methods annotated with @Parameters. Only one method should be thus annotated per class."); 142 | } 143 | FrameworkMethod method = methods.get(0); 144 | int modifiers = method.getMethod().getModifiers(); 145 | if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { 146 | throw new RuntimeException("@Parameters method " + method.getName() 147 | + " must be declared public static in class " 148 | + testClass.getName()); 149 | } 150 | return method; 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/DescriptionMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import java.io.Serializable; 19 | import java.lang.annotation.Annotation; 20 | import java.lang.reflect.Constructor; 21 | import java.lang.reflect.Field; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | import org.junit.runner.Description; 26 | 27 | /** 28 | * This is a helper class for {@link WrappingParameterizedRunner}. It rewrites 29 | * descriptions such that they use the original test class instead of the 30 | * dynamically created (synthesized) extending test class so that test reports 31 | * refer back to the original test class. It also remembers the mappings done so 32 | * they can be re-applied later when the actual test runs report success or 33 | * failure. The {@link Description#equals(Object)} and 34 | * {@link Description#hashCode()} implementations suitably support using the 35 | * Description objects themselves directly as map keys. 36 | */ 37 | public class DescriptionMapper { 38 | private final Map originalToRewrittenDescriptionMap = new HashMap(); 39 | private final Class testClass; 40 | private final Constructor descConstr; 41 | private final Field uniqueIdField; 42 | private final Field testClassField; 43 | 44 | public DescriptionMapper(Class testClass) { 45 | this.testClass = testClass; 46 | 47 | // The Description class is mutation-hostile, so just pull the rug and 48 | // go all reflective on it. 49 | try { 50 | descConstr = Description.class.getDeclaredConstructor(Class.class, 51 | String.class, Serializable.class, Annotation[].class); 52 | uniqueIdField = Description.class.getDeclaredField("fUniqueId"); 53 | testClassField = Description.class.getDeclaredField("fTestClass"); 54 | } catch (NoSuchMethodException e) { 55 | throw new RuntimeException(e); 56 | } catch (SecurityException e) { 57 | throw new RuntimeException(e); 58 | } catch (NoSuchFieldException e) { 59 | throw new RuntimeException(e); 60 | } 61 | descConstr.setAccessible(true); 62 | uniqueIdField.setAccessible(true); 63 | testClassField.setAccessible(true); 64 | } 65 | 66 | /** 67 | * Rewrites the description of a first-level runner child of the 68 | * WrappingParameterizedRunner runner, and recursively its children. 69 | * Replaces the original description with the given description identifying 70 | * the child and the test class with the one given to this class' 71 | * constructor. The child's children descriptions are just rewritten to use 72 | * the test class given in constructor instead of direct subclass, where 73 | * applicable. 74 | * 75 | * @param description 76 | * the new description text to use for this child 77 | * @param orig 78 | * the original description object provided by the child runner. 79 | * @return the rewritten description object 80 | */ 81 | public Description rewriteDescription(String description, Description orig) { 82 | return createDescription(testClass, description, orig); 83 | } 84 | 85 | private Description rewriteIndirectChildDescription(Description orig) { 86 | Class childTestClass = getTestClassFromDescription(orig); 87 | String displayName = orig.getDisplayName(); 88 | 89 | if (childTestClass.getSuperclass() == testClass) { 90 | displayName = displayName.replace(childTestClass.getName(), 91 | testClass.getName()); 92 | childTestClass = testClass; 93 | } 94 | 95 | return createDescription(childTestClass, displayName, orig); 96 | } 97 | 98 | private Description createDescription(Class testClass, 99 | String displayName, Description orig) { 100 | Serializable uniqueId = getUniqueIdFromDescription(orig); 101 | Annotation[] annotations = orig.getAnnotations().toArray( 102 | new Annotation[0]); 103 | 104 | Description desc; 105 | try { 106 | desc = descConstr.newInstance(testClass, displayName, uniqueId, 107 | annotations); 108 | } catch (Throwable t) { 109 | throw new RuntimeException(t); 110 | } 111 | 112 | for (Description child : orig.getChildren()) { 113 | desc.addChild(rewriteIndirectChildDescription(child)); 114 | } 115 | 116 | originalToRewrittenDescriptionMap.put(orig, desc); 117 | return desc; 118 | } 119 | 120 | private Class getTestClassFromDescription(Description orig) { 121 | Class origTestClass; 122 | try { 123 | origTestClass = (Class) testClassField.get(orig); 124 | } catch (IllegalArgumentException e) { 125 | throw new RuntimeException(e); 126 | } catch (IllegalAccessException e) { 127 | throw new RuntimeException(e); 128 | } 129 | return origTestClass; 130 | } 131 | 132 | private Serializable getUniqueIdFromDescription(Description desc) { 133 | try { 134 | return (Serializable) uniqueIdField.get(desc); 135 | } catch (IllegalArgumentException e) { 136 | throw new RuntimeException(e); 137 | } catch (IllegalAccessException e) { 138 | throw new RuntimeException(e); 139 | } 140 | } 141 | 142 | /** 143 | * This is used to map "original" descriptions again in the same manner as 144 | * when they were rewritten earlier using 145 | * {@link #rewriteDescription(String, Description)}. Any description object 146 | * given to the {@link #rewriteDescription(String, Description)} or any 147 | * child, child's child etc inside it can be supplied to this method. 148 | * 149 | * @param orig 150 | * the original description object 151 | * @return the associated description as it was rewritten when calling 152 | * {@link #rewriteDescription(String, Description)}. 153 | */ 154 | public Description map(Description orig) { 155 | Description desc = originalToRewrittenDescriptionMap.get(orig); 156 | assert desc != null : 157 | "WrappingParameterizedRunner: Internal error"; 158 | return desc; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /junit-runners/README.md: -------------------------------------------------------------------------------- 1 | ============= 2 | JUnit Runners 3 | ============= 4 | 5 | JUnit test runners for various purposes. 6 | 7 | - NestedRunner for running plain old Java classes in nested configuration. 8 | - WrappingParameterizedRunner for parameterized tests. 9 | 10 | StandaloneJUnitRunner 11 | - Avoids ant/maven/etc dependency when running your tests and generating standard xml reports. 12 | - Built-in support for threads and explicit control and how tests assigned to them 13 | - Supports packaging your integration tests and dependencies to a single executable jar and run it anywhere. 14 | 15 | Installation 16 | ============ 17 | 18 | JUnit Runners is available in [Maven Central](http://search.maven.org/#artifactdetails%7Ccom.nitorcreations%7Cjunit-runners%7C1.3%7Cjar) so you can include it to your Maven project by adding this to `pom.xml`. 19 | 20 | ```xml 21 | 22 | com.nitorcreations 23 | junit-runners 24 | 1.3 25 | test 26 | 27 | ``` 28 | 29 | NestedRunner 30 | ============ 31 | 32 | Example 33 | ------- 34 | ```java 35 | @RunWith(NestedRunner.class) 36 | public class ListTest { 37 | 38 | // inner class for sharing common context 39 | public class WithArrayList { 40 | // some context for these tests 41 | ArrayList list = new ArrayList(); 42 | 43 | public class WhenEmpty { 44 | @Test 45 | public void itIsEmpty() { 46 | assertTrue(list.isEmpty()); 47 | } 48 | 49 | public class AfterAddingAnElement { 50 | // some more context for these tests 51 | String element = "Element"; 52 | // you can use instance initializer to initialize your context 53 | // it will be run once per test 54 | { 55 | // the list is still empty in here 56 | assertTrue(list.isEmpty()); 57 | list.add(element); 58 | } 59 | 60 | @Test 61 | public void itIsNotEmpty() { 62 | assertFalse(list.isEmpty()); 63 | } 64 | 65 | @Test 66 | public void itContainsTheElement() { 67 | assertTrue(list.contains(element)); 68 | } 69 | 70 | @Test 71 | public void addingAnotherElementIncreasesSize() { 72 | int sizeBeforeAdding = list.size(); 73 | list.add("AnotherElement"); 74 | assertThat(list.size(), is(greaterThan(sizeBeforeAdding))); 75 | } 76 | 77 | @Test 78 | public void listSizeIsStillOne() { 79 | assertThat(list.size(), is(equalTo(1))); 80 | } 81 | } 82 | 83 | @Test 84 | public void isStillEmpty() { 85 | assertTrue(list.isEmpty()); 86 | } 87 | } 88 | 89 | public class WithTwoElements { 90 | @Before 91 | public void init() { 92 | list.add("Element1"); 93 | list.add("Element2"); 94 | } 95 | 96 | @Test 97 | public void hasSizeOfTwo() { 98 | assertThat(list.size(), is(equalTo(2))); 99 | } 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | WrappingParameterizedRunner 106 | =========================== 107 | 108 | See [wiki documentation](https://github.com/NitorCreations/CoreComponents/wiki/WrappingParameterizedRunner) 109 | 110 | NicelyParameterized 111 | ------------------- 112 | Deprecated. Use WrappingParameterizedRunner (above) instead. 113 | 114 | StandaloneJUnitRunner 115 | ===================== 116 | 117 | Usage 118 | ----- 119 | Each main method argument is a comma separated list of junit testclass names to run in it's own thread. 120 | For example a command line of 121 | ```text 122 | Class1 ClassA,ClassB Class2 123 | ``` 124 | Will start 3 threads, where ClassA and ClassB are run in sequence in one thread and Class1 and Class2 in their own threads. 125 | 126 | Building a standalone test runner 127 | --------------------------------- 128 | Add all your tests to src/main/java and configure the pom.xml to generate a jar file with dependencies. 129 | 130 | ```xml 131 | 132 | com.nitorcreations 133 | junit-runners 134 | 1.2 135 | 136 | ... 137 | 138 | maven-shade-plugin 139 | 2.2 140 | 141 | 142 | package 143 | 144 | shade 145 | 146 | 147 | ${project.artifactId} 148 | 149 | 150 | *:* 151 | 152 | META-INF/*.SF 153 | META-INF/*.DSA 154 | META-INF/*.RSA 155 | 156 | 157 | 158 | false 159 | false 160 | 161 | 162 | 163 | com.nitorcreations.junit.runner.StandaloneJUnitRunnerMain 164 | 165 | 166 | META-INF/spring.handlers 167 | 168 | 169 | META-INF/spring.schemas 170 | 171 | 172 | 173 | 174 | 175 | 176 | ``` 177 | 178 | TODO 179 | ==== 180 | 181 | - Multiple runner runner 182 | - Moar tests! 183 | 184 | License 185 | ======= 186 | 187 | [Apache License 2.0](../../blob/master/junit-runners/LICENSE) 188 | -------------------------------------------------------------------------------- /core-utils/src/main/java/com/nitorcreations/core/utils/KillProcess.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.core.utils; 17 | 18 | import static java.lang.Integer.parseInt; 19 | import static java.lang.System.err; 20 | import static java.lang.System.getProperty; 21 | import static java.nio.charset.Charset.defaultCharset; 22 | import static java.nio.file.Files.newDirectoryStream; 23 | import static java.util.Arrays.asList; 24 | import static java.util.concurrent.TimeUnit.SECONDS; 25 | 26 | import java.io.BufferedReader; 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.InputStreamReader; 30 | import java.nio.file.DirectoryStream; 31 | import java.nio.file.Path; 32 | import java.nio.file.Paths; 33 | import java.util.List; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | 37 | public class KillProcess { 38 | private static final String os = getProperty("os.name"); 39 | 40 | public static void main(String... args) { 41 | gracefullyTerminateOrKillProcessUsingPort(parseInt(args[0]), args.length > 1 ? parseInt(args[1]) : 0, true); 42 | } 43 | 44 | public static void killProcessUsingPort(final int port) { 45 | gracefullyTerminateOrKillProcessUsingPort(port, 0, false); 46 | } 47 | 48 | public static void gracefullyTerminateOrKillProcessUsingPort(final int port, int terminateWaitSeconds, boolean threadDump) { 49 | try { 50 | String pid = getProcessPid(port); 51 | if (pid == null) { 52 | return; 53 | } 54 | if (threadDump) { 55 | threadDumpProcess(pid); 56 | } 57 | if (terminateWaitSeconds > 0) { 58 | err.println("Terminating process " + pid + " that was using the required listen port " + port); 59 | termProcess(pid); 60 | for (int i=0; i ds = newDirectoryStream(Paths.get("/proc"))) { 84 | for (Path p : ds) { 85 | String pid = p.getName(p.getNameCount() - 1).toString(); 86 | Matcher matcher = matchProcessOutput(pattern, "pfiles", pid); 87 | if (matcher != null) { 88 | return pid; 89 | } 90 | } 91 | } 92 | } else { 93 | // windows 94 | Pattern pattern = Pattern.compile("TCP.*:" + port + " .*LISTENING ([0-9]+)"); 95 | Matcher matcher = matchProcessOutput(pattern, "netstat", "-ano"); 96 | if (matcher != null) { 97 | return matcher.group(1); 98 | } 99 | } 100 | return null; 101 | } 102 | 103 | private static boolean macOrLinux() { 104 | return "Linux".equals(os) || "Mac OS X".equals(os); 105 | } 106 | 107 | private static boolean solaris() { 108 | return "SunOS".equals(os); 109 | } 110 | 111 | public static void termProcess(final String pid) throws IOException, InterruptedException { 112 | new ProcessBuilder(getTermCommand(pid)).start().waitFor(); 113 | } 114 | 115 | public static void killProcess(final String pid) throws IOException, InterruptedException { 116 | new ProcessBuilder(getKillCommand(pid)).start().waitFor(); 117 | } 118 | 119 | public static void threadDumpProcess(final String pid) throws IOException, InterruptedException { 120 | List cmd = getThreadDumpCommand(pid); 121 | if (cmd != null) { 122 | new ProcessBuilder(cmd).start().waitFor(); 123 | } 124 | } 125 | 126 | private static List getTermCommand(final String pid) { 127 | if (macOrLinux() || solaris()) { 128 | return asList("kill", "-TERM", pid); 129 | } 130 | // windows 131 | return asList("taskkill", "/pid", pid, "/t"); 132 | } 133 | 134 | private static List getKillCommand(final String pid) { 135 | if (macOrLinux() || solaris()) { 136 | return asList("kill", "-KILL", pid); 137 | } 138 | // windows 139 | return asList("taskkill", "/pid", pid, "/t", "/f"); 140 | } 141 | 142 | private static List getThreadDumpCommand(final String pid) { 143 | if (macOrLinux() || solaris()) { 144 | return asList("kill", "-QUIT", pid); 145 | } 146 | return null; 147 | } 148 | 149 | private static Matcher matchProcessOutput(Pattern pattern, String... args) throws IOException { 150 | Process process = new ProcessBuilder(args).start(); 151 | try { 152 | return getMatcher(process.getInputStream(), pattern); 153 | } finally { 154 | process.destroy(); 155 | } 156 | } 157 | 158 | private static Matcher getMatcher(final InputStream is, final Pattern pattern) throws IOException { 159 | try (InputStreamReader stream = new InputStreamReader(is, defaultCharset()); BufferedReader reader = new BufferedReader(stream)) { 160 | String line = reader.readLine(); 161 | while (line != null) { 162 | Matcher matcher = pattern.matcher(line); 163 | if (matcher.find()) { 164 | return matcher; 165 | } 166 | line = reader.readLine(); 167 | } 168 | return null; 169 | } finally { 170 | is.close(); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/license.pm: -------------------------------------------------------------------------------- 1 | # Copyright 2013, 2015 Nitor Creations Oy, Jonas Berlin 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | package My::License; 16 | 17 | use strict; 18 | use warnings; 19 | 20 | BEGIN { 21 | use Exporter (); 22 | our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); 23 | 24 | $VERSION = 1.00; 25 | @ISA = qw(Exporter); 26 | @EXPORT = qw(isLackingProperLicense maintainLicense); # default export list 27 | #@EXPORT_OK = qw(isLackingLicense addOrUpdateLicense); # exported if qw(function) 28 | #%EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ], 29 | } 30 | our @EXPORT_OK; 31 | 32 | our $YEARS_CAPTURE_GROUP = 'year'; 33 | our $AUTHORS_CAPTURE_GROUP = 'author'; 34 | 35 | my $EMPTY_LINE_AFTER_HASHBANG = 1; 36 | 37 | my %licenseTextCache; # filename => contents 38 | 39 | sub _getLicenseText { 40 | my $license_text_file = $_[0]; 41 | my $license = $licenseTextCache{$license_text_file}; 42 | unless (defined($license)) { 43 | open F, '<', $license_text_file or die 'Could not read license file '.$license_text_file; 44 | my $sep = $/; 45 | undef $/; 46 | $license = ; 47 | $/ = $sep; 48 | close F; 49 | $licenseTextCache{$license_text_file} = $license; 50 | } 51 | return $license; 52 | } 53 | 54 | # transform license into a regexp that matches an existing license 55 | # block ignoring whitespace and with "YEAR" changed to the appropriate 56 | # regexp 57 | # in: "# Copyright YEAR Company Ltd\n\n" out: "\s*# Copyright\s+(\d{4}(?:\s*-\s*\d{4})?)\s+Company\s+Ltd\s*" 58 | sub regexpify_license { 59 | my ($license) = @_ or die; 60 | $license =~ s!^\s+!!mg; $license =~ s!\s+$!!mg; # remove heading & trailing whitespace on each line 61 | $license =~ s{^(?:\h*\v)+}{}s; $license =~ s{(?:\v\h*)+$}{}s; # remove heading & trailing empty lines 62 | my @parts = split(/(\s+|YEAR|AUTHORS)/, $license); 63 | push @parts, ''; # avoid having to handle final-iteration special cases in for loop 64 | my $regexp = '\s*'; # compensate for previously removed heading empty lines & whitespace 65 | for(my $i=0; $i<$#parts; $i+=2) { 66 | my $verbatim = $parts[$i]; # normal non-whitespace text that is supposed to exist as-is 67 | $regexp .= quotemeta($verbatim); 68 | 69 | my $special = $parts[$i+1]; # empty, whitespace or "YEAR" which are replaced with regexps 70 | if ($special eq 'YEAR') { 71 | # accept any sensibly formatted set of years and/or year ranges, ignoring whitespace 72 | my $year_or_year_range_regexp = '\d{4}(?:\s*-\s*\d{4})?'; 73 | $special = '(?<'.$YEARS_CAPTURE_GROUP.'>'.$year_or_year_range_regexp.'(?:\s*,\s*'.$year_or_year_range_regexp.')*)'; 74 | } elsif ($special eq 'AUTHORS') { 75 | # accept any sensibly formatted set of authors, ignoring whitespace 76 | my $author_regexp = '\w[^\r\n,]*\w'; 77 | $special = '(?<'.$AUTHORS_CAPTURE_GROUP.'>'.$author_regexp.'(?:\s*,\s*'.$author_regexp.')*)'; 78 | } elsif(length($special)) { 79 | $special = '\s+'; # instead of exact sequence of whitespace characters accept any amount of whitespace 80 | } 81 | $regexp .= $special; 82 | } 83 | $regexp .= '\s*'; # compensate for previously removed trailing empty lines & whitespace 84 | return $regexp; 85 | } 86 | 87 | # in: "2005, 2007-2009, 2012" out: ( 2005=>1, 2007=>1, 2008=>1, 2009=>1, 2012=>1 ) 88 | sub unpack_ranges { 89 | my $years_str = $_[0]; 90 | my @year_ranges = split(/\s*,\s*/,$years_str); 91 | my %years; 92 | for (my $i=0; $i<=$#year_ranges; ++$i) { 93 | my $year_range = $year_ranges[$i]; 94 | my $low_year; 95 | my $high_year; 96 | if ($year_range =~ m!(\d{4})\s*-\s*(\d{4})!) { 97 | $low_year = $1; 98 | $high_year = $2; 99 | } else { 100 | $low_year = $year_range; 101 | $high_year = $year_range; 102 | } 103 | for (my $y=$low_year; $y<=$high_year; ++$y) { 104 | $years{$y} = 1; 105 | } 106 | } 107 | return %years; 108 | } 109 | 110 | # in: ( 2005=>1, 2007=>1, 2008=>1, 2009=>1, 2012=>1 ) out: "2005, 2007-2009, 2012" 111 | sub pack_ranges { 112 | my %years = @_; 113 | my @years = sort (keys %years, 9999); # 9999 -> avoid having to handle final-iteration special case in for loop 114 | my @year_ranges = (); 115 | for (my $i=0; $i<$#years; ) { 116 | my $j; 117 | for ($j=1; $i+$j<$#years; ++$j) { 118 | last if($years[$i]+$j != $years[$i+$j]); 119 | } 120 | push @year_ranges, $j == 1 ? $years[$i] : $years[$i].'-'.($years[$i]+$j-1); 121 | $i += $j; 122 | } 123 | return join(", ", @year_ranges); 124 | } 125 | 126 | sub unpack_authors { 127 | return split(/\s*,\s*/, $_[0]); 128 | } 129 | 130 | sub pack_authors { 131 | return join(", ", grep { defined($_) && length($_) } @_); 132 | } 133 | 134 | sub _execute($$$$$$$) { 135 | my ($license_text_file, $source_file, $contents, $author, $add_author_only_if_no_authors_listed, $author_years, $dry_run) = @_; 136 | 137 | my $license = _getLicenseText($license_text_file); 138 | 139 | # check for possible hashbang line and temporarily detach it 140 | 141 | my $hashbang = ''; 142 | if ($contents =~ s{^(#!\V+\v)(?:\h*\v)*}{}s) { 143 | $hashbang = $1; 144 | if ($EMPTY_LINE_AFTER_HASHBANG) { 145 | $hashbang .= "\n"; 146 | } 147 | } 148 | 149 | # create regexp version of license for relaxed detection of existing license 150 | 151 | my $license_regexp = regexpify_license($license); 152 | 153 | # check for possibly existing license and remove it 154 | 155 | my $years_str; 156 | my $authors_str; 157 | if ($contents =~ s!^$license_regexp!!s) { # this removes the license as a side effect 158 | # license present, construct new $years_str based on currently mentioned years combined with provided list of years, and list of authors merged with provided author 159 | return 0 if($dry_run); 160 | 161 | my $old_years_str = $+{$YEARS_CAPTURE_GROUP}; 162 | my $old_authors_str = $+{$AUTHORS_CAPTURE_GROUP}; 163 | 164 | my %years = unpack_ranges($old_years_str); 165 | foreach my $author_year (keys %{$author_years}) { 166 | $years{$author_year} = 1; # add year to set if not yet there 167 | } 168 | $years_str = pack_ranges(%years); 169 | 170 | my @authors = unpack_authors($old_authors_str); 171 | my %authors = map { $_ => 1 } @authors; 172 | if (defined($authors{$author}) || ($#authors >= 0 && $add_author_only_if_no_authors_listed)) { 173 | $authors_str = $old_authors_str; 174 | } else { 175 | push @authors, $author; 176 | $authors_str = pack_authors(@authors); 177 | } 178 | 179 | } else { 180 | # full license not present - see if any single line of license is 181 | # present, in which case someone broke the header accidentally 182 | my @license_line_regexps = map { regexpify_license($_) } grep { m![a-zA-Z]! } split("\n", $license); 183 | foreach my $license_line_regexp (@license_line_regexps) { 184 | if ($contents =~ m!^$license_line_regexp$!m) { 185 | print STDERR "ERROR: License header broken in ",$source_file," - please fix manually\n"; 186 | return 1; 187 | } 188 | } 189 | 190 | # no license - new list of years is just provided list of years, and list of authors is just provided author 191 | return 2 if($dry_run); 192 | $years_str = pack_ranges(%{$author_years}); 193 | $authors_str = $author; 194 | } 195 | 196 | # format new license 197 | 198 | my $newlicense = $license; 199 | $newlicense =~ s!YEAR!$years_str!g; 200 | $newlicense =~ s!AUTHORS!$authors_str!g; 201 | 202 | # output 203 | 204 | return 0, $hashbang, $newlicense, $contents; 205 | } 206 | 207 | sub isLackingProperLicense($$$) { 208 | my ($license_text_file, $source_file, $contents) = @_; 209 | return _execute($license_text_file, $source_file, $contents, undef, 0, undef, 1); 210 | } 211 | 212 | sub maintainLicense($$$$$) { 213 | my ($license_text_file, $source_file, $contents, $author, $add_author_only_if_no_authors_listed, $author_years) = @_; 214 | return _execute($license_text_file, $source_file, $contents, $author, $add_author_only_if_no_authors_listed, $author_years, 0); 215 | } 216 | 217 | 1; 218 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/README.md: -------------------------------------------------------------------------------- 1 | # license-maintainer 2 | Maintains copyright/license preamble in source files etc in your project. 3 | 4 | This project provides a tool that hooks into the `git commit` process to maintain copyright/license preamble in the files in your project. 5 | 6 | When committing files, the tool inspects the files you are about to commit to see if an expected license header is present. If there is no license header, it is added, and if it already exists, it updates it to make sure that: 7 | 8 | * the license is formatted properly 9 | * the copyright year list contains the current year 10 | * the list of organizations/individuals holding the copyright contains the origanization/individual represented by the user of the git checkout 11 | 12 | Additionally, it also scans files that are not about to be committed, and warns if there are any files that don't contain the expected license. The warning includes instructions how to automatically add licenses to those files as a separate commit. 13 | 14 | You can freely configure which files (using wildcards) should contain a license, and in what format the license should be presented using license template files. The license template files may contain the dynamic variables AUTHORS and YEARS that are then updated on each commit. Example Apache 2.0 license templates are included in a few different presentations. 15 | 16 | In the suggested default configuration, the tool is imported to be a part of your project's source tree, and can be updated at will from this (or your own forked version of this) repository. 17 | 18 | # Requirements 19 | 20 | * Perl 5 21 | * Bash (for automatic install, optional) 22 | 23 | Honestly I have only tested this on Linux so far. Please file any issues you have with any operating system you end up using this tool in. 24 | 25 | # Overview of the relevant part of this repository 26 | 27 | * `.githooks/` 28 | * `.githooks/license-maintainer/` 29 | * `.githooks/license-maintainer/README.md` - this file 30 | * `.githooks/license-maintainer/LICENSE` - license by which the license-maintainer is distributed 31 | * `.githooks/license-maintainer/install` - script for automatic install 32 | * `.githooks/license-maintainer/pre-commit` - entry point for "pre-commit" git hook, has the git-specific parts 33 | * `.githooks/license-maintainer/license.pm` - perl module for adding & updating license in a single file at a time 34 | * `.githooks/license-maintainer/.gitattributes` - license maintenance configuration for the license maintainer itself 35 | * `.githooks/license-maintainer/LICENSE-hash` - sample (Apache 2.0) license file formatted for inclusion in files using `#` for end-of-line comments 36 | * `.githooks/license-maintainer/LICENSE-javadoc` - sample (Apache 2.0) license file formatted for inclusion with javadoc style comments 37 | 38 | # Importing the license maintainer into your project 39 | 40 | * **one-shot**: If you want, you can just copy the `.githooks/license-maintainer` directory into the same location in your project. Jump over the rest of this section and continue from the "Enabling the license maintainer in a git repository" section after copying the directory. 41 | * **the git way**: I recommend importing it using git itself, which will allow you to update it easily later, should you want to. Continue with the instructions below. 42 | 43 | The "master" branch of this project contains both the .githooks/license-maintainer directory (containing the files you want) and some github-required files. But for your own project you want just the .githooks/license-maintainer directory. So for easy deployment into your project, there is a separate "hooks-only" branch which contains just the .githooks/license-maintainer directory. 44 | 45 | ## Adding the license-maintainer repository as a remote repository in your project 46 | 47 | So, we add a git remote called `githooks-license-maintainer` for the hooks-only branch from this repository. 48 | 49 | git remote add --no-tags -t hooks-only githooks-license-maintainer https://github.com/NitorCreations/license-maintainer 50 | 51 | # or if you have a github account set up: 52 | 53 | git remote add --no-tags -t hooks-only githooks-license-maintainer git@github.com:NitorCreations/license-maintainer.git 54 | 55 | ## Importing / updating the license-maintainer code into your project 56 | 57 | To import and later update the license-maintainer code in your project, execute these commands in your "master" branch of your project: 58 | 59 | git fetch githooks-license-maintainer 60 | git merge githooks-license-maintainer/hooks-only 61 | 62 | Now you have the code in your repository. The next step is to enable it. 63 | 64 | # Enabling the license maintainer in a git repository 65 | 66 | Using the the license maintainer is an opt-in procedure that everybody using your repository (or rather every clone of the repository) will need to do once in order for the license maintainer to do its work. 67 | 68 | There are two ways to do it, automatic or manual. If you don't have any other git hooks in your project, the automatic installation will do fine. However if you already have git hooks in use, you might want to manually integrate the license maintainer with your existing hooks. 69 | 70 | ## Automatic install 71 | 72 | To automatically enable the license maintainer in your git checkout, run the following command in the base directory of your project. 73 | 74 | .githooks/license-maintainer/install 75 | 76 | This will create a symbolic link from .git/hooks/pre-commit to .githooks/license-maintainer/pre-commit 77 | 78 | ## Manual install 79 | 80 | If you already have a `pre-commit` hook in use, you just need to call the `.githooks/license-maintainer/pre-commit` script at some point during the execution of your `pre-commit` script (perhaps preferrably as late as possible), and make sure that your script also exits with failure if the license-maintainer pre-commit script exits with failure when you call it. 81 | 82 | # Configuration 83 | 84 | Now we configure which files should have automatic copyright/license maintenance, and which template of copyright/license should be used for each. 85 | 86 | Configuration is done using the "gitattributes" mechanism, which is similar to gitignore. See `gitattributes(5)` manual page for details. 87 | 88 | The license maintainer includes its own configuration for license maintenance in `.githooks/license-maintainer/.gitattributes`. That is the only maintenance done by default. To enable license maintaintenance for files in your project, you specify the license template file to use for a file, or a filename pattern. First create a `.gitattributes` file, for example in the root directory of your repository. Then, for each file or pattern, specify the license using the "licensefile" attribute, e.g. with lines like: 89 | 90 | *.java licensefile=.gitattributes/LICENSE-javadoc 91 | *.sh licensefile=.gitattributes/LICENSE-hash 92 | /install licensefile=.gitattributes/LICENSE-hash 93 | 94 | This will enable maintenance of all `*.java` files in the project with the `LICENSE-javadoc` license template and all `*.sh` files with the `LICENSE-hash` license template. The `install` file in the root directory of the repository also uses the `LICENSE-hash` template. 95 | 96 | Patterns starting with / are effective only in the directory where the `.gitattributes` file resides, and patterns without are effective also in subdirectories recursively. Again, see the `gitattributes(5)` manual page for more information. 97 | 98 | If you want to inhibit license maintenance for some specific file/pattern that otherwise would have license maintenance (by an earlier pattern match for example), you can specify `!licensefile` for that file to remove the setting. 99 | 100 | If you want to verify that the correct license templates will be used for each file, you can do that with the following command: 101 | 102 | git ls-files | git check-attr licensefile --stdin | sed -e 's!licensefile: !!' -e 's!un\(set\|specified\)$!-!' 103 | 104 | The example `LICENSE-*` template files included in the `.gitattributes/` directory are based on the Apache 2.0 license that the license maintainer itself is licensed under. You can freely use them or create your own template files anywhere in your repository with the license(s) you want to use for your project. 105 | 106 | # Temporarily avoiding license maintenance 107 | 108 | You can skip the maintenance of licenses for a single commit if you want. Just add the `-n` or `--no-verify` to disable processing of hooks. Example: 109 | 110 | git commit -n 111 | 112 | # License 113 | 114 | The license-maintainer is distributed under the Apache 2.0 license. You may embed it in your project as long as you comply with the license for the files belonging to license-maintainer. 115 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/PowermockParameterizationStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014-2015 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 19 | import static org.objectweb.asm.Opcodes.ACC_STATIC; 20 | import static org.objectweb.asm.Opcodes.ACC_SUPER; 21 | import static org.objectweb.asm.Opcodes.ALOAD; 22 | import static org.objectweb.asm.Opcodes.DUP; 23 | import static org.objectweb.asm.Opcodes.GETSTATIC; 24 | import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 25 | import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 26 | import static org.objectweb.asm.Opcodes.NEW; 27 | import static org.objectweb.asm.Opcodes.POP; 28 | import static org.objectweb.asm.Opcodes.RETURN; 29 | import static org.objectweb.asm.Opcodes.V1_5; 30 | 31 | import org.objectweb.asm.AnnotationVisitor; 32 | import org.objectweb.asm.ClassVisitor; 33 | import org.objectweb.asm.ClassWriter; 34 | import org.objectweb.asm.MethodVisitor; 35 | import org.objectweb.asm.Type; 36 | import org.objectweb.asm.commons.GeneratorAdapter; 37 | import org.powermock.core.classloader.annotations.PowerMockIgnore; 38 | 39 | /** 40 | * Explicit support for PowerMock. 41 | *

42 | * PowerMock loads test classes using javassist's ClassPool mechanism, which 43 | * will not see the classes we inject into the context classloader. So instead 44 | * we amend PowerMock's classpath with a ByteArrayClassPath that loads the 45 | * testclass in question, only. 46 | *

47 | * To achieve this: 48 | *

    49 | *
  • Create a helper class that implements PowerMock's @ 50 | * {@link org.powermock.core.classloader.ClassPathAdjuster} 51 | * interface.
  • 52 | *
  • Store both the testclass bytecode (as an byte array) and the constructor 53 | * arguments in public static fields in the helper class.
  • 54 | *
  • Tell PowerMock to amend its classpath to include our helper class using 55 | * the @{@link org.powermock.core.classloader.annotations.UseClassPathAdjuster} 56 | * annotation
  • 57 | *
  • Also exempt the package of the helper class using @ 58 | * {@link PowerMockIgnore} on the test class.
  • 59 | *
  • When PowerMock calls our helper class, we add a new @ 60 | * {@link javassist.ByteArrayClassPath} instance initialized to load the test 61 | * class from the byte array.
  • 62 | *
  • When PowerMock has loaded our test class and constructs it, fetch the 63 | * constructor arguments for the superclass from the public static field in the 64 | * helper class.
  • 65 | *
66 | *

67 | * The ClassPathAdjuster interface and the UseClassPathAdjuster annotation 68 | * were contributed to PowerMock in order to unlock this stunt. :) 69 | */ 70 | public class PowermockParameterizationStrategy implements 71 | ParameterizationStrategy { 72 | 73 | private static final String PACKAGE_FOR_POWERMOCK_AVOIDANCE_RAW = "com/nitorcreations/nomocks"; 74 | 75 | private static final String POWERMOCK_HELPER_PREFIX = "PowerMockHelper"; 76 | 77 | private static final String FIELD_TEST_CLASS_BYTE_ARR = "testClassByteArr"; 78 | 79 | private static final String FIELD_CONSTRUCTOR_ARGS = "constructorArgs"; 80 | 81 | private static final String CLASSPATH_ADJUSTER_CLASS_INTERNALNAME = "org/powermock/core/classloader/ClassPathAdjuster"; 82 | private static final String USE_CLASSPATH_ADJUSTER_CLASS_DESCRIPTOR = "Lorg/powermock/core/classloader/annotations/UseClassPathAdjuster;"; 83 | private static final String CLASSPATH_ADJUSTER_CLASSNAME = CLASSPATH_ADJUSTER_CLASS_INTERNALNAME 84 | .replace('/', '.'); 85 | 86 | private static String getPowerMockHelperClassRaw(String nameRaw) { 87 | String powerMockHelperClassRaw = PACKAGE_FOR_POWERMOCK_AVOIDANCE_RAW 88 | + '/' + POWERMOCK_HELPER_PREFIX + '_' 89 | + nameRaw.replace('/', '_').replace('$', '_'); 90 | return powerMockHelperClassRaw; 91 | } 92 | 93 | private String nameRaw; 94 | private String powerMockHelperClassRaw; 95 | 96 | public PowermockParameterizationStrategy() { 97 | try { 98 | Class.forName(CLASSPATH_ADJUSTER_CLASSNAME); 99 | } catch (ClassNotFoundException e) { 100 | throw new ParameterizationStrategyNotAvailableException( 101 | "Your version of PowerMock is too old - version 1.5.5 or later required"); 102 | } 103 | } 104 | 105 | public void classCreationInProgress(String nameRaw, ClassVisitor cw) { 106 | this.nameRaw = nameRaw; 107 | this.powerMockHelperClassRaw = getPowerMockHelperClassRaw(nameRaw); 108 | 109 | // @UseClassPathAdjuster(PowerMockHelperClass.class) 110 | AnnotationVisitor av0 = cw.visitAnnotation( 111 | USE_CLASSPATH_ADJUSTER_CLASS_DESCRIPTOR, true); 112 | av0.visit("value", Type.getObjectType(powerMockHelperClassRaw)); 113 | av0.visitEnd(); 114 | 115 | // @PowerMockIgnore("package.for.powermock.avoidance.*") 116 | AnnotationVisitor av1 = cw.visitAnnotation( 117 | Type.getType(PowerMockIgnore.class).getDescriptor(), true); 118 | AnnotationVisitor av2 = av1.visitArray("value"); 119 | av2.visit(null, PACKAGE_FOR_POWERMOCK_AVOIDANCE_RAW.replace('/', '.') 120 | + ".*"); 121 | av2.visitEnd(); 122 | av1.visitEnd(); 123 | } 124 | 125 | public void loadConstructorArgs(GeneratorAdapter mv) { 126 | mv.visitFieldInsn(GETSTATIC, powerMockHelperClassRaw, 127 | FIELD_CONSTRUCTOR_ARGS, "[Ljava/lang/Object;"); 128 | } 129 | 130 | public void classConstructed(Class clazz, byte[] clazzBytes, 131 | Object[] constructorArgs) { 132 | ClassWriter cw = new ClassWriter(0); 133 | 134 | // public class PowerMockHelperClass implements ClassPathAdjuster { 135 | cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, powerMockHelperClassRaw, null, 136 | "java/lang/Object", 137 | new String[] { CLASSPATH_ADJUSTER_CLASS_INTERNALNAME }); 138 | 139 | // public static byte[] testClassBytes; 140 | cw.visitField(ACC_PUBLIC + ACC_STATIC, FIELD_TEST_CLASS_BYTE_ARR, "[B", 141 | null, null).visitEnd(); 142 | 143 | // public static Object[] constructorArgs; 144 | cw.visitField(ACC_PUBLIC + ACC_STATIC, FIELD_CONSTRUCTOR_ARGS, 145 | "[Ljava/lang/Object;", null, null).visitEnd(); 146 | 147 | // public PowerMockHelperClass() { 148 | { 149 | MethodVisitor mv; 150 | mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); 151 | mv.visitCode(); 152 | // super(); 153 | mv.visitVarInsn(ALOAD, 0); 154 | mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", 155 | "()V"); 156 | mv.visitInsn(RETURN); 157 | mv.visitMaxs(1, 1); 158 | mv.visitEnd(); 159 | } 160 | // @Override 161 | // public void adjustClassPath(ClassPool pool) { 162 | { 163 | MethodVisitor mv; 164 | mv = cw.visitMethod(ACC_PUBLIC, "adjustClassPath", 165 | "(Ljavassist/ClassPool;)V", null, null); 166 | mv.visitCode(); 167 | // pool.appendClassPath(new ByteArrayClassPath("TestClass_0", 168 | // testClassBytes)); 169 | mv.visitVarInsn(ALOAD, 1); 170 | mv.visitTypeInsn(NEW, "javassist/ByteArrayClassPath"); 171 | mv.visitInsn(DUP); 172 | mv.visitLdcInsn(Type.getObjectType(nameRaw).getClassName()); 173 | mv.visitFieldInsn(GETSTATIC, powerMockHelperClassRaw, 174 | FIELD_TEST_CLASS_BYTE_ARR, "[B"); 175 | mv.visitMethodInsn(INVOKESPECIAL, "javassist/ByteArrayClassPath", 176 | "", "(Ljava/lang/String;[B)V"); 177 | mv.visitMethodInsn(INVOKEVIRTUAL, "javassist/ClassPool", 178 | "appendClassPath", 179 | "(Ljavassist/ClassPath;)Ljavassist/ClassPath;"); 180 | mv.visitInsn(POP); 181 | mv.visitInsn(RETURN); 182 | mv.visitMaxs(5, 2); 183 | mv.visitEnd(); 184 | } 185 | cw.visitEnd(); 186 | 187 | byte[] clazzBytesCPA = cw.toByteArray(); 188 | Class clazzCPA = ClassLoaderInjector.injectClass( 189 | Type.getObjectType(powerMockHelperClassRaw).getClassName(), 190 | clazzBytesCPA); 191 | 192 | try { 193 | clazzCPA.getField(FIELD_TEST_CLASS_BYTE_ARR).set(null, clazzBytes); 194 | } catch (Exception e) { 195 | throw new RuntimeException("Error installing powermock helper", e); 196 | } 197 | 198 | try { 199 | clazzCPA.getField(FIELD_CONSTRUCTOR_ARGS) 200 | .set(null, constructorArgs); 201 | } catch (Exception e) { 202 | throw new RuntimeException( 203 | "Error installing parameterized arguments", e); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runner/XmlJUnitFormatter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2013-2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runner; 17 | 18 | import static com.nitorcreations.junit.runner.ThreadLocalStdOutErrCapturer.captureStdOutAndErrStreams; 19 | import static com.nitorcreations.junit.runner.ThreadLocalStdOutErrCapturer.stderrCapture; 20 | import static com.nitorcreations.junit.runner.ThreadLocalStdOutErrCapturer.stdoutCapture; 21 | import static com.nitorcreations.junit.runner.XmlJUnitFormatter.Status.assumptionFailure; 22 | import static com.nitorcreations.junit.runner.XmlJUnitFormatter.Status.error; 23 | import static com.nitorcreations.junit.runner.XmlJUnitFormatter.Status.failed; 24 | import static com.nitorcreations.junit.runner.XmlJUnitFormatter.Status.ignored; 25 | import static com.nitorcreations.junit.runner.XmlJUnitFormatter.Status.success; 26 | import static java.lang.Integer.toHexString; 27 | import static java.lang.System.currentTimeMillis; 28 | import static java.lang.System.err; 29 | import static java.lang.System.getProperty; 30 | import static java.lang.System.out; 31 | 32 | import java.io.File; 33 | import java.io.PrintWriter; 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | import org.junit.runner.Description; 38 | import org.junit.runner.Result; 39 | import org.junit.runner.notification.Failure; 40 | import org.junit.runner.notification.RunListener; 41 | 42 | import com.nitorcreations.junit.runner.ThreadLocalStdOutErrCapturer.CaptureStreams; 43 | 44 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 45 | 46 | public class XmlJUnitFormatter extends RunListener { 47 | private final List results = new ArrayList(); 48 | private final int threadNumber; 49 | private String testName; 50 | private PrintWriter xml; 51 | private Status status; 52 | private String failureTrace; 53 | private long testStartTime; 54 | private int errorCount; 55 | private int failureCount; 56 | private int skippedCount; 57 | 58 | private static final String outputDir; 59 | 60 | static { 61 | outputDir = getProperty("output.dir", ".") + File.separator; 62 | File dir = new File(outputDir); 63 | if (!dir.mkdirs() && !dir.exists()) { 64 | throw new RuntimeException("Failed to create output directory " + outputDir); 65 | } 66 | captureStdOutAndErrStreams(); 67 | } 68 | 69 | enum Status { 70 | success(null), 71 | ignored("skipped"), 72 | error("error"), 73 | failed("failure"), 74 | assumptionFailure("skipped"); 75 | 76 | public final String traceElement; 77 | 78 | Status(String traceElement) { 79 | this.traceElement = traceElement; 80 | } 81 | } 82 | 83 | public XmlJUnitFormatter(int threadNumber) { 84 | this.threadNumber = threadNumber; 85 | } 86 | 87 | static class TestStatus { 88 | public final String name; 89 | public final String classname; 90 | public final String stderr; 91 | public final String stdout; 92 | public final Status status; 93 | public final String trace; 94 | public final long runTime; 95 | 96 | public TestStatus(Description description, Status status, String trace, String stdout, String stderr, long runTime) { 97 | this.status = status; 98 | this.trace = trace; 99 | this.stdout = stdout; 100 | this.stderr = stderr; 101 | this.runTime = runTime; 102 | name = description.getMethodName(); 103 | classname = description.getClassName(); 104 | } 105 | } 106 | 107 | @Override 108 | public void testRunStarted(Description description) throws Exception { 109 | if ((description.getClassName() == null || description.getClassName().equals("null")) && !description.getChildren().isEmpty()) { 110 | testName = getProperty("test.name", ""); 111 | if (!testName.isEmpty()) { 112 | testName += '-'; 113 | } 114 | testName += description.getChildren().get(0).getClassName(); 115 | } else { 116 | testName = getProperty("test.name", description.getClassName()); 117 | } 118 | String name = "TEST-" + testName + "-" + threadNumber + ".xml"; 119 | xml = new PrintWriter(outputDir + name, "UTF-8"); 120 | } 121 | 122 | @Override 123 | @SuppressFBWarnings(value="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "JUnit runner calls testRunStarted before this method") 124 | public void testRunFinished(Result result) throws Exception { 125 | xml.printf("%n"); 126 | xml.printf("%n", testName, 127 | result.getRunTime() / 1000.0, results.size(), errorCount, skippedCount, failureCount); 128 | for (TestStatus test : results) { 129 | String ignoreAttr = ""; 130 | if (test.status == ignored) { 131 | ignoreAttr = " ignored=\"true\""; 132 | } 133 | xml.printf(" ", test.name, test.classname, 134 | test.runTime / 1000.0, ignoreAttr); 135 | printIfPresent("system-out", test.stdout); 136 | printIfPresent("system-err", test.stderr); 137 | printIfPresent(test.status.traceElement, test.trace); 138 | xml.printf("%n"); 139 | } 140 | xml.print(""); 141 | xml.close(); 142 | } 143 | 144 | @SuppressFBWarnings(value="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "JUnit runner calls testRunStarted before this method") 145 | private void printIfPresent(String element, String text) { 146 | if (text == null || text.isEmpty()) { 147 | return; 148 | } 149 | xml.printf("%n <%s>%s", element, xmlEscape(text), element); 150 | } 151 | 152 | private static String xmlEscape(String str) { 153 | StringBuilder sb = new StringBuilder(str.length() + 128); 154 | for (int i = 0; i < str.length(); i++) { 155 | char ch = str.charAt(i); 156 | if (ch == '<') { 157 | sb.append("<"); 158 | } else if (ch == '>') { 159 | sb.append(">"); 160 | } else if (ch == '&') { 161 | sb.append("&"); 162 | } else if (ch == 0) { 163 | sb.append("(0)"); 164 | } else if ((ch >= 1 && ch <= 8) || ch == 0xB || ch == 0xC || (ch >= 0xE && ch <= 0x1F) || (ch >= 0x7F && ch <= 0x84) || (ch >= 0x86 && ch <= 0x9F)) { 165 | sb.append("&#x"); 166 | sb.append(toHexString(ch)); 167 | sb.append(';'); 168 | } else { 169 | sb.append(ch); 170 | } 171 | } 172 | return sb.toString(); 173 | 174 | } 175 | 176 | @Override 177 | public void testStarted(Description description) throws Exception { 178 | status = success; 179 | failureTrace = null; 180 | testStartTime = currentTimeMillis(); 181 | } 182 | 183 | @Override 184 | public void testFinished(Description description) throws Exception { 185 | addResult(description); 186 | } 187 | 188 | @Override 189 | public void testAssumptionFailure(Failure failure) { 190 | status = assumptionFailure; 191 | failureTrace = failure.getTrace(); 192 | skippedCount++; 193 | } 194 | 195 | @Override 196 | public void testIgnored(Description description) throws Exception { 197 | failureTrace = "ignored"; 198 | status = ignored; 199 | skippedCount++; 200 | addResult(description); 201 | } 202 | 203 | @Override 204 | public void testFailure(Failure failure) { 205 | if (status == success) { 206 | if (failure.getException() instanceof AssertionError) { 207 | status = failed; 208 | failureCount++; 209 | } else { 210 | status = error; 211 | errorCount++; 212 | } 213 | failureTrace = failure.getTrace(); 214 | } 215 | } 216 | 217 | private void addResult(Description description) { 218 | long duration; 219 | if (testStartTime > 0) { 220 | duration = currentTimeMillis() - testStartTime; 221 | testStartTime = 0; 222 | } else { 223 | duration = 0; 224 | } 225 | out.flush(); 226 | err.flush(); 227 | CaptureStreams outCapture = stdoutCapture.get(); 228 | CaptureStreams errCapture = stderrCapture.get(); 229 | results.add(new TestStatus(description, status, failureTrace, outCapture.toString(), errCapture.toString(), duration)); 230 | outCapture.bytes.reset(); 231 | errCapture.bytes.reset(); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/NestedRunner.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2013 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners; 17 | 18 | import java.lang.reflect.Method; 19 | import java.util.ArrayList; 20 | import java.util.Collection; 21 | import java.util.List; 22 | 23 | import org.junit.After; 24 | import org.junit.AfterClass; 25 | import org.junit.Before; 26 | import org.junit.BeforeClass; 27 | import org.junit.Test; 28 | import org.junit.internal.runners.statements.RunAfters; 29 | import org.junit.internal.runners.statements.RunBefores; 30 | import org.junit.runner.Description; 31 | import org.junit.runner.Runner; 32 | import org.junit.runner.notification.RunNotifier; 33 | import org.junit.runners.BlockJUnit4ClassRunner; 34 | import org.junit.runners.ParentRunner; 35 | import org.junit.runners.model.FrameworkMethod; 36 | import org.junit.runners.model.InitializationError; 37 | import org.junit.runners.model.Statement; 38 | import org.junit.internal.runners.statements.Fail; 39 | 40 | public class NestedRunner extends ParentRunner { 41 | private final List children = new ArrayList(); 42 | private final NestedClassRunner delegatedRunner; 43 | private final NestedRunner parentRunner; 44 | 45 | public NestedRunner(Class testClass) throws InitializationError { 46 | this(testClass, null); 47 | } 48 | 49 | public NestedRunner(Class testClass, NestedRunner parentRunner) throws InitializationError { 50 | super(testClass); 51 | this.parentRunner = parentRunner; 52 | delegatedRunner = new NestedClassRunner(testClass); 53 | children.addAll(delegatedRunner.giveMeTheDamnChildren()); 54 | addToChildrenAllNestedClassesWithTestsAndTestMethods(testClass); 55 | } 56 | 57 | private void addToChildrenAllNestedClassesWithTestsAndTestMethods(Class testClass) throws InitializationError { 58 | for (Class child : testClass.getDeclaredClasses()) { 59 | if (containsTests(child)) { 60 | children.add(new NestedRunner(child, this)); 61 | } 62 | } 63 | } 64 | 65 | private boolean containsTests(Class clazz) { 66 | for (Method method : clazz.getMethods()) { 67 | if (method.getAnnotation(Test.class) != null) { 68 | return true; 69 | } 70 | } 71 | return childrenContainTests(clazz); 72 | } 73 | 74 | public boolean childrenContainTests(Class clazz) { 75 | for (Class klazz : clazz.getDeclaredClasses()) { 76 | if (containsTests(klazz)) { 77 | return true; 78 | } 79 | } 80 | return false; 81 | } 82 | 83 | protected List getChildren() { 84 | return children; 85 | } 86 | 87 | @Override 88 | public String getName() { 89 | return getTestClass().getJavaClass().getSimpleName(); 90 | } 91 | 92 | protected Description describeChild(Object child) { 93 | if (child instanceof Runner) { 94 | return ((Runner)child).getDescription(); 95 | } else { 96 | return delegatedRunner.callTheProtectedDescribeChild((FrameworkMethod)child); 97 | } 98 | } 99 | 100 | protected void runChild(Object child, RunNotifier notifier) { 101 | if (child instanceof Runner) { 102 | ((Runner)child).run(notifier); 103 | } else { 104 | delegatedRunner.callThePrivateRunChild((FrameworkMethod)child, notifier); 105 | } 106 | } 107 | 108 | private Object constructTestClass() throws Exception { 109 | if (getTestClass().getOnlyConstructor().getParameterTypes().length == 1 && parentRunner != null) { 110 | Object parent = parentRunner.constructTestClass(); 111 | Object newInstance = getTestClass().getOnlyConstructor().newInstance(parent); 112 | delegatedRunner.currentTestObject = newInstance; 113 | return newInstance; 114 | } 115 | Object newInstance = getTestClass().getOnlyConstructor().newInstance(); 116 | delegatedRunner.currentTestObject = newInstance; 117 | return newInstance; 118 | } 119 | 120 | public List getBefores() { 121 | List befores = new ArrayList(); 122 | befores.addAll(getTestClass().getAnnotatedMethods(Before.class)); 123 | return befores; 124 | } 125 | 126 | public List getAfters() { 127 | List afters = new ArrayList(); 128 | afters.addAll(getTestClass().getAnnotatedMethods(After.class)); 129 | return afters; 130 | } 131 | 132 | private Statement withParentBefores(Statement statement) { 133 | if (parentRunner != null) { 134 | return parentRunner.withParentBefores(new RunBefores(statement, getBefores(), delegatedRunner.currentTestObject)); 135 | } 136 | return new RunBefores(statement, getBefores(), delegatedRunner.currentTestObject); 137 | } 138 | 139 | private Statement withParentAfters(Statement statement) { 140 | if (parentRunner != null) { 141 | return new RunAfters(parentRunner.withParentAfters(statement), getAfters(), delegatedRunner.currentTestObject); 142 | } 143 | return new RunAfters(statement, getAfters(), delegatedRunner.currentTestObject); 144 | } 145 | 146 | private class NestedClassRunner extends BlockJUnit4ClassRunner { 147 | private Object currentTestObject; 148 | 149 | public NestedClassRunner(Class childClass) throws InitializationError { 150 | super(childClass); 151 | } 152 | 153 | public void callThePrivateRunChild(FrameworkMethod child, RunNotifier notifier) { 154 | runChild(child, notifier); 155 | } 156 | 157 | public Description callTheProtectedDescribeChild(FrameworkMethod child) { 158 | return describeChild(child); 159 | } 160 | 161 | public Collection giveMeTheDamnChildren() { 162 | return super.getChildren(); 163 | } 164 | 165 | protected void validateConstructor(List errors) { 166 | validateOnlyOneConstructor(errors); 167 | validateNonStaticInnerClassWithDefaultConstructor(errors); 168 | } 169 | 170 | private void validateNonStaticInnerClassWithDefaultConstructor(List errors) { 171 | try { 172 | getTestClass().getJavaClass().getConstructor(NestedRunner.this.getTestClass().getJavaClass()); 173 | } catch (NoSuchMethodException e) { 174 | String gripe = "Nested test classes should be non-static and have a public zero-argument constructor"; 175 | errors.add(new Exception(gripe)); 176 | } 177 | } 178 | 179 | protected Object createTest() throws Exception { 180 | return constructTestClass(); 181 | } 182 | 183 | protected Statement methodBlock(FrameworkMethod method) { 184 | Statement statement = super.methodBlock(method); 185 | if (statement instanceof Fail) { 186 | return statement; 187 | } 188 | statement = withParentBefores(statement); 189 | statement = withParentAfters(statement); 190 | return statement; 191 | } 192 | 193 | //Disable withBefores so it won't collide with our @Before handler 194 | @Override 195 | protected Statement withBefores(FrameworkMethod method, Object target, 196 | Statement statement) { 197 | return new RunBefores(statement, 198 | new ArrayList(), target); 199 | } 200 | 201 | //Disable withAfters so it won't collide with our @After handler 202 | @Override 203 | protected Statement withAfters(FrameworkMethod method, Object target, 204 | Statement statement) { 205 | return new RunAfters(statement, 206 | new ArrayList(), target); 207 | } 208 | 209 | @Override 210 | protected void collectInitializationErrors(List errors) { 211 | validatePublicVoidNoArgMethods(BeforeClass.class, true, errors); 212 | validatePublicVoidNoArgMethods(AfterClass.class, true, errors); 213 | // we have to remove this check because we want non-static classes to work 214 | //validateClassRules(errors); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /core-utils/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.nitorcreations 6 | core-utils 7 | 1.5-SNAPSHOT 8 | jar 9 | Core Utils 10 | Generic utilities 11 | https://github.com/NitorCreations/CoreComponents 12 | 13 | scm:git:git@github.com:NitorCreations/CoreComponents.git 14 | scm:git:git@github.com:NitorCreations/CoreComponents.git 15 | https://github.com/NitorCreations/CoreComponents 16 | HEAD 17 | 18 | 19 | 3.1.1 20 | 21 | 22 | 23 | gmokki 24 | Mikko Tiihonen 25 | mikko.tiihonen@nitorcreations.com 26 | 27 | 28 | efonsell 29 | Edvard Fonsell 30 | edvard.fonsell@nitorcreations.com 31 | 32 | 33 | 34 | 35 | The Apache Software License, Version 2.0 36 | http://www.apache.org/licenses/LICENSE-2.0.txt 37 | repo 38 | 39 | 40 | 41 | UTF-8 42 | UTF-8 43 | 44 | 45 | 46 | 47 | 48 | 49 | maven-eclipse-plugin 50 | 2.9 51 | 52 | 53 | maven-compiler-plugin 54 | 3.2 55 | 56 | 57 | org.codehaus.mojo 58 | cobertura-maven-plugin 59 | 2.6 60 | 61 | 62 | maven-surefire-plugin 63 | 2.17 64 | 65 | 66 | maven-jar-plugin 67 | 2.5 68 | 69 | 70 | maven-source-plugin 71 | 2.4 72 | 73 | 74 | maven-javadoc-plugin 75 | 2.10.1 76 | 77 | 78 | maven-site-plugin 79 | 3.4 80 | 81 | 82 | maven-install-plugin 83 | 2.5.2 84 | 85 | 86 | maven-release-plugin 87 | 2.5.1 88 | 89 | 90 | maven-resources-plugin 91 | 2.7 92 | 93 | 94 | maven-clean-plugin 95 | 2.6 96 | 97 | 98 | maven-deploy-plugin 99 | 2.8.2 100 | 101 | 102 | 103 | 104 | 105 | org.sonatype.plugins 106 | nexus-staging-maven-plugin 107 | 1.6.5 108 | true 109 | 110 | ossrh 111 | https://oss.sonatype.org/ 112 | true 113 | 114 | 115 | 116 | maven-eclipse-plugin 117 | 118 | true 119 | false 120 | .eclipse_classes 121 | true 122 | 123 | 124 | 125 | true 126 | maven-compiler-plugin 127 | 128 | 1.7 129 | 1.7 130 | 131 | 132 | 133 | maven-source-plugin 134 | 135 | 136 | attach-sources 137 | verify 138 | 139 | jar-no-fork 140 | 141 | 142 | 143 | 144 | 145 | maven-javadoc-plugin 146 | 147 | 148 | attach-javadocs 149 | 150 | jar 151 | 152 | 153 | 154 | 155 | 156 | maven-surefire-plugin 157 | 158 | 159 | **/*Test.java 160 | 161 | random 162 | never 163 | true 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | org.apache.velocity 175 | velocity 176 | 1.7 177 | true 178 | 179 | 180 | 181 | 182 | 183 | org.codehaus.mojo 184 | findbugs-maven-plugin 185 | 3.0.0 186 | 187 | true 188 | Max 189 | Low 190 | 191 | 192 | 193 | org.codehaus.mojo 194 | cobertura-maven-plugin 195 | 2.5.2 196 | 197 | 198 | xml 199 | 200 | 201 | 202 | 203 | maven-pmd-plugin 204 | 3.2 205 | 206 | true 207 | 100 208 | 1.6 209 | 210 | **/test/*.java 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | release 219 | 220 | 221 | 222 | org.apache.maven.plugins 223 | maven-source-plugin 224 | 225 | 226 | attach-sources 227 | 228 | jar-no-fork 229 | 230 | 231 | 232 | 233 | 234 | org.apache.maven.plugins 235 | maven-javadoc-plugin 236 | 237 | 238 | attach-javadocs 239 | 240 | jar 241 | 242 | 243 | 244 | 245 | 246 | org.apache.maven.plugins 247 | maven-gpg-plugin 248 | 1.5 249 | 250 | 251 | sign-artifacts 252 | verify 253 | 254 | sign 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright 2013, 2015 Nitor Creations Oy, Jonas Berlin 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | use strict; 18 | use warnings; 19 | use IPC::Open2; 20 | use File::Temp qw(mktemp); 21 | 22 | require '.githooks/license-maintainer/license.pm'; 23 | 24 | sub extract_timestamp($) { 25 | my $gitdate = $_[0]; 26 | if (defined($gitdate)) { 27 | $gitdate =~ m!(\d{9,})! and return $1; 28 | } 29 | return undef; 30 | } 31 | 32 | my $EMPTY_SHA = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'; 33 | 34 | my $INJECT_LICENSES = length(($ENV{"INJECT_LICENSES"} || "") =~ s!\s+!!r) > 0; 35 | 36 | if ($INJECT_LICENSES) { 37 | print STDERR qq{NOTE: Adding/updating licenses for all maintained files, i.e. 38 | - adds license to files lacking an license 39 | - adds the author (configured in the "license.author" configuration option) to 40 | files lacking an author 41 | - adds missing years to existing licenses, for example if some user committed 42 | stuff without updating the license e.g. did not have the pre-commit hook 43 | installed 44 | 45 | }; 46 | } 47 | 48 | my $repoRootDir = `pwd`; 49 | chomp $repoRootDir; 50 | 51 | my $scriptDir = $repoRootDir.'/.githooks/license-maintainer'; 52 | 53 | my $origLineSeparator = $/; 54 | $/="\0"; 55 | 56 | # Fetch author string to use 57 | my $author = `git config --get license.author`; 58 | unless(length($author)) { 59 | print STDERR "ERROR: Author not configured.\n\nConfigure with:\n\n\tgit config license.author \"My Company Inc\"\n"; 60 | exit(1); 61 | } 62 | $author =~ s!^\s+!!; 63 | $author =~ s!\s+$!!; 64 | 65 | # get list of files in stage 66 | my @filesInStage; 67 | open my $fileListFh, '-|', qw(git ls-files --stage -z) or die; 68 | while(<$fileListFh>) { 69 | chomp; 70 | die $_ unless(m!^([0-7]{6}) ([0-9a-f]{40}) ([0-3])\t(.*)$!); 71 | push @filesInStage, { mode => $1, sha => $2, stage => $3, name => $4 }; 72 | } 73 | close $fileListFh; 74 | 75 | # get the license file to use for each staged file 76 | my %licensefile; 77 | my $tmpfile = mktemp(File::Spec->tmpdir.'/licXXXXX'); 78 | open my $checkAttrInFh, "|-", "git check-attr licensefile -z --stdin > '$tmpfile'" or die; 79 | print $checkAttrInFh join("\0", map { $_->{name} } @filesInStage),"\0"; 80 | close $checkAttrInFh; 81 | open my $changeAttrOutFh, "<", $tmpfile or die; 82 | while(<$changeAttrOutFh>) { 83 | chomp; 84 | my $file = $_; 85 | <$changeAttrOutFh>; # attribute name 86 | my $license = <$changeAttrOutFh>; 87 | chomp $license; 88 | $licensefile{$file} = $license if(defined($license) && length($license) && $license ne "unset" && $license ne "unspecified"); 89 | } 90 | close $changeAttrOutFh; 91 | #unlink($tmpfile); 92 | 93 | # get list of files that will be changed by this commit 94 | my %changed; 95 | open my $changeListFh, "-|", qw(git diff --cached --raw -z --name-only --no-renames --diff-filter=AM) or die; 96 | while(<$changeListFh>) { 97 | chomp; 98 | $changed{$_} = 1; 99 | } 100 | close $changeListFh; 101 | 102 | $/=$origLineSeparator; 103 | 104 | # For all files, check license. For files with changes add/update the license as needed. 105 | my @filesInCheckoutThatCouldNotBePatchedWithLicenseUpdate; 106 | my $numFilesWithLicenseBrokenOrMissing = 0; 107 | foreach my $staged (@filesInStage) { 108 | next if ($staged->{stage} != 0); # conflict exists for file, skip 109 | 110 | my $licenseFile = $licensefile{$staged->{name}}; 111 | 112 | next unless(defined($licenseFile)); # no license formatting for file, skip 113 | 114 | my $isFileChangedByCommit = defined($changed{$staged->{name}}); 115 | 116 | # transform file and store transformed in git 117 | # transformedsha=`git cat-file blob | perl license.pl LICENSE- | git hash-object -w --path --stdin` 118 | 119 | # Run "git cat-file blob " to get the staged files' contents on stdout e.g. readable through *origFileContentFh 120 | local *origFileContentFh; 121 | open(\*origFileContentFh, '-|', 'git', 'cat-file', 'blob', $staged->{sha}) or die 'git cat-file '.$staged->{sha}; 122 | 123 | undef $/; 124 | my $origFileContent = ; 125 | $/ = $origLineSeparator; 126 | close origFileContentFh; 127 | 128 | my $license_text_file = $repoRootDir.'/'.$licenseFile; 129 | if (!$isFileChangedByCommit && !$INJECT_LICENSES) { 130 | # file not changed, just check license 131 | 132 | my $ret = My::License::isLackingProperLicense($license_text_file, $staged->{name}, $origFileContent); 133 | if ($ret == 0) { 134 | # license ok 135 | } elsif ($ret == 1) { 136 | # license malformed, error already reported by script 137 | ++$numFilesWithLicenseBrokenOrMissing; 138 | } elsif ($ret == 2) { 139 | # license missing 140 | print STDERR "WARNING: License missing in ",$staged->{name},"\n"; 141 | ++$numFilesWithLicenseBrokenOrMissing; 142 | } else { 143 | die "Unhandled isLackingProperLicense() return code $ret"; 144 | } 145 | } else { 146 | # file changed, add license or update license to contain current year 147 | 148 | my %author_years; 149 | if ($isFileChangedByCommit) { 150 | # use author timestamp of current commit author 151 | my $author_date = extract_timestamp($ENV{'GIT_AUTHOR_DATE'}) || time(); 152 | my @author_date_fields = localtime($author_date); 153 | my $author_year = $author_date_fields[5] + 1900; 154 | $author_years{$author_year} = 1; 155 | } else { 156 | # use author timestamp of commit when file was last changed 157 | open TSTAMPS, '-|', 'git', 'log', '--format=format:%ai', $staged->{name} or die "Internal error: Unable to find any commits for $staged->{name}"; 158 | while () { 159 | my $author_year = substr($_,0,4); 160 | $author_years{$author_year} = 1; 161 | } 162 | close TSTAMPS; 163 | scalar(keys %author_years) >= 0 or die "Internal error: Unable to find any commits for $staged->{name}"; 164 | } 165 | my ($ret, @transformedContent) = My::License::maintainLicense($license_text_file, $staged->{name}, $origFileContent, $author, !$isFileChangedByCommit, \%author_years); 166 | if ($ret == 1) { 167 | print STDERR "Please correct the problems before re-attempting commit\n"; 168 | exit(1); 169 | } elsif ($ret != 0) { 170 | die "Unhandled maintainLicense() return code $ret"; 171 | } 172 | 173 | # Run "git hash-object -w --path --stdin". stdin: transformed contents from *transformedFileContentFh, stdout: sha of contents through *transformedShaFh 174 | local *transformedShaFh; 175 | local *transformedFileContentFh; 176 | my $gitHashObjectPid = open2(\*transformedShaFh, \*transformedFileContentFh, 'git', 'hash-object', '-w', '--path', $staged->{name}, '--stdin') or die 'git hash-object'; 177 | 178 | print transformedFileContentFh @transformedContent; 179 | close transformedFileContentFh; 180 | 181 | # read sha of transformed file 182 | my $transformedSha = ; 183 | die 'read git hash-object sha' unless(defined($transformedSha)); 184 | close transformedShaFh; 185 | chomp $transformedSha; 186 | 187 | # check exit codes of commands run 188 | waitpid $gitHashObjectPid, 0; 189 | die "git hash-object failed ".($? >> 8) if ($? >> 8); 190 | 191 | # sanity check 192 | die "Refusing to result in empty file" if($transformedSha eq $EMPTY_SHA); 193 | 194 | # if transformed version is different from original, update stage and checkout with transformed version 195 | unless ($staged->{sha} eq $transformedSha) { 196 | # Update stage by using the sha of the transformed contents for the filename in question 197 | 198 | # Run "git update-index --cacheinfo " to update stage with transformed version 199 | system('git', 'update-index', '--cacheinfo', $staged->{mode}, $transformedSha, $staged->{name}) and die 'git update-index '.$transformedSha.' '.$staged->{name}.' returned '.($? >> 8); 200 | 201 | # The file in the checkout may be different than the file 202 | # in the index, so we cannot overwrite it directly. 203 | # Instead we try to apply the differencies applied by the 204 | # license transformer, and in case that fails, let user 205 | # resolve it. 206 | 207 | # Run "git diff ". stdin: none, stdout: effective diff of license transformer operation 208 | local *licenseDiffFh; 209 | open(\*licenseDiffFh, '-|', 'git', 'diff', $staged->{sha}, $transformedSha) or die 'git diff'; 210 | 211 | # Run "patch --no-backup-if-mismatch ". stdin: license diff, stdout: passed on to our stdout 212 | my $checkoutPatchPid = open2('>&STDOUT', '<&licenseDiffFh', 'patch', '--no-backup-if-mismatch', $staged->{name}) or die 'patch'; 213 | 214 | # check exit codes of commands run 215 | waitpid $checkoutPatchPid, 0; 216 | if ($? >> 8) { 217 | push @filesInCheckoutThatCouldNotBePatchedWithLicenseUpdate, $staged->{name}; 218 | } 219 | } 220 | } 221 | } 222 | 223 | if ($#filesInCheckoutThatCouldNotBePatchedWithLicenseUpdate >= 0) { 224 | print STDERR ("\n", 225 | "WARNING: Unable to apply license update in the checkout to the following files:\n", 226 | " ",join("\n ", @filesInCheckoutThatCouldNotBePatchedWithLicenseUpdate),"\n", 227 | "\n", 228 | "Please apply the license updates manually for these files. Possibly see the associated .rej files for what changes are needed.\n", 229 | "\n"); 230 | } 231 | if ($numFilesWithLicenseBrokenOrMissing) { 232 | print STDERR qq{ 233 | NOTE: Some files had license problems, kindly fix them in a separate commit by running: 234 | 235 | INJECT_LICENSES=1 git commit --allow-empty -m 'Add/update licenses for all maintained files' --edit 236 | 237 | This adds/updates licenses for all maintained files, i.e. 238 | - adds license to files lacking an license 239 | - adds the author (configured in the "license.author" configuration option) to 240 | files lacking an author 241 | - adds missing years to existing licenses, for example if some user committed 242 | stuff without updating the license e.g. did not have the pre-commit hook 243 | installed 244 | 245 | }; 246 | 247 | } 248 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/ParameterizedSuiteBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014-2015 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import java.lang.reflect.Constructor; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.HashMap; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | /** 27 | * This is a helper class for {@link WrappingParameterizedRunner}. It acts as a 28 | * suite builder, providing an {@link #constructWith(Object...)} method to add 29 | * new test configurations to the suite. The method returns a 30 | * {@link TestInstantiatorBuilder} instance which can then optionally be used to 31 | * further configure the test configuration. 32 | */ 33 | public class ParameterizedSuiteBuilder { 34 | private final Class testClass; 35 | private final List tests = new ArrayList(); 36 | 37 | public ParameterizedSuiteBuilder(Class testClass) { 38 | this.testClass = testClass; 39 | } 40 | 41 | /** 42 | * Add a new test to the suite, to be constructed with the given arguments. 43 | *

44 | * In case there are multiple constructors available, please note that the 45 | * constructor is chosen based on the runtime types of the arguments, which 46 | * might be different from constructing the class directly. 47 | * 48 | * @param constructorArgs 49 | * the arguments to give to the constructor 50 | * @return a builder for further configuration of the test. 51 | */ 52 | public TestInstantiatorBuilder constructWith(Object... constructorArgs) { 53 | TestInstantiatorBuilder testBuilder = new TestInstantiatorBuilder( 54 | constructorArgs); 55 | tests.add(testBuilder); 56 | return testBuilder; 57 | } 58 | 59 | List getTests() { 60 | return tests; 61 | } 62 | 63 | static final Map, Class> UNBOXED_TO_BOXED = new HashMap, Class>(); 64 | static { 65 | UNBOXED_TO_BOXED.put(Integer.TYPE, Integer.class); 66 | UNBOXED_TO_BOXED.put(Byte.TYPE, Byte.class); 67 | UNBOXED_TO_BOXED.put(Character.TYPE, Character.class); 68 | UNBOXED_TO_BOXED.put(Long.TYPE, Long.class); 69 | UNBOXED_TO_BOXED.put(Double.TYPE, Double.class); 70 | UNBOXED_TO_BOXED.put(Float.TYPE, Float.class); 71 | UNBOXED_TO_BOXED.put(Boolean.TYPE, Boolean.class); 72 | UNBOXED_TO_BOXED.put(Short.TYPE, Short.class); 73 | } 74 | 75 | /** 76 | * Builder for configuring a single test in a parameterized suite. The 77 | * with*() methods are used to set up the test. The builder actively 78 | * validates the calls, immediately giving feedback on improper usage. 79 | */ 80 | public class TestInstantiatorBuilder { 81 | private Constructor constructor; 82 | private Object[] constructorArgs; 83 | private String description; 84 | 85 | TestInstantiatorBuilder(Object... constructorArgs) { 86 | if (constructorArgs == null) { 87 | throw new IllegalArgumentException( 88 | "constructor argument list null"); 89 | } 90 | this.constructor = findBestConstructorMatch(constructorArgs); 91 | this.constructorArgs = Arrays.copyOf(constructorArgs, 92 | constructorArgs.length); 93 | } 94 | 95 | public Constructor getConstructor() { 96 | return constructor; 97 | } 98 | 99 | public Object[] getConstructorArgs() { 100 | return constructorArgs; 101 | } 102 | 103 | public String getDescription() { 104 | if (description == null) { 105 | StringBuilder sb = new StringBuilder(); 106 | boolean first = true; 107 | for (Object parameter : constructorArgs) { 108 | if (first) { 109 | first = false; 110 | } else { 111 | sb.append(", "); 112 | } 113 | if (parameter instanceof String) { 114 | parameter = '"' + (String) parameter + '"'; 115 | } 116 | sb.append(parameter); 117 | } 118 | description = sb.toString(); 119 | } 120 | return description; 121 | } 122 | 123 | private Constructor findBestConstructorMatch( 124 | Object... constructorArgs) { 125 | Constructor[] constructors = (Constructor[]) testClass 126 | .getConstructors(); 127 | Constructor c; 128 | if (constructors.length > 1) { 129 | List> cs = new ArrayList>( 130 | Arrays.asList(constructors)); 131 | Iterator> ics = cs.iterator(); 132 | ics: while (ics.hasNext()) { 133 | Class[] parameterTypes = ics.next().getParameterTypes(); 134 | if (parameterTypes.length != constructorArgs.length) { 135 | ics.remove(); 136 | continue; 137 | } 138 | for (int i = 0; i < constructorArgs.length; ++i) { 139 | Class boxedType = boxedTypeFor(parameterTypes[i]); 140 | if (!boxedType.isInstance(constructorArgs[i])) { 141 | ics.remove(); 142 | continue ics; 143 | } 144 | } 145 | } 146 | if (cs.isEmpty()) { 147 | throw new IllegalArgumentException( 148 | "No constructor(s) found matching given argument types"); 149 | } 150 | if (cs.size() > 1) { 151 | filterRedundantConstructors(cs); 152 | if (cs.size() > 1) { 153 | throw new IllegalStateException( 154 | buildAmbiguousConstructorsExceptionString(cs)); 155 | } 156 | } 157 | c = cs.get(0); 158 | } else { 159 | c = constructors[0]; 160 | } 161 | validateConstructorArguments(c, constructorArgs); 162 | return c; 163 | } 164 | 165 | private void filterRedundantConstructors(List> cs) { 166 | for (int i1 = 0; i1 < cs.size() - 1; ++i1) { 167 | for (int i2 = i1 + 1; i2 < cs.size(); ++i2) { 168 | Constructor c1 = cs.get(i1); 169 | Constructor c2 = cs.get(i2); 170 | if (isFirstStrictlyLessSpecificThanSecond(c1, c2)) { 171 | cs.remove(i1); 172 | i2 = i1; 173 | } else if (isFirstStrictlyLessSpecificThanSecond(c2, c1)) { 174 | cs.remove(i2); 175 | --i2; 176 | } 177 | } 178 | } 179 | } 180 | 181 | /** 182 | * Determines whether all arguments of the first constructor are either 183 | * as specific or less specific as the respective arguments of the 184 | * second constructor. So when called with foo(Object, Object) and 185 | * foo(String, String) it will return true, whereas foo(Object, String) 186 | * and foo(String, Object) will return false. 187 | */ 188 | private boolean isFirstStrictlyLessSpecificThanSecond( 189 | Constructor c1, Constructor c2) { 190 | Class[] ps1 = c1.getParameterTypes(); 191 | Class[] ps2 = c2.getParameterTypes(); 192 | assert ps1.length == ps2.length; 193 | for (int i = 0; i < ps1.length; ++i) { 194 | if (!ps1[i].isAssignableFrom(ps2[i])) { 195 | return false; 196 | } 197 | } 198 | return true; 199 | } 200 | 201 | private String buildAmbiguousConstructorsExceptionString( 202 | List> cs) { 203 | StringBuilder sb = new StringBuilder(); 204 | sb.append("Found ") 205 | .append(cs.size()) 206 | .append(" ambiguous constructors matching given arguments:"); 207 | for (Constructor c2 : cs) { 208 | String sep = "\n\t- ("; 209 | for (Class pt : c2.getParameterTypes()) { 210 | String name = pt.getName(); 211 | sb.append(sep).append( 212 | name.startsWith("java.lang.") ? name.substring(10) 213 | : name); 214 | sep = ", "; 215 | } 216 | sb.append(')'); 217 | } 218 | return sb.toString(); 219 | } 220 | 221 | /** 222 | * Set the test description. This appears as the test subtree name in 223 | * the test report. 224 | * 225 | * @param description the test description 226 | * @return the same test builder instance, for chaining. 227 | */ 228 | public TestInstantiatorBuilder named(String description) { 229 | if (description == null) { 230 | description = "(null)"; 231 | } 232 | this.description = description; 233 | return this; 234 | } 235 | 236 | private void validateConstructorArguments(Constructor c, 237 | Object[] args) { 238 | Class[] parameterTypes = c.getParameterTypes(); 239 | if (parameterTypes.length != args.length) { 240 | throw new IllegalArgumentException( 241 | "Constructor argument count mismatch: expected " 242 | + parameterTypes.length + ", got " 243 | + args.length); 244 | } 245 | for (int i = 0; i < args.length; ++i) { 246 | Class boxedType = boxedTypeFor(parameterTypes[i]); 247 | if (args[i] == null) { 248 | if (parameterTypes[i].isPrimitive()) { 249 | throw new IllegalArgumentException( 250 | "Constructor argument " 251 | + i 252 | + " expects primitive type " 253 | + parameterTypes[i].getName() 254 | + " but null value was given for constructor " 255 | + c + " with proposed arguments " 256 | + Arrays.toString(args)); 257 | } 258 | } else if (!boxedType.isInstance(args[i])) { 259 | if (!parameterTypes[i].isPrimitive() || !canBeCastToPrimitiveType(args[i], parameterTypes[i])) { 260 | throw new IllegalArgumentException("Constructor argument " 261 | + i + " expected type " + boxedType.getName() 262 | + " but got " + args[i].getClass().getName() 263 | + " for constructor " + c 264 | + " with proposed arguments " 265 | + Arrays.toString(args)); 266 | } 267 | } 268 | } 269 | } 270 | 271 | private boolean canBeCastToPrimitiveType(Object arg, Class primitiveType) { 272 | if (primitiveType == Boolean.TYPE){ 273 | return false; 274 | } 275 | final boolean isChar = arg instanceof Character; 276 | final boolean isDouble = arg instanceof Double; 277 | final boolean isFloat = arg instanceof Float; 278 | final boolean isLong = arg instanceof Long; 279 | final boolean isInteger = arg instanceof Integer; 280 | final boolean isShort = arg instanceof Short; 281 | final boolean isByte = arg instanceof Byte; 282 | if (!isDouble && !isFloat && !isLong && !isInteger && !isShort && !isChar && !isByte) { 283 | return false; 284 | } 285 | if (primitiveType == Double.TYPE) { 286 | return true; 287 | } 288 | if (isDouble) { 289 | return false; 290 | } 291 | if (primitiveType == Float.TYPE) { 292 | return true; 293 | } 294 | if (isFloat) { 295 | return false; 296 | } 297 | if (primitiveType == Long.TYPE) { 298 | return true; 299 | } 300 | if (isLong) { 301 | return false; 302 | } 303 | if (primitiveType == Integer.TYPE) { 304 | return true; 305 | } 306 | int v = isChar ? ((Character) arg).charValue() : ((Number)arg).intValue(); 307 | if (primitiveType == Character.TYPE) { 308 | return v >= Character.MIN_VALUE && v <= Character.MAX_VALUE; 309 | } 310 | if (primitiveType == Short.TYPE) { 311 | return v >= Short.MIN_VALUE&& v <= Short.MAX_VALUE; 312 | } 313 | if (primitiveType == Byte.TYPE) { 314 | return v >= Byte.MIN_VALUE && v <= Byte.MAX_VALUE; 315 | } 316 | return false; 317 | } 318 | 319 | private Class boxedTypeFor(Class potentiallyUnboxed) { 320 | Class boxed = UNBOXED_TO_BOXED.get(potentiallyUnboxed); 321 | return boxed != null ? boxed : potentiallyUnboxed; 322 | } 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /junit-runners/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /.githooks/license-maintainer/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /junit-runners/src/main/java/com/nitorcreations/junit/runners/parameterized/WrappingParameterizedRunner.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014-2015 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; 19 | import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS; 20 | import static org.objectweb.asm.Opcodes.AALOAD; 21 | import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 22 | import static org.objectweb.asm.Opcodes.ACC_SUPER; 23 | import static org.objectweb.asm.Opcodes.ALOAD; 24 | import static org.objectweb.asm.Opcodes.ASTORE; 25 | import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 26 | import static org.objectweb.asm.Opcodes.RETURN; 27 | import static org.objectweb.asm.Opcodes.V1_5; 28 | 29 | import java.io.FileOutputStream; 30 | import java.lang.reflect.Constructor; 31 | import java.lang.reflect.InvocationTargetException; 32 | import java.lang.reflect.Method; 33 | import java.lang.reflect.Modifier; 34 | import java.util.List; 35 | 36 | import org.junit.runner.Description; 37 | import org.junit.runner.Runner; 38 | import org.junit.runner.notification.Failure; 39 | import org.junit.runner.notification.RunNotifier; 40 | import org.junit.runners.JUnit4; 41 | import org.junit.runners.Parameterized; 42 | import org.objectweb.asm.ClassWriter; 43 | import org.objectweb.asm.Label; 44 | import org.objectweb.asm.MethodVisitor; 45 | import org.objectweb.asm.Type; 46 | import org.objectweb.asm.commons.GeneratorAdapter; 47 | 48 | import com.nitorcreations.junit.runners.parameterized.ParameterizedSuiteBuilder.TestInstantiatorBuilder; 49 | 50 | /** 51 | * This runner supports wrapping most regular junit runners while adding the 52 | * logical functionality of {@link Parameterized}. 53 | *

54 | * Example usage: 55 | * 56 | *

 57 |  * @RunWith(WrappingParameterizedRunner.class)
 58 |  * @WrappedRunWith(SpringJUnit4ClassRunner.class)
 59 |  * @ContextConfiguration(...) // used by SpringJUnit4ClassRunner
 60 |  * public class MyTest {
 61 |  *   @ParameterizedSuite
 62 |  *   public static void suite(ParameterizedSuiteBuilder builder) {
 63 |  *     builder.add().withConstructor("foo").named("try foo");
 64 |  *     builder.add().withConstructor("bar").named("try bar");
 65 |  *   }
 66 |  *   
 67 |  *   private String str;
 68 |  *   
 69 |  *   public MyTest(String str) {
 70 |  *     this.str = str;
 71 |  *   }
 72 |  *   
 73 |  *   @Test
 74 |  *   public void testSomething( ) {
 75 |  *     ...
 76 |  *   }
 77 |  *   @Test
 78 |  *   public void testSomethingElse() {
 79 |  *     ...
 80 |  *   }
 81 |  * }
 82 |  * 
83 | * 84 | * The withConstructor() calls validates constructor parameters in-place so you 85 | * get immediate feedback if you supply the wrong parameters. 86 | *

87 | * The named() calls provides a description of the test set, seen in 88 | * the test tree: 89 | *

    90 | *
  • MyTest 91 | *
      92 | *
    • try foo 93 | *
        94 | *
      • testSomething()
      • 95 | *
      • testSomethingElse()
      • 96 | *
      97 | *
    • 98 | *
    • try bar 99 | *
        100 | *
      • testSomething()
      • 101 | *
      • testSomethingElse()
      • 102 | *
      103 | *
    • 104 | *
    105 | *
  • 106 | *
107 | */ 108 | public class WrappingParameterizedRunner extends Runner { 109 | 110 | private static final class FailingRunner extends Runner { 111 | private final Description description; 112 | private final RuntimeException exception; 113 | 114 | FailingRunner(Description description, 115 | RuntimeException exception) { 116 | this.description = description; 117 | this.exception = exception; 118 | } 119 | 120 | @Override 121 | public void run(RunNotifier notifier) { 122 | notifier.fireTestFailure(new Failure(getDescription(), exception)); 123 | } 124 | 125 | @Override 126 | public Description getDescription() { 127 | return description; 128 | } 129 | } 130 | 131 | private final Class testClass; 132 | private final DescriptionMapper descriptionMapper; 133 | private final Constructor wrappedRunnerConstructor; 134 | private Description description; 135 | private Runner[] runners; 136 | private String[] testDescriptions; 137 | private Exception totalFailure; 138 | 139 | public WrappingParameterizedRunner(Class testClass) 140 | throws NoSuchMethodException, SecurityException, 141 | InstantiationException, IllegalAccessException, 142 | IllegalArgumentException, InvocationTargetException, 143 | ClassNotFoundException { 144 | if (Modifier.isFinal(testClass.getModifiers())) { 145 | throw new IllegalArgumentException( 146 | "Cannot operate on test classes marked final: " + testClass); 147 | } 148 | this.testClass = testClass; 149 | descriptionMapper = new DescriptionMapper(testClass); 150 | Class wrappedRunnerClass = JUnit4.class; 151 | WrappedRunWith wrappedRunWith = testClass 152 | .getAnnotation(WrappedRunWith.class); 153 | if (wrappedRunWith != null) { 154 | wrappedRunnerClass = wrappedRunWith.value(); 155 | } 156 | this.wrappedRunnerConstructor = wrappedRunnerClass 157 | .getConstructor(Class.class); 158 | } 159 | 160 | /** 161 | * Initialize the runner so it can provide a description and run the 162 | * contained tests. 163 | */ 164 | private void ensureSetup() { 165 | if (runners != null) { 166 | return; 167 | } 168 | try { 169 | List tests = fetchTests(); 170 | 171 | runners = new Runner[tests.size()]; 172 | testDescriptions = new String[runners.length]; 173 | int i = 0; 174 | for (TestInstantiatorBuilder test : tests) { 175 | String errorMsg = "Failed to get test descriptions"; 176 | try { 177 | testDescriptions[i] = test.getDescription(); 178 | errorMsg = "Failed to create test class"; 179 | Class parameterizedTestClass = createParameterizedTestClass(i, test); 180 | errorMsg = "Failed to create runner for test"; 181 | runners[i] = wrappedRunnerConstructor 182 | .newInstance(parameterizedTestClass); 183 | } catch (ParameterizationStrategyNotAvailableException e) { 184 | throw e; 185 | } catch (Exception e) { 186 | final RuntimeException exception = new RuntimeException(errorMsg, e); 187 | if (testDescriptions[i] == null) { 188 | testDescriptions[i] = "Test " +i; 189 | } 190 | final Description dummyDescription = Description.createSuiteDescription("Dummy " + (System.identityHashCode(this) + i)); 191 | runners[i] = new FailingRunner(dummyDescription, exception); 192 | } 193 | ++i; 194 | } 195 | } catch (RuntimeException e) { 196 | totalFailure = e; 197 | runners = null; 198 | testDescriptions = null; 199 | } 200 | } 201 | 202 | /** 203 | * Fetch the tests to run by calling the method annotated with @ 204 | * {@link ParameterizedSuite}. 205 | * 206 | * @return list of tests to run 207 | */ 208 | private List fetchTests() { 209 | Method foundMethod = getParameterizedSuiteMethod(); 210 | ParameterizedSuiteBuilder builder = new ParameterizedSuiteBuilder( 211 | testClass); 212 | try { 213 | foundMethod.invoke(null, builder); 214 | } catch (Throwable t) { 215 | throw new RuntimeException( 216 | "Failed to build parameterized suite for " 217 | + testClass.getName(), t); 218 | } 219 | List tests = builder.getTests(); 220 | return tests; 221 | } 222 | 223 | /** 224 | * Find the single public static method in test class that is annotated with @ 225 | * {@link ParameterizedSuite}. 226 | */ 227 | private Method getParameterizedSuiteMethod() { 228 | Method foundMethod = null; 229 | for (Method method : testClass.getMethods()) { 230 | if (null == method.getAnnotation(ParameterizedSuite.class)) { 231 | continue; 232 | } 233 | if (foundMethod == null) { 234 | foundMethod = method; 235 | } else { 236 | throw new IllegalArgumentException("Class " 237 | + testClass.getName() 238 | + " contains multiple methods annotated with @" 239 | + ParameterizedSuite.class.getSimpleName() 240 | + ", expected exactly one"); 241 | } 242 | } 243 | if (foundMethod == null) { 244 | throw new IllegalArgumentException("Class " + testClass.getName() 245 | + " must have a static method annotated with @" 246 | + ParameterizedSuite.class.getSimpleName()); 247 | } 248 | if (!Modifier.isStatic(foundMethod.getModifiers())) { 249 | throw new IllegalArgumentException("Class " + testClass.getName() 250 | + " method " + foundMethod + " must be static"); 251 | } 252 | Class[] parameterTypes = foundMethod.getParameterTypes(); 253 | if (parameterTypes.length != 1 254 | || parameterTypes[0] != ParameterizedSuiteBuilder.class) { 255 | throw new IllegalArgumentException("Class " + testClass.getName() 256 | + " method " + foundMethod 257 | + " must take exactly one argument of type " 258 | + ParameterizedSuiteBuilder.class.getSimpleName()); 259 | } 260 | return foundMethod; 261 | } 262 | 263 | /** 264 | * Create new class that extends the test class and has a single public 265 | * no-argument constructor that calls the wanted superclass (= test class) 266 | * constructor with wanted arguments. This fulfills the "normal" fingerprint 267 | * expected by regular test runners. 268 | * 269 | * @param testIdx 270 | * the test index (in the parameterized set) 271 | * @param test 272 | * the test instantiator instance which contains the constructor 273 | * to use and arguments to call it with. 274 | * @return newly created class extending the test class. 275 | */ 276 | private Class createParameterizedTestClass(int testIdx, 277 | TestInstantiatorBuilder test) { 278 | ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); 279 | String testClassNameRaw = Type.getInternalName(testClass); 280 | String extendingTestClassName = testClass.getName() + '_' + testIdx; 281 | String extendingTestClassNameRaw = testClassNameRaw + '_' + testIdx; 282 | 283 | ParameterizationStrategy strategy = chooseParameterizationStrategy(); 284 | 285 | // create a new class that extends the test class 286 | cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, extendingTestClassNameRaw, null, 287 | testClassNameRaw, null); 288 | 289 | // let the strategy create any fields and annotations necessary 290 | strategy.classCreationInProgress(extendingTestClassNameRaw, cw); 291 | 292 | // create no-arg constructor expected by "normal" runners 293 | // calls the superclass (e.g. original test class) constructor with the 294 | // parameters for this test) 295 | MethodVisitor mvo = cw.visitMethod(ACC_PUBLIC, "", "()V", null, 296 | null); 297 | GeneratorAdapter mv = new GeneratorAdapter(mvo, ACC_PUBLIC, "", 298 | "()V"); 299 | mv.visitCode(); 300 | Label label = new Label(); 301 | mv.visitLabel(label); 302 | mv.visitLineNumber(1, label); 303 | // ask strategy to emit the bytecode to load the Object[] containing the 304 | // constructor args for this test 305 | strategy.loadConstructorArgs(mv); 306 | mv.visitVarInsn(ASTORE, 1); 307 | mv.loadThis(); 308 | Type constructorType = Type.getType(test.getConstructor()); 309 | Type[] argumentTypes = constructorType.getArgumentTypes(); 310 | for (int arg = 0; arg < test.getConstructorArgs().length; ++arg) { 311 | Type argType = argumentTypes[arg]; 312 | mv.visitVarInsn(ALOAD, 1); 313 | mv.push(arg); 314 | mv.visitInsn(AALOAD); 315 | mv.unbox(argType); 316 | } 317 | String constructorArgsRaw = constructorType.getDescriptor(); 318 | mv.visitMethodInsn(INVOKESPECIAL, testClassNameRaw, "", 319 | constructorArgsRaw); 320 | mv.visitInsn(RETURN); 321 | mv.visitMaxs(0, 0); // dummy values 322 | mv.visitEnd(); 323 | 324 | // done, convert class to byte array 325 | cw.visitEnd(); 326 | byte[] clazzBytes = cw.toByteArray(); 327 | 328 | /* ** DEBUG export class to /tmp/x.class ** 329 | try { 330 | FileOutputStream fos = new FileOutputStream("/tmp/x.class"); 331 | fos.write(clazzBytes); 332 | fos.close(); 333 | } catch (Exception e1) { 334 | e1.printStackTrace(); 335 | } 336 | ** /DEBUG ** */ 337 | 338 | // inject the new class into the current classloader 339 | Class clazz = ClassLoaderInjector.injectClass( 340 | extendingTestClassName, clazzBytes); 341 | // let the strategy complete initialization of the newly created class 342 | strategy.classConstructed(clazz, clazzBytes, test.getConstructorArgs()); 343 | return clazz; 344 | } 345 | 346 | private ParameterizationStrategy chooseParameterizationStrategy() { 347 | if (wrappedRunnerConstructor.getDeclaringClass().getPackage().getName() 348 | .contains("powermock")) { 349 | return new PowermockParameterizationStrategy(); 350 | } 351 | return new DefaultParameterizationStrategy(); 352 | } 353 | 354 | @Override 355 | public synchronized Description getDescription() { 356 | if (description == null) { 357 | ensureSetup(); 358 | Description d = Description.createSuiteDescription(testClass); 359 | if (totalFailure == null) { 360 | for (int i = 0; i < runners.length; ++i) { 361 | String testDescription = i + ". " + testDescriptions[i]; 362 | Runner runner = runners[i]; 363 | d.addChild(descriptionMapper.rewriteDescription( 364 | testDescription, runner.getDescription())); 365 | } 366 | } 367 | description = d; 368 | } 369 | return description; 370 | } 371 | 372 | @Override 373 | public synchronized void run(RunNotifier notifier) { 374 | getDescription(); 375 | if (totalFailure != null) { 376 | notifier.fireTestFailure(new Failure(getDescription(), totalFailure)); 377 | } else { 378 | notifier = new DescriptionMappingRunNotifierProxy(notifier, 379 | descriptionMapper); 380 | for (Runner runner : runners) { 381 | runner.run(notifier); 382 | } 383 | } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /junit-runners/src/test/java/com/nitorcreations/junit/runners/parameterized/WrappingParameterizedRunnerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Nitor Creations Oy 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.nitorcreations.junit.runners.parameterized; 17 | 18 | import static org.hamcrest.CoreMatchers.is; 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertNotNull; 21 | import static org.junit.Assert.assertThat; 22 | import static org.mockito.ArgumentCaptor.forClass; 23 | import static org.mockito.Mockito.mock; 24 | import static org.mockito.Mockito.times; 25 | import static org.mockito.Mockito.verify; 26 | import static org.mockito.Mockito.verifyNoMoreInteractions; 27 | 28 | import java.io.Serializable; 29 | import java.util.Arrays; 30 | import java.util.List; 31 | 32 | import org.junit.Test; 33 | import org.junit.experimental.runners.Enclosed; 34 | import org.junit.runner.Description; 35 | import org.junit.runner.RunWith; 36 | import org.junit.runner.notification.Failure; 37 | import org.junit.runner.notification.RunListener; 38 | import org.junit.runner.notification.RunNotifier; 39 | import org.junit.runners.JUnit4; 40 | import org.mockito.ArgumentCaptor; 41 | import org.mockito.Mock; 42 | import org.mockito.runners.MockitoJUnitRunner; 43 | import org.powermock.api.mockito.PowerMockito; 44 | import org.powermock.core.classloader.annotations.PrepareForTest; 45 | import org.powermock.modules.junit4.PowerMockRunner; 46 | import org.slf4j.LoggerFactory; 47 | import org.springframework.beans.factory.annotation.Autowired; 48 | import org.springframework.context.annotation.Bean; 49 | import org.springframework.context.annotation.Configuration; 50 | import org.springframework.test.context.ContextConfiguration; 51 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 52 | 53 | @RunWith(Enclosed.class) 54 | public class WrappingParameterizedRunnerTest { 55 | 56 | @RunWith(Enclosed.class) 57 | public static class different_runners { 58 | /** 59 | * JUnit4 is the default JUnit runner, so this should behave exactly as 60 | * the normal Parameterized runner. 61 | */ 62 | @RunWith(WrappingParameterizedRunner.class) 63 | @WrappedRunWith(JUnit4.class) 64 | public static class using_JUnit4 { 65 | @ParameterizedSuite 66 | public static void suite(ParameterizedSuiteBuilder builder) { 67 | builder.constructWith("foo"); 68 | builder.constructWith("bar"); 69 | } 70 | 71 | private String s; 72 | 73 | public using_JUnit4(String s) { 74 | this.s = s; 75 | } 76 | 77 | @Test 78 | public void test1() { 79 | LoggerFactory.getLogger(getClass()).info("test1() s=" + s); 80 | } 81 | 82 | @Test 83 | public void test2() { 84 | LoggerFactory.getLogger(getClass()).info("test2() s=" + s); 85 | } 86 | } 87 | 88 | /** 89 | * Test using default runner, which is JUnit4. 90 | */ 91 | @RunWith(WrappingParameterizedRunner.class) 92 | public static class using_default_runner { 93 | @ParameterizedSuite 94 | public static void suite(ParameterizedSuiteBuilder builder) { 95 | builder.constructWith("foo"); 96 | } 97 | 98 | private String s; 99 | 100 | public using_default_runner(String s) { 101 | this.s = s; 102 | } 103 | 104 | @Test 105 | public void test() { 106 | LoggerFactory.getLogger(getClass()).info("test() s=" + s); 107 | } 108 | } 109 | 110 | @RunWith(WrappingParameterizedRunner.class) 111 | @WrappedRunWith(MockitoJUnitRunner.class) 112 | public static class using_MockitoJUnitRunner { 113 | @ParameterizedSuite 114 | public static void suite(ParameterizedSuiteBuilder builder) { 115 | builder.constructWith("foo"); 116 | builder.constructWith("bar"); 117 | } 118 | 119 | private String s; 120 | 121 | @Mock 122 | private Runnable mock; 123 | 124 | public using_MockitoJUnitRunner(String s) { 125 | this.s = s; 126 | } 127 | 128 | @Test 129 | public void test1() { 130 | LoggerFactory.getLogger(getClass()).info("test1() s=" + s); 131 | } 132 | 133 | @Test 134 | public void testMockExists() { 135 | assertNotNull(mock); 136 | } 137 | 138 | @Test 139 | public void test2() { 140 | LoggerFactory.getLogger(getClass()).info("test2() s=" + s); 141 | } 142 | } 143 | 144 | @RunWith(WrappingParameterizedRunner.class) 145 | @WrappedRunWith(PowerMockRunner.class) 146 | @PrepareForTest(using_PowerMockRunner.help.class) 147 | public static class using_PowerMockRunner { 148 | @ParameterizedSuite 149 | public static void suite(ParameterizedSuiteBuilder builder) { 150 | builder.constructWith("foo"); 151 | builder.constructWith("bar"); 152 | } 153 | 154 | private String s; 155 | 156 | public using_PowerMockRunner(String s) { 157 | this.s = s; 158 | } 159 | 160 | public static class help { 161 | /** 162 | * A final method that we override with powermock 163 | */ 164 | public final String get() { 165 | return "should be overridden"; 166 | } 167 | } 168 | 169 | @Test 170 | public void test1() { 171 | LoggerFactory.getLogger(getClass()).info("test() s=" + s); 172 | help mock = PowerMockito.mock(help.class); 173 | PowerMockito.when(mock.get()).thenReturn("yeah"); 174 | assertThat(mock.get(), is("yeah")); 175 | } 176 | 177 | @Test 178 | public void test2() { 179 | LoggerFactory.getLogger(getClass()).info("test() s=" + s); 180 | help mock = PowerMockito.mock(help.class); 181 | PowerMockito.when(mock.get()).thenReturn("joo"); 182 | assertThat(mock.get(), is("joo")); 183 | } 184 | } 185 | 186 | @RunWith(WrappingParameterizedRunner.class) 187 | @WrappedRunWith(SpringJUnit4ClassRunner.class) 188 | @ContextConfiguration(classes = { using_SpringJUnit4ClassRunner.TestConfig.class }) 189 | public static class using_SpringJUnit4ClassRunner { 190 | 191 | public interface MyInterface { 192 | String transform(String s); 193 | } 194 | 195 | @Configuration 196 | public static class TestConfig { 197 | @Bean 198 | MyInterface myInterface() { 199 | return new MyInterface() { 200 | public String transform(String s) { 201 | return s.toLowerCase() + s.toUpperCase(); 202 | } 203 | }; 204 | } 205 | } 206 | 207 | @ParameterizedSuite 208 | public static void suite(ParameterizedSuiteBuilder builder) { 209 | builder.constructWith("foo"); 210 | builder.constructWith("Bar"); 211 | } 212 | 213 | @Autowired 214 | private MyInterface myInterface; 215 | 216 | private String s; 217 | 218 | public using_SpringJUnit4ClassRunner(String s) { 219 | this.s = s; 220 | } 221 | 222 | @Test 223 | public void test1() { 224 | LoggerFactory.getLogger(getClass()).info( 225 | "test1() s=" + s + " transformed=" 226 | + myInterface.transform(s)); 227 | } 228 | 229 | @Test 230 | public void test2() { 231 | LoggerFactory.getLogger(getClass()).info( 232 | "test2() s=" 233 | + s 234 | + " transformed²=" 235 | + myInterface.transform(myInterface 236 | .transform(s))); 237 | } 238 | } 239 | } 240 | 241 | @RunWith(WrappingParameterizedRunner.class) 242 | @WrappedRunWith(JUnit4.class) 243 | public static class features { 244 | private String s; 245 | private final Integer io; 246 | private final double f; 247 | private final Object x; 248 | private final byte b; 249 | 250 | @ParameterizedSuite 251 | public static void suite(ParameterizedSuiteBuilder builder) { 252 | builder.constructWith("foo", 2, 3.2f, new Object(), (byte) 200) 253 | .named("Hello 1"); 254 | builder.constructWith("bar", 4, 5.0f, "kala", (byte) 5); 255 | builder.constructWith(null, null, 5.0f, null, (byte) 7) 256 | .named("Yoo"); 257 | } 258 | 259 | public features(String s, Integer io, float f, Object x, byte b) { 260 | this.s = s; 261 | this.f = f; 262 | this.io = io; 263 | this.x = x; 264 | this.b = b; 265 | } 266 | 267 | @Test 268 | public void test() { 269 | LoggerFactory.getLogger(getClass()).info( 270 | "test() s=" + s + " " + io + " " + f + " " + x + " " + b); 271 | } 272 | 273 | @Test 274 | public void test2() { 275 | LoggerFactory.getLogger(getClass()).info("test2() " + s); 276 | } 277 | } 278 | 279 | @RunWith(WrappingParameterizedRunner.class) 280 | @WrappedRunWith(JUnit4.class) 281 | public static class constructor_variants { 282 | 283 | @ParameterizedSuite 284 | public static void suite(ParameterizedSuiteBuilder builder) { 285 | builder.constructWith("foo", ""); 286 | builder.constructWith("foo", new Object()); 287 | builder.constructWith(new Serializable() { 288 | private static final long serialVersionUID = -2542951035340069284L; 289 | }, ""); 290 | builder.constructWith(new Serializable() { 291 | private static final long serialVersionUID = -3956496975645452784L; 292 | }, new Object()); 293 | builder.constructWith(new Object(), "x"); 294 | } 295 | 296 | public constructor_variants(Object s, Object o) { 297 | LoggerFactory.getLogger(getClass()).info("wrong"); 298 | } 299 | 300 | public constructor_variants(Serializable s, String o) { 301 | LoggerFactory.getLogger(getClass()).info("success2"); 302 | } 303 | 304 | public constructor_variants(String s, String o) { 305 | LoggerFactory.getLogger(getClass()).info("success0"); 306 | } 307 | 308 | public constructor_variants(Serializable s, Object o) { 309 | LoggerFactory.getLogger(getClass()).info("success3"); 310 | } 311 | 312 | public constructor_variants(String s, Object o) { 313 | LoggerFactory.getLogger(getClass()).info("success1"); 314 | } 315 | 316 | public constructor_variants(Object s, CharSequence o) { 317 | LoggerFactory.getLogger(getClass()).info("success4"); 318 | } 319 | 320 | @Test 321 | public void test() { 322 | } 323 | } 324 | 325 | public static class verify_successfully_constructed_suite_operation { 326 | @RunWith(WrappingParameterizedRunner.class) 327 | public static class SampleTestClass { 328 | @ParameterizedSuite 329 | public static void suite(ParameterizedSuiteBuilder builder) { 330 | builder.constructWith(2); 331 | builder.constructWith(3).named("hello"); 332 | } 333 | private final long d; 334 | public SampleTestClass(long d) { 335 | this.d = d; 336 | } 337 | @Test 338 | public void test1() { 339 | assertEquals(2L, d); 340 | } 341 | @Test 342 | public void test2() { 343 | assertEquals(1L, d); 344 | } 345 | } 346 | 347 | @Test 348 | public void ensure_proper_runner_operation() throws Exception { 349 | WrappingParameterizedRunner runner = new WrappingParameterizedRunner(SampleTestClass.class); 350 | 351 | Description runnerDescription = runner.getDescription(); 352 | 353 | final String testClassName = SampleTestClass.class.getName(); 354 | 355 | verifyDesc(runnerDescription , testClassName, testClassName, true); 356 | 357 | List subRunners = runnerDescription.getChildren(); 358 | assertEquals(2, subRunners.size()); 359 | verifyDesc(subRunners.get(0), "0. 2", testClassName, true); 360 | verifyDesc(subRunners.get(1), "1. hello", testClassName, true); 361 | 362 | List tests1 = verifyTestDescs(testClassName, subRunners.get(0).getChildren()); 363 | List tests2 = verifyTestDescs(testClassName, subRunners.get(1).getChildren()); 364 | 365 | RunNotifier notifier = new RunNotifier(); 366 | RunListener mockListener = mock(RunListener.class); 367 | notifier.addListener(mockListener); 368 | runner.run(notifier); 369 | 370 | ArgumentCaptor startCaptor = forClass(Description.class); 371 | ArgumentCaptor failureCaptor = forClass(Failure.class); 372 | ArgumentCaptor finishCaptor = forClass(Description.class); 373 | verify(mockListener, times(4)).testStarted(startCaptor .capture()); 374 | verify(mockListener, times(3)).testFailure(failureCaptor.capture()); 375 | verify(mockListener, times(4)).testFinished(finishCaptor .capture()); 376 | verifyNoMoreInteractions(mockListener); 377 | 378 | List starts = startCaptor.getAllValues(); 379 | List failures = failureCaptor.getAllValues(); 380 | List finishes = finishCaptor.getAllValues(); 381 | 382 | assertEquals(Arrays.asList(tests1.get(0), tests1.get(1), tests2.get(0), tests2.get(1)), starts); 383 | assertEquals(tests1.get(1), failures.get(0).getDescription()); 384 | assertEquals(tests2.get(0), failures.get(1).getDescription()); 385 | assertEquals(tests2.get(1), failures.get(2).getDescription()); 386 | assertEquals(Arrays.asList(tests1.get(0), tests1.get(1), tests2.get(0), tests2.get(1)), finishes); 387 | } 388 | 389 | private List verifyTestDescs(final String testClassName, List tests) { 390 | assertEquals(2, tests.size()); 391 | verifyDesc(tests.get(0), "test1(" + testClassName + ")", testClassName, false); 392 | verifyDesc(tests.get(1), "test2(" + testClassName + ")", testClassName, false); 393 | return tests; 394 | } 395 | 396 | private void verifyDesc(Description desc, String displayName, String className, boolean isSuite) { 397 | assertEquals(displayName, desc.getDisplayName()); 398 | assertEquals(className, desc.getClassName()); 399 | assertEquals(isSuite, desc.isSuite()); 400 | } 401 | } 402 | 403 | @RunWith(WrappingParameterizedRunner.class) 404 | public static class verify_suite_construction_failure_reporting { 405 | 406 | @RunWith(WrappingParameterizedRunner.class) 407 | public static class constructor_failure { 408 | static final String EXCEPTION_MESSAGE = "A01"; 409 | @ParameterizedSuite 410 | public static void suite(ParameterizedSuiteBuilder builder) { 411 | throw new RuntimeException(EXCEPTION_MESSAGE); 412 | } 413 | } 414 | 415 | static final String STATIC_EXCEPTION_MESSAGE = "456"; 416 | @RunWith(WrappingParameterizedRunner.class) 417 | public static class static_block_failure { 418 | static { 419 | if (true) { 420 | throw new RuntimeException(STATIC_EXCEPTION_MESSAGE); 421 | } 422 | } 423 | @ParameterizedSuite 424 | public static void suite(ParameterizedSuiteBuilder builder) { 425 | } 426 | } 427 | 428 | @RunWith(WrappingParameterizedRunner.class) 429 | public static class constructor_parameter_toString_failure { 430 | static final String EXCEPTION_MESSAGE = "123"; 431 | @ParameterizedSuite 432 | public static void suite(ParameterizedSuiteBuilder builder) { 433 | builder.constructWith(new Object() { 434 | @Override 435 | public String toString() { 436 | throw new RuntimeException(EXCEPTION_MESSAGE); 437 | } 438 | }); 439 | } 440 | public constructor_parameter_toString_failure(Object object) { } 441 | } 442 | 443 | @ParameterizedSuite 444 | public static void suite(ParameterizedSuiteBuilder builder) { 445 | builder.constructWith(constructor_failure.class); 446 | builder.constructWith(static_block_failure.class, STATIC_EXCEPTION_MESSAGE); 447 | builder.constructWith(constructor_parameter_toString_failure.class); 448 | } 449 | 450 | private final Class testClass; 451 | private final String exceptionMessage; 452 | 453 | public verify_suite_construction_failure_reporting(Class testClass) throws Exception { 454 | this(testClass, (String) testClass.getDeclaredField("EXCEPTION_MESSAGE").get(null)); 455 | } 456 | 457 | public verify_suite_construction_failure_reporting(Class testClass, String exceptionMessage) throws Exception { 458 | this.testClass = testClass; 459 | this.exceptionMessage = exceptionMessage; 460 | } 461 | 462 | @Test 463 | public void ensure_failure() throws Exception { 464 | WrappingParameterizedRunner runner = new WrappingParameterizedRunner(testClass); 465 | RunNotifier notifier = new RunNotifier(); 466 | RunListener mockListener = mock(RunListener.class); 467 | notifier.addListener(mockListener ); 468 | runner.run(notifier); 469 | ArgumentCaptor failureCaptor = forClass(Failure.class); 470 | verify(mockListener).testFailure(failureCaptor.capture()); 471 | verifyNoMoreInteractions(mockListener); 472 | Failure failure = failureCaptor.getValue(); 473 | Throwable e = failure.getException(); 474 | while (e.getCause() != null) { 475 | e = e.getCause(); 476 | } 477 | assertEquals(RuntimeException.class, e.getClass()); 478 | assertEquals(exceptionMessage, e.getMessage()); 479 | } 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /junit-runners/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.nitorcreations 5 | junit-runners 6 | 1.4-SNAPSHOT 7 | jar 8 | JUnit Runners 9 | JUnit4 Test Runners 10 | https://github.com/NitorCreations/CoreComponents 11 | 12 | scm:git:https://github.com/NitorCreations/CoreComponents.git 13 | scm:git:https://github.com/NitorCreations/CoreComponents.git 14 | https://github.com/NitorCreations/CoreComponents 15 | HEAD 16 | 17 | 18 | 3.1.1 19 | 20 | 21 | 22 | Macroz 23 | Markku Rontu 24 | markku.rontu@nitorcreations.com 25 | 26 | 27 | niom 28 | Marko Pukari 29 | marko.pukari@nitorcreations.com 30 | 31 | 32 | xkr47 33 | Jonas Berlin 34 | jonas.berlin@nitorcreations.com 35 | 36 | 37 | gmokki 38 | Mikko Tiihonen 39 | mikko.tiihonen@nitorcreations.com 40 | 41 | 42 | 43 | 44 | The Apache Software License, Version 2.0 45 | http://www.apache.org/licenses/LICENSE-2.0.txt 46 | repo 47 | 48 | 49 | 50 | UTF-8 51 | UTF-8 52 | 53 | 4.11 54 | 5.0.3 55 | 1.5.6 56 | 57 | 1.3.1 58 | 1.3 59 | 3.0.0 60 | 4.1.1.RELEASE 61 | 1.10.8 62 | 2.1 63 | 1.7.7 64 | 1.1.2 65 | 3.1 66 | 67 | 68 | 69 | 70 | 71 | maven-eclipse-plugin 72 | 2.9 73 | 74 | 75 | maven-compiler-plugin 76 | 3.2 77 | 78 | 79 | org.codehaus.mojo 80 | cobertura-maven-plugin 81 | 2.6 82 | 83 | 84 | maven-surefire-plugin 85 | 2.17 86 | 87 | 88 | maven-jar-plugin 89 | 2.5 90 | 91 | 92 | 93 | com.nitorcreations.junit.runner.StandaloneJUnitRunnerMain 94 | 95 | 96 | 97 | 98 | 99 | maven-source-plugin 100 | 2.4 101 | 102 | 103 | maven-javadoc-plugin 104 | 2.10.1 105 | 106 | 107 | maven-site-plugin 108 | 3.4 109 | 110 | 111 | maven-install-plugin 112 | 2.5.2 113 | 114 | 115 | maven-release-plugin 116 | 2.5.1 117 | 118 | 119 | maven-resources-plugin 120 | 2.7 121 | 122 | 123 | maven-clean-plugin 124 | 2.6 125 | 126 | 127 | maven-deploy-plugin 128 | 2.8.2 129 | 130 | 131 | maven-enforcer-plugin 132 | ${maven.enforcer.version} 133 | 134 | 135 | 136 | 137 | 138 | org.sonatype.plugins 139 | nexus-staging-maven-plugin 140 | 1.6.5 141 | true 142 | 143 | ossrh 144 | https://oss.sonatype.org/ 145 | true 146 | 147 | 148 | 149 | maven-eclipse-plugin 150 | 151 | true 152 | false 153 | .eclipse_classes 154 | true 155 | 156 | 157 | 158 | true 159 | maven-compiler-plugin 160 | 161 | 1.5 162 | 1.5 163 | 164 | 165 | 166 | maven-source-plugin 167 | 168 | 169 | attach-sources 170 | verify 171 | 172 | jar-no-fork 173 | 174 | 175 | 176 | 177 | 178 | maven-javadoc-plugin 179 | 180 | 181 | attach-javadocs 182 | 183 | jar 184 | 185 | 186 | 187 | 188 | 189 | maven-surefire-plugin 190 | 191 | 192 | **/*Test.java 193 | 194 | random 195 | never 196 | true 197 | 198 | 199 | 200 | maven-enforcer-plugin 201 | 202 | 203 | ban-unwanted-dependencies 204 | 205 | enforce 206 | 207 | 208 | 209 | 210 | 211 | 212 | commons-logging:commons-logging 213 | log4j:log4j 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | junit 228 | junit 229 | ${junit.version} 230 | 231 | 232 | org.ow2.asm 233 | asm 234 | ${asm.version} 235 | 236 | 237 | org.ow2.asm 238 | asm-commons 239 | ${asm.version} 240 | 241 | 242 | org.hamcrest 243 | hamcrest-library 244 | ${hamcrest.version} 245 | 246 | 247 | org.mockito 248 | mockito-core 249 | ${mockito.core.version} 250 | 251 | 252 | org.objenesis 253 | objenesis 254 | ${objenesis.version} 255 | 256 | 257 | org.powermock 258 | powermock-core 259 | ${powermock.version} 260 | 261 | 262 | org.powermock 263 | powermock-module-junit4 264 | ${powermock.version} 265 | 266 | 267 | org.powermock 268 | powermock-api-mockito 269 | ${powermock.version} 270 | 271 | 272 | org.springframework 273 | spring-core 274 | ${spring.core.version} 275 | 276 | 277 | commons-logging 278 | commons-logging 279 | 280 | 281 | 282 | 283 | org.springframework 284 | spring-context 285 | ${spring.core.version} 286 | 287 | 288 | org.springframework 289 | spring-test 290 | ${spring.core.version} 291 | 292 | 293 | ch.qos.logback 294 | logback-classic 295 | ${logback.version} 296 | 297 | 298 | org.slf4j 299 | slf4j-api 300 | ${slf4j.version} 301 | 302 | 303 | org.slf4j 304 | jcl-over-slf4j 305 | ${slf4j.version} 306 | 307 | 308 | cglib 309 | cglib-nodep 310 | ${cglib.version} 311 | 312 | 313 | com.google.code.findbugs 314 | annotations 315 | ${findbugs.annotations.version} 316 | provided 317 | 318 | 319 | 320 | 321 | 322 | junit 323 | junit 324 | 325 | 326 | org.ow2.asm 327 | asm 328 | 329 | 330 | org.ow2.asm 331 | asm-commons 332 | 333 | 334 | org.powermock 335 | powermock-core 336 | 337 | 338 | 339 | org.hamcrest 340 | hamcrest-library 341 | test 342 | 343 | 344 | org.mockito 345 | mockito-core 346 | test 347 | 348 | 349 | org.objenesis 350 | objenesis 351 | test 352 | 353 | 354 | org.powermock 355 | powermock-module-junit4 356 | test 357 | 358 | 359 | org.powermock 360 | powermock-api-mockito 361 | test 362 | 363 | 364 | org.springframework 365 | spring-core 366 | test 367 | 368 | 369 | org.springframework 370 | spring-context 371 | test 372 | 373 | 374 | org.springframework 375 | spring-test 376 | test 377 | 378 | 379 | ch.qos.logback 380 | logback-classic 381 | test 382 | 383 | 384 | org.slf4j 385 | slf4j-api 386 | test 387 | 388 | 389 | org.slf4j 390 | jcl-over-slf4j 391 | test 392 | 393 | 394 | cglib 395 | cglib-nodep 396 | test 397 | 398 | 399 | com.google.code.findbugs 400 | annotations 401 | 402 | 403 | 404 | 405 | 406 | org.codehaus.mojo 407 | findbugs-maven-plugin 408 | 3.0.0 409 | 410 | true 411 | Max 412 | Low 413 | 414 | 415 | 416 | 417 | org.codehaus.mojo 418 | cobertura-maven-plugin 419 | 2.6 420 | 421 | 422 | xml 423 | 424 | 425 | 426 | 427 | maven-pmd-plugin 428 | 3.2 429 | 430 | true 431 | 100 432 | 1.6 433 | 434 | 435 | **/test/*.java 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | release 444 | 445 | 446 | 447 | org.apache.maven.plugins 448 | maven-source-plugin 449 | 450 | 451 | attach-sources 452 | 453 | jar-no-fork 454 | 455 | 456 | 457 | 458 | 459 | org.apache.maven.plugins 460 | maven-javadoc-plugin 461 | 462 | 463 | attach-javadocs 464 | 465 | jar 466 | 467 | 468 | 469 | 470 | 471 | org.apache.maven.plugins 472 | maven-gpg-plugin 473 | 1.5 474 | 475 | 476 | sign-artifacts 477 | verify 478 | 479 | sign 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | --------------------------------------------------------------------------------