├── burst-junit4 ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── squareup │ │ │ └── burst │ │ │ ├── Snack.java │ │ │ ├── Soda.java │ │ │ ├── Magazine.java │ │ │ ├── ConstructorNoArgumentTest.java │ │ │ ├── SingleFieldTest.java │ │ │ ├── FieldAndMethodTest.java │ │ │ ├── ConstructorSingleArgumentTest.java │ │ │ ├── MultipleFieldsTest.java │ │ │ ├── ConstructorSingleNamedArgumentTest.java │ │ │ ├── MethodTest.java │ │ │ ├── MultipleNamedFieldsTest.java │ │ │ ├── NamedFieldAndNamedMethodTest.java │ │ │ ├── ConstructorMultipleArgumentTest.java │ │ │ ├── ConstructorAndMethodTest.java │ │ │ ├── NamedMethodTest.java │ │ │ ├── ConstructorMultipleNamedArgumentTest.java │ │ │ ├── ParentRunnerSpyTest.java │ │ │ ├── BeforeAndAfterClassTest.java │ │ │ ├── ClassRuleTest.java │ │ │ ├── RuleUsingAnnotationsTest.java │ │ │ ├── JournalingListener.java │ │ │ └── BurstJUnit4Test.java │ └── main │ │ └── java │ │ └── com │ │ └── squareup │ │ └── burst │ │ ├── BurstMethod.java │ │ ├── ParentRunnerSpy.java │ │ ├── BurstJUnit4.java │ │ └── BurstRunner.java └── pom.xml ├── .gitignore ├── .buildscript ├── settings.xml └── deploy_snapshot.sh ├── burst ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── squareup │ │ │ └── burst │ │ │ ├── Util.java │ │ │ ├── annotation │ │ │ ├── Burst.java │ │ │ └── Name.java │ │ │ ├── BurstableConstructor.java │ │ │ ├── TestConstructor.java │ │ │ └── Burst.java │ └── test │ │ └── java │ │ └── com │ │ └── squareup │ │ └── burst │ │ ├── TestUtil.java │ │ ├── BurstableConstructorTest.java │ │ └── BurstTest.java └── pom.xml ├── .travis.yml ├── CONTRIBUTING.md ├── deploy_javadoc.sh ├── CHANGELOG.md ├── pom.xml ├── README.md ├── checkstyle.xml └── LICENSE.txt /burst-junit4/src/test/java/com/squareup/burst/Snack.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | public enum Snack { 4 | CHIPS, NUTS, CANDY 5 | } 6 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/Soda.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | public enum Soda { 4 | PEPSI, COKE, RC_COLA; 5 | } 6 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/Magazine.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | public enum Magazine { 4 | TIME, COSMO, POPULAR_SCIENCE 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | eclipsebin 5 | 6 | bin 7 | gen 8 | build 9 | out 10 | lib 11 | 12 | target 13 | pom.xml.* 14 | release.properties 15 | 16 | .idea 17 | *.iml 18 | *.ipr 19 | *.iws 20 | classes 21 | 22 | obj 23 | 24 | .DS_Store 25 | -------------------------------------------------------------------------------- /.buildscript/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sonatype-nexus-snapshots 5 | ${env.CI_DEPLOY_USERNAME} 6 | ${env.CI_DEPLOY_PASSWORD} 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /burst/src/main/java/com/squareup/burst/Util.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | final class Util { 4 | static T checkNotNull(T o, String message) { 5 | if (o == null) { 6 | throw new NullPointerException(message); 7 | } 8 | return o; 9 | } 10 | 11 | private Util() { 12 | throw new AssertionError("No instances."); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ConstructorNoArgumentTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | 6 | import static org.junit.Assert.assertTrue; 7 | 8 | @RunWith(BurstJUnit4.class) 9 | public final class ConstructorNoArgumentTest { 10 | @Test public void testMethod() { 11 | assertTrue(true); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/SingleFieldTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import com.squareup.burst.annotation.Burst; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public class SingleFieldTest { 11 | @Burst public Soda soda; 12 | 13 | @Test public void testMethod() { 14 | assertThat(soda).isNotNull(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/FieldAndMethodTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import com.squareup.burst.annotation.Burst; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public class FieldAndMethodTest { 11 | @Burst public Snack snack; 12 | 13 | @Test public void testMethod(Soda soda) { 14 | assertThat(snack).isNotNull(); 15 | assertThat(soda).isNotNull(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ConstructorSingleArgumentTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | 6 | import static org.junit.Assert.assertNotNull; 7 | 8 | @RunWith(BurstJUnit4.class) 9 | public final class ConstructorSingleArgumentTest { 10 | private final Soda soda; 11 | 12 | public ConstructorSingleArgumentTest(Soda soda) { 13 | this.soda = soda; 14 | } 15 | 16 | @Test public void testMethod() { 17 | assertNotNull(soda); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/MultipleFieldsTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import com.squareup.burst.annotation.Burst; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public class MultipleFieldsTest { 11 | @Burst public Soda soda; 12 | @Burst public Snack snack; 13 | 14 | @Test public void testMethod() { 15 | assertThat(soda).isNotNull(); 16 | assertThat(snack).isNotNull(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ConstructorSingleNamedArgumentTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Name; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import static org.junit.Assert.assertNotNull; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public final class ConstructorSingleNamedArgumentTest { 11 | private final Soda soda; 12 | 13 | public ConstructorSingleNamedArgumentTest(@Name("Drink") Soda soda) { 14 | this.soda = soda; 15 | } 16 | 17 | @Test public void testMethod() { 18 | assertNotNull(soda); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/MethodTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | 6 | import static org.junit.Assert.assertNotNull; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public final class MethodTest { 11 | @Test public void none() { 12 | assertTrue(true); 13 | } 14 | 15 | @Test public void single(Soda soda) { 16 | assertNotNull(soda); 17 | } 18 | 19 | @Test public void multiple(Soda soda, Snack snack) { 20 | assertNotNull(soda); 21 | assertNotNull(snack); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/MultipleNamedFieldsTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Burst; 4 | import com.squareup.burst.annotation.Name; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @RunWith(BurstJUnit4.class) 11 | public class MultipleNamedFieldsTest { 12 | @Burst @Name("Drink") public Soda soda; 13 | @Burst public Snack snack; 14 | 15 | @Test public void testMethod() { 16 | assertThat(soda).isNotNull(); 17 | assertThat(snack).isNotNull(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/NamedFieldAndNamedMethodTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Burst; 4 | import com.squareup.burst.annotation.Name; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | @RunWith(BurstJUnit4.class) 11 | public class NamedFieldAndNamedMethodTest { 12 | @Burst @Name("Food") public Snack snack; 13 | 14 | @Test public void testMethod(@Name("Drink") Soda soda) { 15 | assertThat(snack).isNotNull(); 16 | assertThat(soda).isNotNull(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ConstructorMultipleArgumentTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | 6 | import static org.junit.Assert.assertNotNull; 7 | 8 | @RunWith(BurstJUnit4.class) 9 | public final class ConstructorMultipleArgumentTest { 10 | private final Soda soda; 11 | private final Snack snack; 12 | 13 | public ConstructorMultipleArgumentTest(Soda soda, Snack snack) { 14 | this.soda = soda; 15 | this.snack = snack; 16 | } 17 | 18 | @Test public void testMethod() { 19 | assertNotNull(soda); 20 | assertNotNull(snack); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ConstructorAndMethodTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | 6 | import static org.junit.Assert.assertNotNull; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public final class ConstructorAndMethodTest { 11 | private final Soda soda; 12 | 13 | public ConstructorAndMethodTest(Soda soda) { 14 | this.soda = soda; 15 | } 16 | 17 | @Test public void none() { 18 | assertTrue(true); 19 | } 20 | 21 | @Test public void single(Snack snack) { 22 | assertNotNull(soda); 23 | assertNotNull(snack); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | 6 | after_success: 7 | - .buildscript/deploy_snapshot.sh 8 | 9 | env: 10 | global: 11 | - secure: "I3+AlmRguLTShB7E411tgW0/u2oMWTdNO9Re27KjRanHERLDWRDsjOHHaKoK2MZ5Mv6k8KogfRzgongChJXNyVCoKoFj4kowMzzQypEL/bXNj7YNXmDbFUpDSaxlBPnbCAL03ZoHbH1r60ZZQY+w/p8HHLFfwQSRHiuxW2iZCjY=" 12 | - secure: "EBmDKP8/wGPfP6NTb8P3WOfLeFwTpJwQsajTkKTpeVtEvRr6cpKe1W85nQgdiWed5j7Tw6br7E937uYZP9s54TFakq8uMw9OQrHzboN+fYc41sDQoxobZLypHFt3kxoyWkOKu18P5timvMXUdXge2/A0CTj4ZKG6z1N6vhY8MM0=" 13 | 14 | branches: 15 | except: 16 | - gh-pages 17 | 18 | notifications: 19 | email: false 20 | 21 | sudo: false 22 | 23 | cache: 24 | directories: 25 | - $HOME/.m2 26 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/NamedMethodTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Name; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import static org.junit.Assert.assertNotNull; 8 | import static org.junit.Assert.assertTrue; 9 | 10 | @RunWith(BurstJUnit4.class) 11 | public final class NamedMethodTest { 12 | @Test public void none() { 13 | assertTrue(true); 14 | } 15 | 16 | @Test public void single(@Name("Drink") Soda soda) { 17 | assertNotNull(soda); 18 | } 19 | 20 | @Test public void multiple(@Name("Drink") Soda soda, Snack snack) { 21 | assertNotNull(soda); 22 | assertNotNull(snack); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ConstructorMultipleNamedArgumentTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Name; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import static org.junit.Assert.assertNotNull; 8 | 9 | @RunWith(BurstJUnit4.class) 10 | public final class ConstructorMultipleNamedArgumentTest { 11 | private final Soda soda; 12 | private final Snack snack; 13 | 14 | public ConstructorMultipleNamedArgumentTest(@Name("Drink") Soda soda, Snack snack) { 15 | this.soda = soda; 16 | this.snack = snack; 17 | } 18 | 19 | @Test public void testMethod() { 20 | assertNotNull(soda); 21 | assertNotNull(snack); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | If you would like to contribute code you can do so through GitHub by 5 | forking the repository and sending a pull request. 6 | 7 | When submitting code, please make every effort to follow existing conventions 8 | and style in order to keep the code as readable as possible. Please also make 9 | sure your code compiles by running `mvn clean verify`. Checkstyle failures 10 | during compilation indicate errors in your style and can be viewed in the 11 | `checkstyle-result.xml` file. 12 | 13 | Before your code can be accepted into the project you must also sign the 14 | [Individual Contributor License Agreement (CLA)][1]. 15 | 16 | 17 | [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 18 | -------------------------------------------------------------------------------- /burst/src/test/java/com/squareup/burst/TestUtil.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Name; 4 | 5 | import java.lang.annotation.Annotation; 6 | 7 | final class TestUtil { 8 | 9 | static Name createName(final String name) { 10 | return new Name() { 11 | 12 | @Override public Class annotationType() { 13 | return Name.class; 14 | } 15 | 16 | @Override public String value() { 17 | return name; 18 | } 19 | }; 20 | } 21 | 22 | @interface Fake { } 23 | 24 | static Fake createFake() { 25 | return new Fake() { 26 | 27 | @Override public Class annotationType() { 28 | return Fake.class; 29 | } 30 | }; 31 | } 32 | 33 | private TestUtil() { 34 | throw new AssertionError("No instances."); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /burst/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | com.squareup.burst 8 | burst-parent 9 | 1.2.1-SNAPSHOT 10 | 11 | 12 | burst 13 | Burst 14 | 15 | 16 | 17 | junit 18 | junit 19 | test 20 | 21 | 22 | org.assertj 23 | assertj-core 24 | test 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /deploy_javadoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | REPO="git@github.com:square/burst.git" 6 | GROUP_ID="com.squareup.burst" 7 | ARTIFACT_ID="burst" 8 | 9 | DIR=temp-clone 10 | 11 | # Delete any existing temporary website clone 12 | rm -rf $DIR 13 | 14 | # Clone the current repo into temp folder 15 | git clone $REPO $DIR 16 | 17 | # Move working directory into temp folder 18 | cd $DIR 19 | 20 | # Checkout and track the gh-pages branch 21 | git checkout -t origin/gh-pages 22 | 23 | # Delete everything 24 | rm -rf * 25 | 26 | # Download the latest javadoc 27 | curl -L "https://search.maven.org/remote_content?g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip 28 | unzip javadoc.zip 29 | rm javadoc.zip 30 | 31 | # Stage all files in git and create a commit 32 | git add . 33 | git add -u 34 | git commit -m "Website at $(date)" 35 | 36 | # Push the new files up to GitHub 37 | git push origin gh-pages 38 | 39 | # Delete our temp folder 40 | cd .. 41 | rm -rf $DIR 42 | -------------------------------------------------------------------------------- /burst-junit4/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | com.squareup.burst 8 | burst-parent 9 | 1.2.1-SNAPSHOT 10 | 11 | 12 | burst-junit4 13 | Burst JUnit 4 Integration 14 | 15 | 16 | 17 | com.squareup.burst 18 | burst 19 | ${project.version} 20 | 21 | 22 | junit 23 | junit 24 | 25 | 26 | 27 | org.assertj 28 | assertj-core 29 | test 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /burst/src/main/java/com/squareup/burst/annotation/Burst.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Marks a field to be injected by Burst when unit tests are run. 10 | * If a test class has fields annotated with this, it must have a default constructor, 11 | * and no other constructors. 12 | *

13 | * The following are equivalent: 14 | *

15 |  *   public class MyTests {
16 |  *     enum Color { RED, BLUE }
17 |  *
18 |  *     private final Color color;
19 |  *
20 |  *     public MyTests(Color color) {
21 |  *       this.color = color;
22 |  *     }
23 |  *
24 |  *     …
25 |  *   }
26 |  * 
27 | *
28 |  *   public class MyTests {
29 |  *     enum Color { RED, BLUE }
30 |  *
31 |  *     {@literal @}Burst private Color color;
32 |  *
33 |  *     …
34 |  *   }
35 |  * 
36 | */ 37 | @Retention(RetentionPolicy.RUNTIME) 38 | @Target(ElementType.FIELD) 39 | public @interface Burst { 40 | } 41 | -------------------------------------------------------------------------------- /.buildscript/deploy_snapshot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. 4 | # 5 | # Adapted from https://coderwall.com/p/9b_lfq and 6 | # http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ 7 | 8 | SLUG="square/burst" 9 | JDK="openjdk8" 10 | BRANCH="master" 11 | 12 | set -e 13 | 14 | if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then 15 | echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'." 16 | elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then 17 | echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'." 18 | elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then 19 | echo "Skipping snapshot deployment: was pull request." 20 | elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then 21 | echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." 22 | else 23 | echo "Deploying snapshot..." 24 | mvn clean source:jar javadoc:jar deploy --settings=".buildscript/settings.xml" -Dmaven.test.skip=true 25 | echo "Snapshot deployed!" 26 | fi 27 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ParentRunnerSpyTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.junit.Test; 6 | import org.junit.runner.Description; 7 | import org.junit.runner.notification.RunNotifier; 8 | import org.junit.runners.ParentRunner; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class ParentRunnerSpyTest { 13 | 14 | @Test 15 | public void testGetFilteredChildren() throws Exception { 16 | List children = 17 | ParentRunnerSpy.getFilteredChildren(new ParentRunner(ParentRunnerSpyTest.class) { 18 | @Override protected List getChildren() { 19 | ArrayList children = new ArrayList<>(); 20 | children.add("children"); 21 | return children; 22 | } 23 | 24 | @Override protected Description describeChild(String o) { 25 | return null; 26 | } 27 | 28 | @Override protected void runChild(String o, RunNotifier runNotifier) {} 29 | }); 30 | 31 | assertEquals(1, children.size()); 32 | assertEquals("children", children.get(0)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/BeforeAndAfterClassTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.AfterClass; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import static org.junit.Assert.assertFalse; 9 | import static org.junit.Assert.assertNotNull; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | @RunWith(BurstJUnit4.class) 13 | public class BeforeAndAfterClassTest { 14 | private static boolean beforeClassCalled = false; 15 | private static boolean afterClassCalled = false; 16 | 17 | private final Snack snack; 18 | 19 | public BeforeAndAfterClassTest(Snack snack) { 20 | this.snack = snack; 21 | } 22 | 23 | @BeforeClass public static void beforeClass() { 24 | assertFalse(beforeClassCalled); 25 | beforeClassCalled = true; 26 | } 27 | 28 | @AfterClass public static void afterClass() { 29 | assertTrue(beforeClassCalled); 30 | assertFalse(afterClassCalled); 31 | afterClassCalled = true; 32 | } 33 | 34 | @Test public void testSnackIsSet() { 35 | assertTrue(beforeClassCalled); 36 | assertFalse(afterClassCalled); 37 | assertNotNull(snack); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 1.2.0 *(2017-08-26)* 5 | ---------------------------- 6 | 7 | * New: `@Name` annotation allows you to control the enum prefix in the test name. When no name is provided the enum name is no longer used. 8 | 9 | 10 | Version 1.1.1 *(2016-12-07)* 11 | ---------------------------- 12 | 13 | * Fix: Pass method annotations when creating JUnit Descriptions. 14 | * Fix: Do not invoke BeforeClass/AfterClass/ClassRule for each variation. 15 | 16 | 17 | Version 1.1.0 *(2015-04-15)* 18 | ---------------------------- 19 | 20 | * New: Support for JUnit 4.12. 21 | * JUnit 3 support has been dropped. If you are on Android, use the new 'testing-support-lib' for JUnit 4 on-device tests. This also moves to forbidding default constructors in addition to a burst-enabled constructor. 22 | 23 | 24 | Version 1.0.2 *(2014-12-09)* 25 | ---------------------------- 26 | 27 | * Added support for field injection with the `@Burst` field annotation. 28 | 29 | 30 | Version 1.0.1 *(2014-10-21)* 31 | ---------------------------- 32 | 33 | * Fix: Correct filtering so individual tests can be run from the IDE. 34 | 35 | 36 | Version 1.0.0 *(2014-10-08)* 37 | ---------------------------- 38 | 39 | Initial release. 40 | -------------------------------------------------------------------------------- /burst-junit4/src/main/java/com/squareup/burst/BurstMethod.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.lang.reflect.Method; 4 | import org.junit.internal.runners.model.ReflectiveCallable; 5 | import org.junit.runners.model.FrameworkMethod; 6 | 7 | import static com.squareup.burst.BurstJUnit4.nameWithArguments; 8 | import static com.squareup.burst.Util.checkNotNull; 9 | 10 | final class BurstMethod extends FrameworkMethod { 11 | private final Enum[] methodArgs; 12 | 13 | BurstMethod(Method method, Enum[] methodArgs) { 14 | super(checkNotNull(method, "method")); 15 | this.methodArgs = checkNotNull(methodArgs, "methodArgs"); 16 | } 17 | 18 | @Override public Object invokeExplosively(final Object target, Object... params) 19 | throws Throwable { 20 | checkNotNull(target, "target"); 21 | 22 | ReflectiveCallable callable = new ReflectiveCallable() { 23 | @Override protected Object runReflectiveCall() throws Throwable { 24 | return getMethod().invoke(target, methodArgs); 25 | } 26 | }; 27 | return callable.run(); 28 | } 29 | 30 | @Override public String getName() { 31 | return nameWithArguments(super.getName(), methodArgs, getMethod().getParameterAnnotations()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/ClassRuleTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.ClassRule; 4 | import org.junit.Test; 5 | import org.junit.rules.ExternalResource; 6 | import org.junit.rules.TestRule; 7 | import org.junit.runner.RunWith; 8 | 9 | import static org.junit.Assert.assertFalse; 10 | import static org.junit.Assert.assertNotNull; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | @RunWith(BurstJUnit4.class) 14 | public class ClassRuleTest { 15 | @ClassRule public static TestRule rule = new ExternalResource() { 16 | @Override protected void before() throws Throwable { 17 | assertFalse(beforeCalled); 18 | beforeCalled = true; 19 | } 20 | 21 | @Override protected void after() { 22 | assertTrue(beforeCalled); 23 | assertFalse(afterCalled); 24 | afterCalled = true; 25 | } 26 | }; 27 | 28 | private static boolean beforeCalled = false; 29 | private static boolean afterCalled = false; 30 | 31 | private final Snack snack; 32 | 33 | public ClassRuleTest(Snack snack) { 34 | this.snack = snack; 35 | } 36 | 37 | @Test public void testSnackIsSet() { 38 | assertTrue(beforeCalled); 39 | assertFalse(afterCalled); 40 | assertNotNull(snack); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /burst-junit4/src/main/java/com/squareup/burst/ParentRunnerSpy.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | import java.util.ArrayList; 6 | import java.util.Collection; 7 | import java.util.List; 8 | import org.junit.runners.ParentRunner; 9 | 10 | /** 11 | * Exposes {@link ParentRunner}'s private members. 12 | */ 13 | final class ParentRunnerSpy { 14 | private static final Method getFilteredChildrenMethod; 15 | 16 | static { 17 | try { 18 | getFilteredChildrenMethod = ParentRunner.class.getDeclaredMethod("getFilteredChildren"); 19 | getFilteredChildrenMethod.setAccessible(true); 20 | } catch (NoSuchMethodException e) { 21 | throw new ExceptionInInitializerError(e); 22 | } 23 | } 24 | 25 | /** 26 | * Reflectively invokes a {@link ParentRunner}'s getFilteredChildren method. Manipulating this 27 | * list lets us control which tests will be run. 28 | */ 29 | static List getFilteredChildren(ParentRunner parentRunner) { 30 | try { 31 | //noinspection unchecked 32 | return new ArrayList<>((Collection) getFilteredChildrenMethod.invoke(parentRunner)); 33 | } catch (IllegalAccessException | InvocationTargetException e) { 34 | throw new RuntimeException("Failed to invoke getFilteredChildren()", e); 35 | } 36 | } 37 | 38 | private ParentRunnerSpy() { 39 | throw new AssertionError("No instances."); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /burst/src/main/java/com/squareup/burst/annotation/Name.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Marks a field or parameter name for test output. This can help make test output more 10 | * readable. 11 | *

12 | * The following snippet: 13 | *

14 |  *   package com.example.yourpackage
15 |  *
16 |  *   public class MyTests {
17 |  *     enum BooleanCondition { TRUE, FALSE }
18 |  *
19 |  *     {@literal @}Name("IsEnabled") private BooleanCondition isEnabled;
20 |  *     {@literal @}Name("IsFlagged") private BooleanCondition isFlagged;
21 |  *
22 |  *     …
23 |  *
24 |  *     {@literal @}Test
25 |  *     public void testConditions() {
26 |  *       …
27 |  *     }
28 |  *   }
29 |  * 
30 | * Would produce output like this in the runner: 31 | *
32 |  *   testCondition[](com.example.yourpackage.MyTest[IsEnabled=TRUE, IsFlagged=TRUE])
33 |  *   testCondition[](com.example.yourpackage.MyTest[IsEnabled=TRUE, IsFlagged=FALSE])
34 |  *   testCondition[](com.example.yourpackage.MyTest[IsEnabled=FALSE, IsFlagged=TRUE])
35 |  *   testCondition[](com.example.yourpackage.MyTest[IsEnabled=FALSE, IsFlagged=FALSE])
36 |  * 
37 | */ 38 | @Retention(RetentionPolicy.RUNTIME) 39 | @Target({ ElementType.FIELD, ElementType.PARAMETER }) 40 | public @interface Name { 41 | 42 | /** 43 | * @return The String value that Burst should use for the name in test output. 44 | */ 45 | String value(); 46 | } 47 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/RuleUsingAnnotationsTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | import java.util.Collection; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.TestWatcher; 10 | import org.junit.runner.Description; 11 | import org.junit.runner.RunWith; 12 | 13 | import static java.lang.annotation.ElementType.METHOD; 14 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 15 | import static org.junit.Assert.assertEquals; 16 | import static org.junit.Assert.assertTrue; 17 | 18 | @RunWith(BurstJUnit4.class) 19 | public class RuleUsingAnnotationsTest { 20 | 21 | @Target(METHOD) 22 | @Retention(RUNTIME) @interface CustomAnnotation { 23 | 24 | } 25 | 26 | static class RuleWithAnnotation extends TestWatcher { 27 | 28 | Collection annotations; 29 | 30 | @Override 31 | protected void starting(Description description) { 32 | annotations = description.getAnnotations(); 33 | } 34 | } 35 | 36 | @Rule 37 | public RuleWithAnnotation rule = new RuleWithAnnotation(); 38 | 39 | @Test 40 | @CustomAnnotation 41 | public void shouldDetectAnnotationsOnATest() { 42 | assertEquals(2, rule.annotations.size()); 43 | 44 | boolean junitTestAnnotationDetected = false; 45 | boolean customAnnotationDetected = false; 46 | 47 | for (Annotation annotation : rule.annotations) { 48 | if (annotation.annotationType() == Test.class) { 49 | junitTestAnnotationDetected = true; 50 | } else if (annotation.annotationType() == CustomAnnotation.class) { 51 | customAnnotationDetected = true; 52 | } 53 | } 54 | 55 | assertTrue(junitTestAnnotationDetected); 56 | assertTrue(customAnnotationDetected); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/JournalingListener.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.junit.runner.Description; 6 | import org.junit.runner.Result; 7 | import org.junit.runner.notification.Failure; 8 | import org.junit.runner.notification.RunListener; 9 | import org.junit.runner.notification.RunNotifier; 10 | 11 | final class JournalingListener { 12 | private final RunNotifier notifier = new RunNotifier(); 13 | private final List journal = new ArrayList<>(); 14 | 15 | JournalingListener() { 16 | notifier.addListener(new RunListener() { 17 | @Override public void testRunStarted(Description description) throws Exception { 18 | throw new AssertionError(); 19 | } 20 | 21 | @Override public void testRunFinished(Result result) throws Exception { 22 | throw new AssertionError(); 23 | } 24 | 25 | @Override public void testStarted(Description description) throws Exception { 26 | journal.add("START " + description.getDisplayName()); 27 | } 28 | 29 | @Override public void testFinished(Description description) throws Exception { 30 | journal.add("FINISH " + description.getDisplayName()); 31 | } 32 | 33 | @Override public void testFailure(Failure failure) throws Exception { 34 | journal.add( 35 | "FAIL " + failure.getDescription().getDisplayName() + " " + failure.getMessage()); 36 | } 37 | 38 | @Override public void testAssumptionFailure(Failure failure) { 39 | journal.add("ASSUMPTION FAIL " 40 | + failure.getDescription().getDisplayName() 41 | + " " 42 | + failure.getMessage()); 43 | } 44 | 45 | @Override public void testIgnored(Description description) throws Exception { 46 | journal.add("IGNORE " + description.getDisplayName()); 47 | } 48 | }); 49 | } 50 | 51 | RunNotifier notifier() { 52 | return notifier; 53 | } 54 | 55 | List journal() { 56 | return new ArrayList<>(journal); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /burst-junit4/src/main/java/com/squareup/burst/BurstJUnit4.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Method; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import org.junit.Test; 8 | import org.junit.runner.Runner; 9 | import org.junit.runner.manipulation.Filter; 10 | import org.junit.runner.manipulation.NoTestsRemainException; 11 | import org.junit.runners.Suite; 12 | import org.junit.runners.model.FrameworkMethod; 13 | import org.junit.runners.model.InitializationError; 14 | import org.junit.runners.model.TestClass; 15 | 16 | import static com.squareup.burst.Util.checkNotNull; 17 | import static java.util.Collections.unmodifiableList; 18 | 19 | /** 20 | * A suite associated with a particular test class. Its children are {@link BurstRunner}s, each 21 | * representing a particular variation of that class. 22 | */ 23 | public final class BurstJUnit4 extends Suite { 24 | public BurstJUnit4(Class cls) throws InitializationError { 25 | super(cls, explode(cls)); 26 | } 27 | 28 | /* 29 | * ParentRunner's default filter implementation generates a hierarchy of test descriptions, 30 | * applies the filter to those descriptions, and removes any test nodes whose descriptions were 31 | * all filtered out. 32 | *

33 | * This would be problematic for us since we generate non-standard test descriptions which include 34 | * parameter information. This implementation lets each {@link BurstRunner} child filter itself 35 | * via {@link BurstRunner#filter(Filter)}. 36 | */ 37 | @Override public void filter(Filter filter) throws NoTestsRemainException { 38 | List filteredChildren = ParentRunnerSpy.getFilteredChildren(this); 39 | // Iterate over a clone so that we can safely mutate the original. 40 | for (Runner child : new ArrayList<>(filteredChildren)) { 41 | try { 42 | filter.apply(child); 43 | } catch (NoTestsRemainException e) { 44 | filteredChildren.remove(child); 45 | } 46 | } 47 | if (filteredChildren.isEmpty()) { 48 | throw new NoTestsRemainException(); 49 | } 50 | } 51 | 52 | static List explode(Class cls) throws InitializationError { 53 | checkNotNull(cls, "cls"); 54 | 55 | TestClass testClass = new TestClass(cls); 56 | List testMethods = testClass.getAnnotatedMethods(Test.class); 57 | 58 | List burstMethods = new ArrayList<>(testMethods.size()); 59 | for (FrameworkMethod testMethod : testMethods) { 60 | Method method = testMethod.getMethod(); 61 | for (Enum[] methodArgs : Burst.explodeArguments(method)) { 62 | burstMethods.add(new BurstMethod(method, methodArgs)); 63 | } 64 | } 65 | 66 | TestConstructor constructor = BurstableConstructor.findSingle(cls); 67 | Enum[][] constructorArgsList = Burst.explodeArguments(constructor); 68 | List burstRunners = new ArrayList<>(constructorArgsList.length); 69 | for (Enum[] constructorArgs : constructorArgsList) { 70 | burstRunners.add(new BurstRunner(cls, constructor, constructorArgs, burstMethods)); 71 | } 72 | 73 | return unmodifiableList(burstRunners); 74 | } 75 | 76 | static String nameWithArguments(String name, Enum[] arguments, 77 | Annotation[][] argumentAnnotations) { 78 | if (arguments.length == 0) { 79 | return name; 80 | } 81 | return name + '[' + Burst.friendlyName(arguments, argumentAnnotations) + ']'; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.sonatype.oss 8 | oss-parent 9 | 7 10 | 11 | 12 | com.squareup.burst 13 | burst-parent 14 | 1.2.1-SNAPSHOT 15 | pom 16 | Burst (Parent) 17 | Burst is a unit testing library for varying test data. 18 | https://github.com/square/burst 19 | 20 | 21 | burst 22 | burst-junit4 23 | 24 | 25 | 26 | UTF-8 27 | 1.7 28 | 29 | 4.13.1 30 | 31 | 1.6.1 32 | 1.2 33 | 34 | 35 | 36 | https://github.com/square/burst/ 37 | scm:git:https://github.com/square/burst.git 38 | scm:git:git@github.com:square/burst.git 39 | HEAD 40 | 41 | 42 | 43 | GitHub Issues 44 | https://github.com/square/burst/issues 45 | 46 | 47 | 48 | 49 | Apache 2.0 50 | http://www.apache.org/licenses/LICENSE-2.0.txt 51 | 52 | 53 | 54 | 55 | 56 | 57 | junit 58 | junit 59 | ${junit.version} 60 | 61 | 62 | org.assertj 63 | assertj-core 64 | ${assertj.version} 65 | 66 | 67 | com.google.dexmaker 68 | dexmaker 69 | ${dexmaker.version} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.0 81 | 82 | ${java.version} 83 | ${java.version} 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-release-plugin 93 | 2.5 94 | 95 | true 96 | 97 | 98 | 99 | org.apache.maven.plugins 100 | maven-checkstyle-plugin 101 | 2.10 102 | 103 | true 104 | checkstyle.xml 105 | true 106 | 107 | 108 | 109 | verify 110 | 111 | checkstyle 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /burst/src/main/java/com/squareup/burst/BurstableConstructor.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Burst; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Modifier; 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | final class BurstableConstructor { 13 | 14 | /** 15 | * Find the parameterized constructor of cls if there is one, or the default constructor if 16 | * there isn't. 17 | * 18 | * @throws IllegalStateException if there are multiple parameterized constructors 19 | */ 20 | public static TestConstructor findSingle(Class cls) { 21 | final TestConstructor[] constructors = findAll(cls); 22 | 23 | if (constructors.length == 0) { 24 | throw new IllegalStateException(cls.getName() + " requires at least 1 public constructor"); 25 | } else if (constructors.length == 1) { 26 | return constructors[0]; 27 | } else { 28 | throw new IllegalStateException("Class " 29 | + cls.getName() 30 | + " has too many parameterized constructors. " 31 | + "Should only be 1 (with enum variations)."); 32 | } 33 | } 34 | 35 | /** 36 | * Finds all constructors of {@code cls} that are burstable. 37 | * A burstable constructor is public, and may or may not be the default constructor. 38 | * 39 | * @throws IllegalStateException if cls has public fields and a non-default constructor 40 | */ 41 | private static TestConstructor[] findAll(Class cls) { 42 | final Constructor[] constructors = cls.getConstructors(); 43 | final Field[] fields = getBurstableFields(cls); 44 | final List filteredConstructors = new ArrayList<>(); 45 | 46 | for (Constructor constructor : constructors) { 47 | if (constructor.getParameterTypes().length > 0 && fields.length > 0) { 48 | throw new IllegalStateException( 49 | "Class " 50 | + cls.getName() 51 | + " has a parameterized constructor, so cannot also be parameterized on fields"); 52 | } 53 | 54 | filteredConstructors.add(new TestConstructor(constructor, fields)); 55 | } 56 | 57 | return filteredConstructors.toArray(new TestConstructor[filteredConstructors.size()]); 58 | } 59 | 60 | /** 61 | * Get all the {@link Burst}-annotated fields. 62 | * Validates that the field is non-static and non-final, but doesn't check type (types are 63 | * validated by static methods in {@link com.squareup.burst.Burst}). 64 | */ 65 | private static Field[] getBurstableFields(Class cls) { 66 | final List fields = new ArrayList<>(); 67 | 68 | for (Field field : getAllFields(cls)) { 69 | if (field.isAnnotationPresent(Burst.class)) { 70 | 71 | if (Modifier.isStatic(field.getModifiers())) { 72 | throw new IllegalStateException("Burstable field must not be static: " + field.getName()); 73 | } 74 | 75 | if (Modifier.isFinal(field.getModifiers())) { 76 | throw new IllegalStateException("Burstable field must not be final: " + field.getName()); 77 | } 78 | 79 | fields.add(field); 80 | } 81 | } 82 | 83 | return fields.toArray(new Field[fields.size()]); 84 | } 85 | 86 | /** 87 | * Returns all fields of any visibility on type and all of type's superclasses. 88 | * Aka {@link Class#getFields()}, but returns all fields, not just public ones. 89 | */ 90 | private static Collection getAllFields(Class type) { 91 | final Collection allFields = new ArrayList<>(); 92 | 93 | while (type != Object.class) { 94 | Collections.addAll(allFields, type.getDeclaredFields()); 95 | type = type.getSuperclass(); 96 | } 97 | 98 | return Collections.unmodifiableCollection(allFields); 99 | } 100 | 101 | private BurstableConstructor() { 102 | throw new AssertionError("No instances."); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /burst-junit4/src/main/java/com/squareup/burst/BurstRunner.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import org.junit.rules.TestRule; 8 | import org.junit.runner.Description; 9 | import org.junit.runner.manipulation.Filter; 10 | import org.junit.runner.manipulation.NoTestsRemainException; 11 | import org.junit.runners.BlockJUnit4ClassRunner; 12 | import org.junit.runners.model.FrameworkMethod; 13 | import org.junit.runners.model.InitializationError; 14 | import org.junit.runners.model.Statement; 15 | 16 | import static com.squareup.burst.BurstJUnit4.nameWithArguments; 17 | import static com.squareup.burst.Util.checkNotNull; 18 | 19 | /** 20 | * A set of tests associated with a particular variation of some test class. 21 | */ 22 | final class BurstRunner extends BlockJUnit4ClassRunner { 23 | private final TestConstructor constructor; 24 | private final Enum[] constructorArgs; 25 | private final List methods; 26 | 27 | BurstRunner(Class cls, TestConstructor constructor, Enum[] constructorArgs, 28 | List methods) throws InitializationError { 29 | super(checkNotNull(cls, "cls")); 30 | this.constructor = checkNotNull(constructor, "constructor"); 31 | this.constructorArgs = checkNotNull(constructorArgs, "constructorArgs"); 32 | this.methods = checkNotNull(methods, "methods"); 33 | } 34 | 35 | @Override protected List getChildren() { 36 | return methods; 37 | } 38 | 39 | @Override protected String getName() { 40 | return nameWithArguments(super.getName(), constructorArgs, 41 | constructor.getArgumentAnnotations()); 42 | } 43 | 44 | @Override protected Description describeChild(FrameworkMethod method) { 45 | return Description.createTestDescription(getName(), method.getName(), method.getAnnotations()); 46 | } 47 | 48 | @Override protected Object createTest() throws Exception { 49 | return constructor.newInstance(constructorArgs); 50 | } 51 | 52 | @Override protected Statement withBeforeClasses(Statement statement) { 53 | // The parent runner, BurstJUnit4, will handle @BeforeClass/@AfterClass/@ClassRule once for the 54 | // whole class. We don't want to repeat them for each variation. 55 | return statement; 56 | } 57 | 58 | @Override protected Statement withAfterClasses(Statement statement) { 59 | // The parent runner, BurstJUnit4, will handle @BeforeClass/@AfterClass/@ClassRule once for the 60 | // whole class. We don't want to repeat them for each variation. 61 | return statement; 62 | } 63 | 64 | @Override protected List classRules() { 65 | // The parent runner, BurstJUnit4, will handle @BeforeClass/@AfterClass/@ClassRule once for the 66 | // whole class. We don't want to repeat them for each variation. 67 | return Collections.emptyList(); 68 | } 69 | 70 | @Override protected void validateConstructor(List errors) { 71 | // Constructor was already validated by Burst. 72 | } 73 | 74 | @Override protected void validatePublicVoidNoArgMethods(Class annotation, 75 | boolean isStatic, List errors) { 76 | // Methods were already validated by Burst. 77 | } 78 | 79 | /* 80 | * ParentRunner's default filter implementation generates a hierarchy of test descriptions, 81 | * applies the filter to those descriptions, and removes any test nodes whose descriptions were 82 | * all filtered out. 83 | *

84 | * This would be problematic for us since we generate non-standard test descriptions which include 85 | * parameter information. This implementation generates "plain" descriptions without parameter 86 | * information and passes those to the filter. 87 | */ 88 | @Override public void filter(Filter filter) throws NoTestsRemainException { 89 | List filteredChildren = ParentRunnerSpy.getFilteredChildren(this); 90 | // Iterate over a clone so that we can safely mutate the original. 91 | for (FrameworkMethod child : new ArrayList<>(filteredChildren)) { 92 | if (!filter.shouldRun(describeChildPlain(child))) { 93 | filteredChildren.remove(child); 94 | } 95 | } 96 | if (filteredChildren.isEmpty()) { 97 | throw new NoTestsRemainException(); 98 | } 99 | } 100 | 101 | /** 102 | * Generates a "plain" description of a burst test, with no parameter information. This should be 103 | * used only for filtering. It would not be safe for {@link #describeChild(FrameworkMethod)} to 104 | * return these plain descriptions, as then multiple tests would share the same description. 105 | */ 106 | private Description describeChildPlain(FrameworkMethod method) { 107 | return Description.createTestDescription(getTestClass().getJavaClass(), 108 | method.getMethod().getName(), method.getAnnotations()); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /burst/src/main/java/com/squareup/burst/TestConstructor.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.InvocationTargetException; 7 | import java.util.Arrays; 8 | 9 | import static com.squareup.burst.Util.checkNotNull; 10 | 11 | /** 12 | * A wrapper around {@link Constructor} that can also set fields reflectively. 13 | */ 14 | final class TestConstructor { 15 | private final Constructor constructor; 16 | private final Field[] fields; 17 | 18 | public TestConstructor(Constructor constructor, Field... fields) { 19 | this.constructor = checkNotNull(constructor, "constructor"); 20 | this.fields = fields; 21 | 22 | for (Field field : fields) { 23 | field.setAccessible(true); 24 | } 25 | } 26 | 27 | public String getName() { 28 | return constructor.getName(); 29 | } 30 | 31 | public Class[] getConstructorParameterTypes() { 32 | return constructor.getParameterTypes(); 33 | } 34 | 35 | /** 36 | * Returns an array of the Class objects associated with the fields this constructor was 37 | * initialized with, followed by the parameter types of this constructor. 38 | * If the constructor was declared with no fields and no parameters, an empty array will be 39 | * returned. 40 | */ 41 | public Class[] getVariationTypes() { 42 | final Class[] ctorTypes = constructor.getParameterTypes(); 43 | final Class[] allTypes = new Class[ctorTypes.length + fields.length]; 44 | 45 | System.arraycopy(ctorTypes, 0, allTypes, 0, ctorTypes.length); 46 | 47 | for (int i = 0; i < fields.length; i++) { 48 | allTypes[i + ctorTypes.length] = fields[i].getType(); 49 | } 50 | 51 | return allTypes; 52 | } 53 | 54 | /** 55 | * Calls {@link Constructor#newInstance(java.lang.Object...)}, as 56 | * well as initializing all the fields passed to 57 | * {@link TestConstructor#TestConstructor(Constructor, Field...)}. 58 | *

59 | * Constructor arguments should be first in the array, followed by field arguments. 60 | */ 61 | public Object newInstance(Object[] args) 62 | throws IllegalAccessException, InvocationTargetException, InstantiationException { 63 | if (args.length != getVariationTypes().length) { 64 | throw new IllegalArgumentException(String.format( 65 | "Constructor takes %d values, only %d passed", getVariationTypes().length, args.length)); 66 | } 67 | 68 | // Partition arg list 69 | final Object[] ctorArgs = extractConstructorArgs(args); 70 | final Object[] fieldArgs = extractFieldArgs(args); 71 | 72 | final Object instance = newInstanceWithoutFields(ctorArgs); 73 | initializeFieldsOnInstance(instance, fieldArgs); 74 | return instance; 75 | } 76 | 77 | /** 78 | * @return array containing the elements of args that apply to the underlying 79 | * constructor. 80 | */ 81 | public Object[] extractConstructorArgs(final Object[] args) { 82 | return Arrays.copyOfRange(args, 0, constructor.getParameterTypes().length); 83 | } 84 | 85 | /** 86 | * @return array containing the elements of args that apply to fields. 87 | */ 88 | public Object[] extractFieldArgs(final Object[] args) { 89 | return Arrays.copyOfRange(args, constructor.getParameterTypes().length, args.length); 90 | } 91 | 92 | /** 93 | * Creates a new instance by calling the underlying constructor, without initializing 94 | * any fields. 95 | */ 96 | public Object newInstanceWithoutFields(Object[] args) 97 | throws IllegalAccessException, InvocationTargetException, InstantiationException { 98 | return constructor.newInstance(args); 99 | } 100 | 101 | /** 102 | * Sets the fields on an instance (as passed to 103 | * {@link TestConstructor#TestConstructor(Constructor, Field...)}) to args. 104 | * The number of arguments in the array must be equal to the number of fields. 105 | */ 106 | public void initializeFieldsOnInstance(final Object instance, final Object[] args) 107 | throws IllegalAccessException { 108 | if (fields.length != args.length) { 109 | throw new IllegalArgumentException(String.format( 110 | "Requires values for %d fields, only %d values passed", fields.length, args.length)); 111 | } 112 | 113 | for (int i = 0; i < fields.length; i++) { 114 | fields[i].set(instance, args[i]); 115 | } 116 | } 117 | 118 | public Annotation[][] getArgumentAnnotations() { 119 | final Annotation[][] ctorAnnotations = constructor.getParameterAnnotations(); 120 | final Annotation[][] allAnnotations = new Annotation[ctorAnnotations.length + fields.length][]; 121 | 122 | System.arraycopy(ctorAnnotations, 0, allAnnotations, 0, ctorAnnotations.length); 123 | 124 | for (int i = 0; i < fields.length; i++) { 125 | allAnnotations[i + ctorAnnotations.length] = fields[i].getAnnotations(); 126 | } 127 | 128 | return allAnnotations; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Burst 2 | ===== 3 | 4 | A unit testing library for varying test data. 5 | 6 | **DEPRECATED**: Burst remains stable and functional, but you should check out [TestParameterInjector](https://github.com/google/TestParameterInjector) from Google which is like Burst++. Unless there are major bugs in Burst which necessitate a patch release, no new development will be occurring. 7 | 8 | 9 | 10 | Usage 11 | ----- 12 | 13 | Burst is a set of test runners which rely on enums for varying both the instantiation of test 14 | classes and the methods inside of them. 15 | 16 | Define an enum for the property you wish to vary. 17 | ```java 18 | public enum Soda { 19 | PEPSI, COKE 20 | } 21 | ``` 22 | The enum can be simple as above, or contain data and methods specific to what you are testing. 23 | ```java 24 | public enum Sets { 25 | HASH_SET() { 26 | @Override public Set create() { 27 | return new HashSet(); 28 | } 29 | }, 30 | LINKED_HASH_SET() { 31 | @Override public Set create() { 32 | return new LinkedHashSet(); 33 | } 34 | }, 35 | TREE_SET() { 36 | @Override public Set create() { 37 | return new TreeSet(); 38 | } 39 | }; 40 | 41 | public abstract Set create(); 42 | } 43 | ``` 44 | 45 | Annotate your test class to use the `BurstJUnit4` runner. 46 | ```java 47 | @RunWith(BurstJUnit4.class) 48 | public class DrinkSodaTest { 49 | // TODO Tests... 50 | } 51 | ``` 52 | 53 | An enum that appears on a test constructor will cause all the enclosed test to be run once for each 54 | value in the enum. 55 | ```java 56 | public DrinkSodaTest(Soda soda) { 57 | // TODO Do something with 'soda'... 58 | } 59 | ``` 60 | 61 | Combine multiple enums for the combination of their variations. 62 | ```java 63 | public DrinkSodaTest(Soda soda, Sets sets) { 64 | // TODO Do something with 'soda' and 'sets'... 65 | } 66 | ``` 67 | This will be called with the constructor arguments 68 | [`PEPSI` & `HASH_SET`, `PEPSI` & `LINKED_HASH_SET`, `PEPSI` & `TREE_SET`, `COKE` & `HASH_SET`, ..]. 69 | 70 | If your constructor is just setting fields, you can just annotate the fields with `@Burst`. 71 | ```java 72 | @RunWith(BurstJUnit4.class) 73 | public class DrinkSodaTest { 74 | @Burst Soda soda; 75 | @Burst Sets sets; 76 | // TODO Tests... 77 | } 78 | ``` 79 | This behaves just like the above example. 80 | 81 | **Note:** Classes can either have constructors with arguments *or* annotated fields. A class with both will cause the test runner to throw an exception. 82 | 83 | Methods may also be varied using one or more parameters that are enums. 84 | ```java 85 | @Test public void drinkFavoriteSodas(Soda soda) { 86 | // TODO Test drink method with 'soda'... 87 | } 88 | ``` 89 | 90 | Having both constructor (or field) variation and method variation is supported. 91 | ```java 92 | @RunWith(BurstJUnit4.class) 93 | public class DrinkSodaTest { 94 | private final Set favorites; 95 | 96 | public DrinkSodaTest(Sets sets) { 97 | favorites = sets.create(); 98 | } 99 | 100 | @Test public void trackFavorites() { 101 | // TODO ... 102 | } 103 | 104 | @Test public void drinkFavoriteSodas(Soda soda) { 105 | // TODO ... 106 | } 107 | } 108 | ``` 109 | The `trackFavorites` test will be executed 3 times, once for each `Sets` value. The 110 | `drinkFavoriteSodas` test, however, is executed 6 times, for each of the three `Sets` values it 111 | runs twice for each `Soda`. 112 | 113 | If a particular variation or variation combination does not make sense you can use [assumptions][1] 114 | to filter either directly in the test or as a custom [rule][2]. 115 | 116 | 117 | 118 | Download 119 | -------- 120 | 121 | * **JUnit 4** 122 | 123 | A test runner which can be used for JUnit 4. 124 | 125 | ``` 126 | com.squareup.burst:burst-junit4:1.2.0 127 | ``` 128 | 129 | * **Core library** 130 | 131 | Contains the core logic which creates the combinations of arguments for both constructors and 132 | method. Usually not useful on its own. 133 | 134 | ``` 135 | com.squareup.burst:burst:1.2.0 136 | ``` 137 | 138 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. 139 | 140 | 141 | 142 | License 143 | ------- 144 | 145 | Copyright 2014 Square, Inc. 146 | 147 | Licensed under the Apache License, Version 2.0 (the "License"); 148 | you may not use this file except in compliance with the License. 149 | You may obtain a copy of the License at 150 | 151 | http://www.apache.org/licenses/LICENSE-2.0 152 | 153 | Unless required by applicable law or agreed to in writing, software 154 | distributed under the License is distributed on an "AS IS" BASIS, 155 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 156 | See the License for the specific language governing permissions and 157 | limitations under the License. 158 | 159 | 160 | 161 | 162 | [1]: http://junit.org/javadoc/latest/org/junit/Assume.html 163 | [2]: http://junit.org/javadoc/latest/org/junit/Rule.html 164 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/ 165 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /burst/src/main/java/com/squareup/burst/Burst.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Name; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Method; 7 | 8 | import static com.squareup.burst.Util.checkNotNull; 9 | 10 | /** 11 | * Helper methods to facilitate exercising all variations of the declared parameters on test 12 | * constructors and methods. 13 | */ 14 | public final class Burst { 15 | private static final Enum[][] NONE = new Enum[1][0]; 16 | 17 | /** 18 | * Explode a list of argument values for invoking the specified constructor with all combinations 19 | * of its parameters. 20 | */ 21 | public static Enum[][] explodeArguments(TestConstructor constructor) { 22 | checkNotNull(constructor, "constructor"); 23 | 24 | return explodeParameters(constructor.getVariationTypes(), 25 | constructor.getName() + " constructor"); 26 | } 27 | 28 | /** 29 | * Explode a list of argument values for invoking the specified method with all combinations of 30 | * its parameters. 31 | */ 32 | public static Enum[][] explodeArguments(Method method) { 33 | checkNotNull(method, "method"); 34 | 35 | return explodeParameters(method.getParameterTypes(), 36 | method.getDeclaringClass().getName() + '.' + method.getName() + " method"); 37 | } 38 | 39 | /** 40 | * Creates an "exploded" test name which includes information about the {@code constructorArgs} 41 | * and {@code methodArgs}. This will append both the enum class and enum value name for every 42 | * argument in order. 43 | *

44 | * For example, a method named "snackBreak" being invoked with constructor arguments 45 | * {@code Drink.SODA} and {@code Snack.ALMONDS} and method arguments {@code BreakTime.AFTERNOON} 46 | * would produce "snackBreak_SODA_ALMONDS_AFTERNOON". 47 | * 48 | * @throws ClassCastException If any element of {@code constructorArgs} or {@code methodArgs} is 49 | * not an enum value. 50 | */ 51 | public static String friendlyName(Enum[] arguments) { 52 | return friendlyName(arguments, new Annotation[0][0]); 53 | } 54 | 55 | /** 56 | * Creates an "exploded" test name which includes information about the {@code constructorArgs} 57 | * and {@code methodArgs}. This will append both the enum class and enum value name for every 58 | * argument in order. 59 | *

60 | * For example, a method named "snackBreak" being invoked with constructor arguments 61 | * {@code Drink.SODA} and {@code Snack.ALMONDS} and method arguments {@code BreakTime.AFTERNOON} 62 | * would produce "snackBreak_SODA_ALMONDS_AFTERNOON". 63 | *

64 | * If any of the arguments have an {@literal @}Name annotation, the enum class name will be 65 | * replaced with the value provided in the annotation. 66 | * 67 | * @throws ClassCastException If any element of {@code constructorArgs} or {@code methodArgs} is 68 | * not an enum value. 69 | */ 70 | public static String friendlyName(Enum[] arguments, Annotation[][] argumentAnnotations) { 71 | checkNotNull(arguments, "arguments"); 72 | if (arguments.length == 0) { 73 | return ""; 74 | } 75 | 76 | StringBuilder builder = new StringBuilder(); 77 | for (int i = 0; i < arguments.length; i++) { 78 | Object argument = arguments[i]; 79 | if (builder.length() > 0) { 80 | builder.append(", "); 81 | } 82 | 83 | Enum value = (Enum) argument; 84 | // Appends the enum name and value name. (e.g., Card.VISA) 85 | if (argumentAnnotations.length > i) { 86 | for (Annotation annotation : argumentAnnotations[i]) { 87 | if (annotation instanceof Name) { 88 | String name = ((Name) annotation).value(); 89 | builder.append(name).append('='); 90 | break; 91 | } 92 | } 93 | } 94 | builder.append(value); 95 | } 96 | return builder.toString(); 97 | } 98 | 99 | private static Enum[][] explodeParameters(Class[] parameterTypes, String name) { 100 | int parameterCount = parameterTypes.length; 101 | if (parameterCount == 0) { 102 | return NONE; 103 | } 104 | 105 | int count = 1; // Total variation count. 106 | Enum[][] valuesList = new Enum[parameterCount][]; 107 | 108 | for (int i = 0; i < parameterCount; i++) { 109 | Class parameterType = parameterTypes[i]; 110 | if (!parameterType.isEnum()) { 111 | throw new IllegalStateException(name 112 | + " parameter #" 113 | + (i + 1) 114 | + " type is not an enum. (" 115 | + parameterType.getName() 116 | + ')'); 117 | } 118 | //noinspection unchecked 119 | Class> enumType = (Class>) parameterType; 120 | 121 | Enum[] values = enumType.getEnumConstants(); 122 | valuesList[i] = values; 123 | count *= values.length; 124 | } 125 | return explode(count, valuesList); 126 | } 127 | 128 | private static Enum[][] explode(int count, Enum[][] valuesList) { 129 | // The number of times to replay iterating over individual enum values. 130 | int replays = 1; 131 | // The number of times to repeat an enum value. 132 | int adjacent = count; 133 | 134 | Enum[][] arguments = new Enum[count][valuesList.length]; 135 | for (int valuesIndex = 0; valuesIndex < valuesList.length; valuesIndex++) { 136 | Enum[] values = valuesList[valuesIndex]; 137 | 138 | // Gap between replays is the previous number of adjacent values. 139 | int replayGap = adjacent; 140 | // The number of adjacent is divided among the number of current values. 141 | adjacent /= values.length; 142 | 143 | for (int replay = 0; replay < replays; replay++) { 144 | int replayOffset = replay * replayGap; 145 | for (int valueIndex = 0; valueIndex < values.length; valueIndex++) { 146 | int valueOffset = valueIndex * adjacent; 147 | for (int i = 0; i < adjacent; i++) { 148 | arguments[replayOffset + valueOffset + i][valuesIndex] = values[valueIndex]; 149 | } 150 | } 151 | } 152 | 153 | // Increase the iteration replays by the number of values in the current enum. 154 | replays *= values.length; 155 | } 156 | 157 | return arguments; 158 | } 159 | 160 | private Burst() { 161 | throw new AssertionError("No instances."); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /burst/src/test/java/com/squareup/burst/BurstableConstructorTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import com.squareup.burst.annotation.Burst; 4 | import org.junit.Rule; 5 | import org.junit.Test; 6 | import org.junit.rules.ExpectedException; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | public class BurstableConstructorTest { 11 | enum First { } 12 | enum Second { } 13 | 14 | @Rule public final ExpectedException thrown = ExpectedException.none(); 15 | 16 | public static class None { 17 | } 18 | @Test public void noConstructor() { 19 | TestConstructor ctor = BurstableConstructor.findSingle(None.class); 20 | assertThat(ctor.getVariationTypes()).isEmpty(); 21 | } 22 | 23 | public static class Default { 24 | public Default() {} 25 | } 26 | @Test public void defaultConstructor() { 27 | TestConstructor ctor = BurstableConstructor.findSingle(Default.class); 28 | assertThat(ctor.getVariationTypes()).isEmpty(); 29 | } 30 | 31 | public static class One { 32 | public One(First first) {} 33 | } 34 | @Test public void singleParameterizedConstructor() { 35 | TestConstructor ctor = BurstableConstructor.findSingle(One.class); 36 | assertThat(ctor.getVariationTypes()).containsExactly(First.class); 37 | } 38 | 39 | public static class DefaultAndOne { 40 | public DefaultAndOne() {} 41 | public DefaultAndOne(First first) {} 42 | } 43 | @Test public void defaultAndParameterizedConstructor() { 44 | thrown.expect(IllegalStateException.class); 45 | thrown.expectMessage("too many parameterized constructors"); 46 | 47 | BurstableConstructor.findSingle(DefaultAndOne.class); 48 | } 49 | 50 | public static class NonPublicConstructor { 51 | NonPublicConstructor() {} 52 | } 53 | @Test public void nonPublic() { 54 | thrown.expect(IllegalStateException.class); 55 | thrown.expectMessage("requires at least 1 public constructor"); 56 | 57 | BurstableConstructor.findSingle(NonPublicConstructor.class); 58 | } 59 | 60 | public static class TooMany { 61 | public TooMany(First first) {} 62 | public TooMany(Second second) {} 63 | } 64 | @Test public void tooMany() { 65 | thrown.expect(IllegalStateException.class); 66 | thrown.expectMessage( 67 | "has too many parameterized constructors. Should only be 1 (with enum variations)."); 68 | 69 | BurstableConstructor.findSingle(TooMany.class); 70 | } 71 | 72 | public static class NonEnumInConstructor { 73 | public NonEnumInConstructor(Object first) {} 74 | } 75 | @Test public void nonEnumInConstructor() { 76 | TestConstructor ctor = BurstableConstructor.findSingle(NonEnumInConstructor.class); 77 | assertThat(ctor.getVariationTypes()).containsExactly(Object.class); 78 | } 79 | 80 | public static class NotAnEnumInConstructor { 81 | public NotAnEnumInConstructor(Object first) {} 82 | } 83 | @Test public void notAnEnumInConstructor() { 84 | TestConstructor ctor = BurstableConstructor.findSingle(NotAnEnumInConstructor.class); 85 | assertThat(ctor.getVariationTypes()).containsExactly(Object.class); 86 | } 87 | 88 | public static class DefaultWithField { 89 | @Burst First first; 90 | } 91 | @Test public void defaultConstructorWithField() { 92 | TestConstructor ctor = BurstableConstructor.findSingle(DefaultWithField.class); 93 | assertThat(ctor.getVariationTypes()).containsExactly(First.class); 94 | } 95 | 96 | public static class NoneWithField { 97 | @Burst First first; 98 | public NoneWithField() {} 99 | } 100 | @Test public void singleEmptyConstructorWithField() { 101 | TestConstructor ctor = BurstableConstructor.findSingle(NoneWithField.class); 102 | assertThat(ctor.getVariationTypes()).containsExactly(First.class); 103 | } 104 | 105 | public static class MultipleFields { 106 | @Burst First first; 107 | @Burst Second second; 108 | } 109 | @Test public void emptyConstructorMultipleFields() { 110 | TestConstructor ctor = BurstableConstructor.findSingle(MultipleFields.class); 111 | assertThat(ctor.getVariationTypes()).containsOnly(First.class, Second.class); 112 | } 113 | 114 | public static class OneWithField { 115 | @Burst Second second; 116 | public OneWithField(First first) {} 117 | } 118 | @Test public void singleParameterizedConstructorWithField() { 119 | thrown.expectMessage( 120 | "has a parameterized constructor, so cannot also be parameterized on fields"); 121 | 122 | BurstableConstructor.findSingle(OneWithField.class); 123 | } 124 | 125 | public static class PrivateField { 126 | @Burst private First first; 127 | } 128 | @Test public void privateAnnotatedField() { 129 | TestConstructor ctor = BurstableConstructor.findSingle(PrivateField.class); 130 | assertThat(ctor.getVariationTypes()).containsExactly(First.class); 131 | } 132 | 133 | public static class InheritedField extends PrivateField { 134 | @Burst Second second; 135 | } 136 | @Test public void inheritedField() { 137 | TestConstructor ctor = BurstableConstructor.findSingle(InheritedField.class); 138 | assertThat(ctor.getVariationTypes()).containsOnly(First.class, Second.class); 139 | } 140 | 141 | public static class StaticField { 142 | @Burst static First first; 143 | } 144 | @Test public void StaticField() { 145 | thrown.expect(IllegalStateException.class); 146 | thrown.expectMessage("Burstable field must not be static"); 147 | 148 | BurstableConstructor.findSingle(StaticField.class); 149 | } 150 | 151 | public static class FinalField { 152 | @Burst final First first = null; 153 | } 154 | @Test public void finalField() { 155 | thrown.expect(IllegalStateException.class); 156 | thrown.expectMessage("Burstable field must not be final"); 157 | 158 | BurstableConstructor.findSingle(FinalField.class); 159 | } 160 | 161 | public static class NotAnEnumField { 162 | @Burst Object first; 163 | } 164 | @Test public void notAnEnumField() { 165 | TestConstructor ctor = BurstableConstructor.findSingle(NotAnEnumField.class); 166 | assertThat(ctor.getVariationTypes()).containsExactly(Object.class); 167 | } 168 | 169 | public static class UnannotatedField { 170 | First first; 171 | @Burst Second second; 172 | } 173 | @Test public void unannotatedField() { 174 | TestConstructor ctor = BurstableConstructor.findSingle(UnannotatedField.class); 175 | assertThat(ctor.getVariationTypes()).containsExactly(Second.class); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /burst/src/test/java/com/squareup/burst/BurstTest.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.rules.ExpectedException; 6 | import java.lang.annotation.Annotation; 7 | import java.lang.reflect.Method; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | /** Reflection on constructors, methods, and their parameters. */ 12 | @SuppressWarnings("UnusedDeclaration") 13 | public class BurstTest { 14 | enum First { APPLE, BEARD, COUCH } 15 | enum Second { DINGO, EAGLE } 16 | enum Third { FRANK, GREAT, HEAVY, ITALY } 17 | 18 | public static class One { 19 | public One(First first) {} 20 | } 21 | public static class Three { 22 | public Three(First first, Second second, Third third) {} 23 | } 24 | 25 | public static class Bad { 26 | public Bad(Object o) {} 27 | } 28 | 29 | @Rule public final ExpectedException thrown = ExpectedException.none(); 30 | 31 | @Test public void nonEnumConstructorParameter() { 32 | TestConstructor constructor = new TestConstructor(Bad.class.getConstructors()[0]); 33 | 34 | thrown.expect(IllegalStateException.class); 35 | thrown.expectMessage(Bad.class.getName() 36 | + " constructor parameter #1 type is not an enum. (java.lang.Object)"); 37 | 38 | Burst.explodeArguments(constructor); 39 | } 40 | 41 | @Test public void singleConstructorParameter() { 42 | TestConstructor constructor = new TestConstructor(One.class.getConstructors()[0]); 43 | Object[][] objects = Burst.explodeArguments(constructor); 44 | assertThat(objects).containsExactly( 45 | new Object[] { First.APPLE }, 46 | new Object[] { First.BEARD }, 47 | new Object[] { First.COUCH } 48 | ); 49 | } 50 | 51 | @Test public void multipleConstructorParameters() { 52 | TestConstructor constructor = new TestConstructor(Three.class.getConstructors()[0]); 53 | Object[][] objects = Burst.explodeArguments(constructor); 54 | assertThat(objects).containsExactly( 55 | new Object[] { First.APPLE, Second.DINGO, Third.FRANK }, 56 | new Object[] { First.APPLE, Second.DINGO, Third.GREAT }, 57 | new Object[] { First.APPLE, Second.DINGO, Third.HEAVY }, 58 | new Object[] { First.APPLE, Second.DINGO, Third.ITALY }, 59 | new Object[] { First.APPLE, Second.EAGLE, Third.FRANK }, 60 | new Object[] { First.APPLE, Second.EAGLE, Third.GREAT }, 61 | new Object[] { First.APPLE, Second.EAGLE, Third.HEAVY }, 62 | new Object[] { First.APPLE, Second.EAGLE, Third.ITALY }, 63 | new Object[] { First.BEARD, Second.DINGO, Third.FRANK }, 64 | new Object[] { First.BEARD, Second.DINGO, Third.GREAT }, 65 | new Object[] { First.BEARD, Second.DINGO, Third.HEAVY }, 66 | new Object[] { First.BEARD, Second.DINGO, Third.ITALY }, 67 | new Object[] { First.BEARD, Second.EAGLE, Third.FRANK }, 68 | new Object[] { First.BEARD, Second.EAGLE, Third.GREAT }, 69 | new Object[] { First.BEARD, Second.EAGLE, Third.HEAVY }, 70 | new Object[] { First.BEARD, Second.EAGLE, Third.ITALY }, 71 | new Object[] { First.COUCH, Second.DINGO, Third.FRANK }, 72 | new Object[] { First.COUCH, Second.DINGO, Third.GREAT }, 73 | new Object[] { First.COUCH, Second.DINGO, Third.HEAVY }, 74 | new Object[] { First.COUCH, Second.DINGO, Third.ITALY }, 75 | new Object[] { First.COUCH, Second.EAGLE, Third.FRANK }, 76 | new Object[] { First.COUCH, Second.EAGLE, Third.GREAT }, 77 | new Object[] { First.COUCH, Second.EAGLE, Third.HEAVY }, 78 | new Object[] { First.COUCH, Second.EAGLE, Third.ITALY } 79 | ); 80 | } 81 | 82 | @Test public void noMethodParameters() throws NoSuchMethodException { 83 | class Example { 84 | public void example() {} 85 | } 86 | Method method = Example.class.getMethod("example"); 87 | 88 | Object[][] objects = Burst.explodeArguments(method); 89 | assertThat(objects).containsExactly(new Object[0]); 90 | } 91 | 92 | @Test public void nonEnumMethodParameter() throws NoSuchMethodException { 93 | class Example { 94 | public void example(Object o) {} 95 | } 96 | Method method = Example.class.getMethod("example", Object.class); 97 | 98 | thrown.expect(IllegalStateException.class); 99 | thrown.expectMessage(Example.class.getName() 100 | + ".example method parameter #1 type is not an enum. (java.lang.Object)"); 101 | 102 | Burst.explodeArguments(method); 103 | } 104 | 105 | @Test public void singleMethodParameters() throws NoSuchMethodException { 106 | class Example { 107 | public void example(First first) {} 108 | } 109 | Method method = Example.class.getMethod("example", First.class); 110 | 111 | Object[][] objects = Burst.explodeArguments(method); 112 | assertThat(objects).containsExactly( 113 | new Object[] { First.APPLE }, 114 | new Object[] { First.BEARD }, 115 | new Object[] { First.COUCH } 116 | ); 117 | } 118 | 119 | @Test public void multipleMethodParameters() throws NoSuchMethodException { 120 | class Example { 121 | public void example(First first, Second second, Third third) {} 122 | } 123 | Method method = Example.class.getMethod("example", First.class, Second.class, Third.class); 124 | 125 | Object[][] objects = Burst.explodeArguments(method); 126 | assertThat(objects).containsExactly( 127 | new Object[] { First.APPLE, Second.DINGO, Third.FRANK }, 128 | new Object[] { First.APPLE, Second.DINGO, Third.GREAT }, 129 | new Object[] { First.APPLE, Second.DINGO, Third.HEAVY }, 130 | new Object[] { First.APPLE, Second.DINGO, Third.ITALY }, 131 | new Object[] { First.APPLE, Second.EAGLE, Third.FRANK }, 132 | new Object[] { First.APPLE, Second.EAGLE, Third.GREAT }, 133 | new Object[] { First.APPLE, Second.EAGLE, Third.HEAVY }, 134 | new Object[] { First.APPLE, Second.EAGLE, Third.ITALY }, 135 | new Object[] { First.BEARD, Second.DINGO, Third.FRANK }, 136 | new Object[] { First.BEARD, Second.DINGO, Third.GREAT }, 137 | new Object[] { First.BEARD, Second.DINGO, Third.HEAVY }, 138 | new Object[] { First.BEARD, Second.DINGO, Third.ITALY }, 139 | new Object[] { First.BEARD, Second.EAGLE, Third.FRANK }, 140 | new Object[] { First.BEARD, Second.EAGLE, Third.GREAT }, 141 | new Object[] { First.BEARD, Second.EAGLE, Third.HEAVY }, 142 | new Object[] { First.BEARD, Second.EAGLE, Third.ITALY }, 143 | new Object[] { First.COUCH, Second.DINGO, Third.FRANK }, 144 | new Object[] { First.COUCH, Second.DINGO, Third.GREAT }, 145 | new Object[] { First.COUCH, Second.DINGO, Third.HEAVY }, 146 | new Object[] { First.COUCH, Second.DINGO, Third.ITALY }, 147 | new Object[] { First.COUCH, Second.EAGLE, Third.FRANK }, 148 | new Object[] { First.COUCH, Second.EAGLE, Third.GREAT }, 149 | new Object[] { First.COUCH, Second.EAGLE, Third.HEAVY }, 150 | new Object[] { First.COUCH, Second.EAGLE, Third.ITALY } 151 | ); 152 | } 153 | 154 | @Test public void noArguments() { 155 | String actual = Burst.friendlyName(new Enum[0]); 156 | assertThat(actual).isEqualTo(""); 157 | } 158 | 159 | @Test public void singleArgument() { 160 | String actual = Burst.friendlyName(new Enum[] { First.APPLE }); 161 | assertThat(actual).isEqualTo("APPLE"); 162 | } 163 | 164 | @Test public void multipleArguments() { 165 | String actual = Burst.friendlyName(new Enum[] { First.APPLE, Second.EAGLE, Third.ITALY }); 166 | assertThat(actual).isEqualTo("APPLE, EAGLE, ITALY"); 167 | } 168 | 169 | @Test public void singleArgument_named() { 170 | String actual = Burst.friendlyName(new Enum[] { First.APPLE }, new Annotation[][] { 171 | {TestUtil.createName("Fruit")} 172 | }); 173 | assertThat(actual).isEqualTo("Fruit=APPLE"); 174 | } 175 | 176 | @Test public void multipleArguments_named() { 177 | String actual = Burst.friendlyName(new Enum[] { First.APPLE, Second.EAGLE, Third.ITALY }, new Annotation[][] { 178 | {TestUtil.createName("Fruit")}, 179 | {TestUtil.createName("Bird")}, 180 | {TestUtil.createName("Country")} 181 | }); 182 | assertThat(actual).isEqualTo("Fruit=APPLE, Bird=EAGLE, Country=ITALY"); 183 | } 184 | 185 | @Test public void multipleArguments_multipleAnnotations_named() { 186 | String actual = Burst.friendlyName(new Enum[] { First.APPLE, Second.EAGLE, Third.ITALY }, new Annotation[][] { 187 | {TestUtil.createFake(), TestUtil.createName("Fruit")}, 188 | {TestUtil.createFake(), TestUtil.createName("Bird")}, 189 | {TestUtil.createName("Country"), TestUtil.createFake()} 190 | }); 191 | assertThat(actual).isEqualTo("Fruit=APPLE, Bird=EAGLE, Country=ITALY"); 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /burst-junit4/src/test/java/com/squareup/burst/BurstJUnit4Test.java: -------------------------------------------------------------------------------- 1 | package com.squareup.burst; 2 | 3 | import org.junit.Test; 4 | import org.junit.runners.model.InitializationError; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public final class BurstJUnit4Test { 9 | private final JournalingListener listener = new JournalingListener(); 10 | 11 | @Test public void constructorNone() throws InitializationError { 12 | BurstJUnit4 runner = new BurstJUnit4(ConstructorNoArgumentTest.class); 13 | runner.run(listener.notifier()); 14 | assertThat(listener.journal()).containsExactly( 15 | "START testMethod(com.squareup.burst.ConstructorNoArgumentTest)", 16 | "FINISH testMethod(com.squareup.burst.ConstructorNoArgumentTest)"); 17 | } 18 | 19 | @Test public void constructorSingle() throws InitializationError { 20 | BurstJUnit4 runner = new BurstJUnit4(ConstructorSingleArgumentTest.class); 21 | runner.run(listener.notifier()); 22 | assertThat(listener.journal()).containsExactly( 23 | "START testMethod(com.squareup.burst.ConstructorSingleArgumentTest[PEPSI])", 24 | "FINISH testMethod(com.squareup.burst.ConstructorSingleArgumentTest[PEPSI])", 25 | "START testMethod(com.squareup.burst.ConstructorSingleArgumentTest[COKE])", 26 | "FINISH testMethod(com.squareup.burst.ConstructorSingleArgumentTest[COKE])", 27 | "START testMethod(com.squareup.burst.ConstructorSingleArgumentTest[RC_COLA])", 28 | "FINISH testMethod(com.squareup.burst.ConstructorSingleArgumentTest[RC_COLA])"); 29 | } 30 | 31 | @Test public void constructorSingleNamed() throws InitializationError { 32 | BurstJUnit4 runner = new BurstJUnit4(ConstructorSingleNamedArgumentTest.class); 33 | runner.run(listener.notifier()); 34 | assertThat(listener.journal()).containsExactly( 35 | "START testMethod(com.squareup.burst.ConstructorSingleNamedArgumentTest[Drink=PEPSI])", 36 | "FINISH testMethod(com.squareup.burst.ConstructorSingleNamedArgumentTest[Drink=PEPSI])", 37 | "START testMethod(com.squareup.burst.ConstructorSingleNamedArgumentTest[Drink=COKE])", 38 | "FINISH testMethod(com.squareup.burst.ConstructorSingleNamedArgumentTest[Drink=COKE])", 39 | "START testMethod(com.squareup.burst.ConstructorSingleNamedArgumentTest[Drink=RC_COLA])", 40 | "FINISH testMethod(com.squareup.burst.ConstructorSingleNamedArgumentTest[Drink=RC_COLA])"); 41 | } 42 | 43 | @Test public void constructorMultiple() throws InitializationError { 44 | BurstJUnit4 runner = new BurstJUnit4(ConstructorMultipleArgumentTest.class); 45 | runner.run(listener.notifier()); 46 | assertThat(listener.journal()).containsExactly( 47 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[PEPSI, CHIPS])", 48 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[PEPSI, CHIPS])", 49 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[PEPSI, NUTS])", 50 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[PEPSI, NUTS])", 51 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[PEPSI, CANDY])", 52 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[PEPSI, CANDY])", 53 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[COKE, CHIPS])", 54 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[COKE, CHIPS])", 55 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[COKE, NUTS])", 56 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[COKE, NUTS])", 57 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[COKE, CANDY])", 58 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[COKE, CANDY])", 59 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[RC_COLA, CHIPS])", 60 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[RC_COLA, CHIPS])", 61 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[RC_COLA, NUTS])", 62 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[RC_COLA, NUTS])", 63 | "START testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[RC_COLA, CANDY])", 64 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleArgumentTest[RC_COLA, CANDY])"); 65 | } 66 | 67 | @Test public void constructorMultipleNames() throws InitializationError { 68 | BurstJUnit4 runner = new BurstJUnit4(ConstructorMultipleNamedArgumentTest.class); 69 | runner.run(listener.notifier()); 70 | assertThat(listener.journal()).containsExactly( 71 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=PEPSI, CHIPS])", 72 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=PEPSI, CHIPS])", 73 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=PEPSI, NUTS])", 74 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=PEPSI, NUTS])", 75 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=PEPSI, CANDY])", 76 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=PEPSI, CANDY])", 77 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=COKE, CHIPS])", 78 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=COKE, CHIPS])", 79 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=COKE, NUTS])", 80 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=COKE, NUTS])", 81 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=COKE, CANDY])", 82 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=COKE, CANDY])", 83 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=RC_COLA, CHIPS])", 84 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=RC_COLA, CHIPS])", 85 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=RC_COLA, NUTS])", 86 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=RC_COLA, NUTS])", 87 | "START testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=RC_COLA, CANDY])", 88 | "FINISH testMethod(com.squareup.burst.ConstructorMultipleNamedArgumentTest[Drink=RC_COLA, CANDY])"); 89 | } 90 | 91 | @Test public void method() throws InitializationError { 92 | BurstJUnit4 runner = new BurstJUnit4(MethodTest.class); 93 | runner.run(listener.notifier()); 94 | assertThat(listener.journal()).containsExactly( 95 | "START single[PEPSI](com.squareup.burst.MethodTest)", 96 | "FINISH single[PEPSI](com.squareup.burst.MethodTest)", 97 | "START single[COKE](com.squareup.burst.MethodTest)", 98 | "FINISH single[COKE](com.squareup.burst.MethodTest)", 99 | "START single[RC_COLA](com.squareup.burst.MethodTest)", 100 | "FINISH single[RC_COLA](com.squareup.burst.MethodTest)", 101 | "START none(com.squareup.burst.MethodTest)", 102 | "FINISH none(com.squareup.burst.MethodTest)", 103 | "START multiple[PEPSI, CHIPS](com.squareup.burst.MethodTest)", 104 | "FINISH multiple[PEPSI, CHIPS](com.squareup.burst.MethodTest)", 105 | "START multiple[PEPSI, NUTS](com.squareup.burst.MethodTest)", 106 | "FINISH multiple[PEPSI, NUTS](com.squareup.burst.MethodTest)", 107 | "START multiple[PEPSI, CANDY](com.squareup.burst.MethodTest)", 108 | "FINISH multiple[PEPSI, CANDY](com.squareup.burst.MethodTest)", 109 | "START multiple[COKE, CHIPS](com.squareup.burst.MethodTest)", 110 | "FINISH multiple[COKE, CHIPS](com.squareup.burst.MethodTest)", 111 | "START multiple[COKE, NUTS](com.squareup.burst.MethodTest)", 112 | "FINISH multiple[COKE, NUTS](com.squareup.burst.MethodTest)", 113 | "START multiple[COKE, CANDY](com.squareup.burst.MethodTest)", 114 | "FINISH multiple[COKE, CANDY](com.squareup.burst.MethodTest)", 115 | "START multiple[RC_COLA, CHIPS](com.squareup.burst.MethodTest)", 116 | "FINISH multiple[RC_COLA, CHIPS](com.squareup.burst.MethodTest)", 117 | "START multiple[RC_COLA, NUTS](com.squareup.burst.MethodTest)", 118 | "FINISH multiple[RC_COLA, NUTS](com.squareup.burst.MethodTest)", 119 | "START multiple[RC_COLA, CANDY](com.squareup.burst.MethodTest)", 120 | "FINISH multiple[RC_COLA, CANDY](com.squareup.burst.MethodTest)"); 121 | } 122 | 123 | @Test public void namedMethod() throws InitializationError { 124 | BurstJUnit4 runner = new BurstJUnit4(NamedMethodTest.class); 125 | runner.run(listener.notifier()); 126 | assertThat(listener.journal()).containsExactly( 127 | "START single[Drink=PEPSI](com.squareup.burst.NamedMethodTest)", 128 | "FINISH single[Drink=PEPSI](com.squareup.burst.NamedMethodTest)", 129 | "START single[Drink=COKE](com.squareup.burst.NamedMethodTest)", 130 | "FINISH single[Drink=COKE](com.squareup.burst.NamedMethodTest)", 131 | "START single[Drink=RC_COLA](com.squareup.burst.NamedMethodTest)", 132 | "FINISH single[Drink=RC_COLA](com.squareup.burst.NamedMethodTest)", 133 | "START none(com.squareup.burst.NamedMethodTest)", 134 | "FINISH none(com.squareup.burst.NamedMethodTest)", 135 | "START multiple[Drink=PEPSI, CHIPS](com.squareup.burst.NamedMethodTest)", 136 | "FINISH multiple[Drink=PEPSI, CHIPS](com.squareup.burst.NamedMethodTest)", 137 | "START multiple[Drink=PEPSI, NUTS](com.squareup.burst.NamedMethodTest)", 138 | "FINISH multiple[Drink=PEPSI, NUTS](com.squareup.burst.NamedMethodTest)", 139 | "START multiple[Drink=PEPSI, CANDY](com.squareup.burst.NamedMethodTest)", 140 | "FINISH multiple[Drink=PEPSI, CANDY](com.squareup.burst.NamedMethodTest)", 141 | "START multiple[Drink=COKE, CHIPS](com.squareup.burst.NamedMethodTest)", 142 | "FINISH multiple[Drink=COKE, CHIPS](com.squareup.burst.NamedMethodTest)", 143 | "START multiple[Drink=COKE, NUTS](com.squareup.burst.NamedMethodTest)", 144 | "FINISH multiple[Drink=COKE, NUTS](com.squareup.burst.NamedMethodTest)", 145 | "START multiple[Drink=COKE, CANDY](com.squareup.burst.NamedMethodTest)", 146 | "FINISH multiple[Drink=COKE, CANDY](com.squareup.burst.NamedMethodTest)", 147 | "START multiple[Drink=RC_COLA, CHIPS](com.squareup.burst.NamedMethodTest)", 148 | "FINISH multiple[Drink=RC_COLA, CHIPS](com.squareup.burst.NamedMethodTest)", 149 | "START multiple[Drink=RC_COLA, NUTS](com.squareup.burst.NamedMethodTest)", 150 | "FINISH multiple[Drink=RC_COLA, NUTS](com.squareup.burst.NamedMethodTest)", 151 | "START multiple[Drink=RC_COLA, CANDY](com.squareup.burst.NamedMethodTest)", 152 | "FINISH multiple[Drink=RC_COLA, CANDY](com.squareup.burst.NamedMethodTest)"); 153 | } 154 | 155 | @Test public void constructorAndMethod() throws InitializationError { 156 | BurstJUnit4 runner = new BurstJUnit4(ConstructorAndMethodTest.class); 157 | runner.run(listener.notifier()); 158 | assertThat(listener.journal()).containsExactly( 159 | "START single[CHIPS](com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 160 | "FINISH single[CHIPS](com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 161 | "START single[NUTS](com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 162 | "FINISH single[NUTS](com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 163 | "START single[CANDY](com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 164 | "FINISH single[CANDY](com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 165 | "START none(com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 166 | "FINISH none(com.squareup.burst.ConstructorAndMethodTest[PEPSI])", 167 | "START single[CHIPS](com.squareup.burst.ConstructorAndMethodTest[COKE])", 168 | "FINISH single[CHIPS](com.squareup.burst.ConstructorAndMethodTest[COKE])", 169 | "START single[NUTS](com.squareup.burst.ConstructorAndMethodTest[COKE])", 170 | "FINISH single[NUTS](com.squareup.burst.ConstructorAndMethodTest[COKE])", 171 | "START single[CANDY](com.squareup.burst.ConstructorAndMethodTest[COKE])", 172 | "FINISH single[CANDY](com.squareup.burst.ConstructorAndMethodTest[COKE])", 173 | "START none(com.squareup.burst.ConstructorAndMethodTest[COKE])", 174 | "FINISH none(com.squareup.burst.ConstructorAndMethodTest[COKE])", 175 | "START single[CHIPS](com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 176 | "FINISH single[CHIPS](com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 177 | "START single[NUTS](com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 178 | "FINISH single[NUTS](com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 179 | "START single[CANDY](com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 180 | "FINISH single[CANDY](com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 181 | "START none(com.squareup.burst.ConstructorAndMethodTest[RC_COLA])", 182 | "FINISH none(com.squareup.burst.ConstructorAndMethodTest[RC_COLA])"); 183 | } 184 | 185 | @Test public void singleField() throws InitializationError { 186 | BurstJUnit4 runner = new BurstJUnit4(SingleFieldTest.class); 187 | runner.run(listener.notifier()); 188 | assertThat(listener.journal()).containsExactly( 189 | "START testMethod(com.squareup.burst.SingleFieldTest[PEPSI])", 190 | "FINISH testMethod(com.squareup.burst.SingleFieldTest[PEPSI])", 191 | "START testMethod(com.squareup.burst.SingleFieldTest[COKE])", 192 | "FINISH testMethod(com.squareup.burst.SingleFieldTest[COKE])", 193 | "START testMethod(com.squareup.burst.SingleFieldTest[RC_COLA])", 194 | "FINISH testMethod(com.squareup.burst.SingleFieldTest[RC_COLA])"); 195 | } 196 | 197 | @Test public void multipleFields() throws InitializationError { 198 | BurstJUnit4 runner = new BurstJUnit4(MultipleFieldsTest.class); 199 | runner.run(listener.notifier()); 200 | assertThat(listener.journal()).containsExactly( 201 | "START testMethod(com.squareup.burst.MultipleFieldsTest[PEPSI, CHIPS])", 202 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[PEPSI, CHIPS])", 203 | "START testMethod(com.squareup.burst.MultipleFieldsTest[PEPSI, NUTS])", 204 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[PEPSI, NUTS])", 205 | "START testMethod(com.squareup.burst.MultipleFieldsTest[PEPSI, CANDY])", 206 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[PEPSI, CANDY])", 207 | "START testMethod(com.squareup.burst.MultipleFieldsTest[COKE, CHIPS])", 208 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[COKE, CHIPS])", 209 | "START testMethod(com.squareup.burst.MultipleFieldsTest[COKE, NUTS])", 210 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[COKE, NUTS])", 211 | "START testMethod(com.squareup.burst.MultipleFieldsTest[COKE, CANDY])", 212 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[COKE, CANDY])", 213 | "START testMethod(com.squareup.burst.MultipleFieldsTest[RC_COLA, CHIPS])", 214 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[RC_COLA, CHIPS])", 215 | "START testMethod(com.squareup.burst.MultipleFieldsTest[RC_COLA, NUTS])", 216 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[RC_COLA, NUTS])", 217 | "START testMethod(com.squareup.burst.MultipleFieldsTest[RC_COLA, CANDY])", 218 | "FINISH testMethod(com.squareup.burst.MultipleFieldsTest[RC_COLA, CANDY])"); 219 | } 220 | 221 | @Test public void multipleNamedFields() throws InitializationError { 222 | BurstJUnit4 runner = new BurstJUnit4(MultipleNamedFieldsTest.class); 223 | runner.run(listener.notifier()); 224 | assertThat(listener.journal()).containsExactly( 225 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=PEPSI, CHIPS])", 226 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=PEPSI, CHIPS])", 227 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=PEPSI, NUTS])", 228 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=PEPSI, NUTS])", 229 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=PEPSI, CANDY])", 230 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=PEPSI, CANDY])", 231 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=COKE, CHIPS])", 232 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=COKE, CHIPS])", 233 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=COKE, NUTS])", 234 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=COKE, NUTS])", 235 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=COKE, CANDY])", 236 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=COKE, CANDY])", 237 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=RC_COLA, CHIPS])", 238 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=RC_COLA, CHIPS])", 239 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=RC_COLA, NUTS])", 240 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=RC_COLA, NUTS])", 241 | "START testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=RC_COLA, CANDY])", 242 | "FINISH testMethod(com.squareup.burst.MultipleNamedFieldsTest[Drink=RC_COLA, CANDY])"); 243 | } 244 | 245 | @Test public void fieldAndMethod() throws InitializationError { 246 | BurstJUnit4 runner = new BurstJUnit4(FieldAndMethodTest.class); 247 | runner.run(listener.notifier()); 248 | assertThat(listener.journal()).containsExactly( 249 | "START testMethod[PEPSI](com.squareup.burst.FieldAndMethodTest[CHIPS])", 250 | "FINISH testMethod[PEPSI](com.squareup.burst.FieldAndMethodTest[CHIPS])", 251 | "START testMethod[COKE](com.squareup.burst.FieldAndMethodTest[CHIPS])", 252 | "FINISH testMethod[COKE](com.squareup.burst.FieldAndMethodTest[CHIPS])", 253 | "START testMethod[RC_COLA](com.squareup.burst.FieldAndMethodTest[CHIPS])", 254 | "FINISH testMethod[RC_COLA](com.squareup.burst.FieldAndMethodTest[CHIPS])", 255 | "START testMethod[PEPSI](com.squareup.burst.FieldAndMethodTest[NUTS])", 256 | "FINISH testMethod[PEPSI](com.squareup.burst.FieldAndMethodTest[NUTS])", 257 | "START testMethod[COKE](com.squareup.burst.FieldAndMethodTest[NUTS])", 258 | "FINISH testMethod[COKE](com.squareup.burst.FieldAndMethodTest[NUTS])", 259 | "START testMethod[RC_COLA](com.squareup.burst.FieldAndMethodTest[NUTS])", 260 | "FINISH testMethod[RC_COLA](com.squareup.burst.FieldAndMethodTest[NUTS])", 261 | "START testMethod[PEPSI](com.squareup.burst.FieldAndMethodTest[CANDY])", 262 | "FINISH testMethod[PEPSI](com.squareup.burst.FieldAndMethodTest[CANDY])", 263 | "START testMethod[COKE](com.squareup.burst.FieldAndMethodTest[CANDY])", 264 | "FINISH testMethod[COKE](com.squareup.burst.FieldAndMethodTest[CANDY])", 265 | "START testMethod[RC_COLA](com.squareup.burst.FieldAndMethodTest[CANDY])", 266 | "FINISH testMethod[RC_COLA](com.squareup.burst.FieldAndMethodTest[CANDY])"); 267 | } 268 | 269 | @Test public void namedFieldAndNamedMethod() throws InitializationError { 270 | BurstJUnit4 runner = new BurstJUnit4(NamedFieldAndNamedMethodTest.class); 271 | runner.run(listener.notifier()); 272 | assertThat(listener.journal()).containsExactly( 273 | "START testMethod[Drink=PEPSI](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CHIPS])", 274 | "FINISH testMethod[Drink=PEPSI](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CHIPS])", 275 | "START testMethod[Drink=COKE](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CHIPS])", 276 | "FINISH testMethod[Drink=COKE](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CHIPS])", 277 | "START testMethod[Drink=RC_COLA](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CHIPS])", 278 | "FINISH testMethod[Drink=RC_COLA](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CHIPS])", 279 | "START testMethod[Drink=PEPSI](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=NUTS])", 280 | "FINISH testMethod[Drink=PEPSI](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=NUTS])", 281 | "START testMethod[Drink=COKE](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=NUTS])", 282 | "FINISH testMethod[Drink=COKE](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=NUTS])", 283 | "START testMethod[Drink=RC_COLA](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=NUTS])", 284 | "FINISH testMethod[Drink=RC_COLA](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=NUTS])", 285 | "START testMethod[Drink=PEPSI](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CANDY])", 286 | "FINISH testMethod[Drink=PEPSI](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CANDY])", 287 | "START testMethod[Drink=COKE](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CANDY])", 288 | "FINISH testMethod[Drink=COKE](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CANDY])", 289 | "START testMethod[Drink=RC_COLA](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CANDY])", 290 | "FINISH testMethod[Drink=RC_COLA](com.squareup.burst.NamedFieldAndNamedMethodTest[Food=CANDY])"); 291 | } 292 | } 293 | --------------------------------------------------------------------------------