diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..180225b45 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,29 @@ +name: Maven Build + +on: + pull_request: + branches: [ "master" ] + +jobs: + build: + name: Java ${{ matrix.java }} build + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: true + matrix: + java: [ 8, 11 ] + experimental: [false] + include: + - java: 17 + experimental: true + steps: + - uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.java }} + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn --no-transfer-progress --batch-mode package --file pom.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..e13554928 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Maven deploy release +# Reset before repeated testing +# git push --delete origin 2.13.0 +# git push -d origin release/2.13.0 release/2.13.0-snapshot +on: + push: + branches: [ "release/*" ] +jobs: + deploy: + # Avoid loops + if: github.event.commits[0].author.email != 'actions-bot@github.com' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Maven Central Repository + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import + gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase + + - name: Split branch into release version + env: + BRANCH: ${{ github.ref_name }} + id: split + run: echo "fragment=${BRANCH##*/}" >> $GITHUB_OUTPUT + + - name: Configure git user + run: | + git config user.email "actions-bot@github.com" + git config user.name "GitHub Actions (run by ${{ github.actor }})" + + - name: Maven set release version + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + GH_TOKEN: ${{ github.token }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + run: | + mvn --no-transfer-progress versions:set -DoldVersion=\* -DnewVersion=${{ steps.split.outputs.fragment }} -DgroupId=org.jmock -DgenerateBackupPoms=false + git add . + git commit --no-edit -m "Release version ${{ steps.split.outputs.fragment }}" + git tag -a -m "Release version ${{ steps.split.outputs.fragment }}" ${{ steps.split.outputs.fragment }} + mvn --no-transfer-progress --batch-mode deploy -P release + mvn versions:set -DoldVersion=* -DnextSnapshot=true -DgroupId=org.jmock -DgenerateBackupPoms=false + git checkout -b ${{ github.ref_name }}-snapshot + git add . + git commit --no-edit -m "Open development for next release" + git push origin ${{ github.ref_name }} HEAD --tags + gh pr create --repo jmock-developers/jmock-library -B master --title 'Merge ${{ github.ref_name }} back into master' --body 'Created by Github action' diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 000000000..eeccdf358 --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,27 @@ +name: Maven deploy snapshot +on: + push: + branches: [ master ] +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Maven Central Repository + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + server-id: ossrh + # used for signing the testjar + gpg-passphrase: secret + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + + - name: Publish package + run: mvn --no-transfer-progress --batch-mode deploy + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f8193fd67..000000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -language: java - -jdk: - - openjdk-ea - - openjdk11 - - oraclejdk11 - - openjdk10 -# - oraclejdk10 - - openjdk9 - - oraclejdk8 #not supported - -matrix: - allow_failures: - - jdk: openjdk-ea - - jdk: oraclejdk8 - -cache: - directories: - - $HOME/.m2 #for faster builds - -env: -# Travis has slow VMs? - global: - - blitzerThreads=1 blitzerActions=1 blitzerTimeout=1000 - - secure: "XBIyEF5RxH/zKjh0gle5ce/FRe9pP5b46LXKNOfwlau5QYvn8WXC41Sb6yYC8VW3vI9S930BnVGZONiDXsSWuylLezdu2g88nwJ2CsTxHU0Yg2GAzKwaorBQqyK/6w2foRRXtz69NJccfdIdPuDZSEhJtTKWSmMMQ7U65KH+zzQ=" - - secure: "SSlAKOg8QLEAkZX1ehy9c6FR5R6gMzLhc6u2Smq9tm8JsWVz4EtS6NnTQaosthrNEfLJwVVCfmOh2dzvYZfhtbKAUMdkox45tem4+LZfgkSaQAGjSvsC6geioa4Alo6AKyR8foc5Y7P34f9ujYSZHNyRG9F6E0Qq2zj/hzHhstU=" diff --git a/README.DEVELOPMENT b/README.DEVELOPMENT index 12bd72b0c..dd3cc6e4f 100644 --- a/README.DEVELOPMENT +++ b/README.DEVELOPMENT @@ -48,5 +48,5 @@ export SONATYPE_USERNAME=UUU export SONATYPE_PASSWORD=PPPP mvn clean deploy -P release --settings settings.xml -Dgpg.keyname=XXXXXXXX -mvn versions:set -DoldVersion=* -DnewVersion=2.11.0-SNAPSHOT -DgroupId=org.jmock -DgenerateBackupPoms=false +mvn versions:set -DoldVersion=* -DnewVersion=2.13.0-SNAPSHOT -DgroupId=org.jmock -DgenerateBackupPoms=false diff --git a/README.md b/README.md index b00bdd101..e1920c2ae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # JMock Library -[![Build Status](https://travis-ci.org/jmock-developers/jmock-library.svg?branch=jmock2)](https://travis-ci.org/jmock-developers/jmock-library) +[![Maven Build](https://github.com/jmock-developers/jmock-library/actions/workflows/build.yml/badge.svg)](https://github.com/jmock-developers/jmock-library/actions/workflows/build.yml) [![Maven Central](https://img.shields.io/maven-central/v/org.jmock/jmock.svg?label=Maven%20Central)](https://mvnrepository.com/artifact/org.jmock) # Maven @@ -7,20 +7,33 @@ org.jmock jmock-junit5 - 2.12.0 + 2.13.0 test ``` # Gradle + +## Gradle 7 and Above + +```gradle +testImplementation( + "junit:junit5:5.3.1", + "org.jmock:jmock-junit5:2.13.0" +) +``` + +## Gradle 6 and Below + ``` testCompile( "junit:junit5:5.3.1", - "org.jmock:jmock-junit5:2.12.0" + "org.jmock:jmock-junit5:2.13.0" ) ``` # Recent Changes -## 2.10.0 -### JUnit 5 Support +- See https://github.com/jmock-developers/jmock-library/releases/tag/2.13.0 + +# JUnit 5 Support * Swap @Rule JUnit4Mockery for @RegisterExtension JMock5Mockery * Assign to a non-private JMock5Mockery or JUnit5 won't use it @@ -49,27 +62,6 @@ public class JUnit5TestThatDoesSatisfyExpectations { ### JUnit 4 moved to provided scope in org.jmock:jmock * This allows dependents to use other versions of junit or other test frameworks (e.g. junit 5) -### Java7 Support will be dropped next release - -## 2.9.0 -* Dropped JDK 6 compliance. -* Exposed the InvocationDispatcher so that ThreadingPolicies - -## Upgrading to 2.8.X -Are you seeing NPEs? - -We have had to make a breaking change to `with()`. Tests using `with(any(matcher))` for method signatures that require native types will throw `NullPointerException`. - -You should change - - oneOf(mock).methodWithIntParams(with(any(Integer.class))); - -to the following - - oneOf(mock).methodWithIntParams(with.intIs(anything()); -This is due to a compiler change in Java 1.7. The 2.6.0 release was compiled with Java 1.6 so it did not suffer this problem. - - # Advantages of jMock 2 over jMock 1 * Uses real method calls, not strings, so you can refactor more easily and autocomplete in the IDE. diff --git a/jmock-example/pom.xml b/jmock-example/pom.xml index 69da4f75c..2196162f7 100644 --- a/jmock-example/pom.xml +++ b/jmock-example/pom.xml @@ -12,7 +12,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT ../pom.xml diff --git a/jmock-imposters-testdata/pom.xml b/jmock-imposters-testdata/pom.xml new file mode 100644 index 000000000..786b0c452 --- /dev/null +++ b/jmock-imposters-testdata/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + + + org.jmock + jmock-parent + 2.13.1-SNAPSHOT + + + jmock-imposters-testdata + + + 2.13.13 + + + + + org.scala-lang + scala-library + ${scala.version} + + + + + + + net.alchim31.maven + scala-maven-plugin + 4.3.1 + + + + compile + testCompile + + + + + + + \ No newline at end of file diff --git a/jmock-imposters-testdata/src/main/scala/org/jmock/testdata/scalaexample/BadlyAnnotated.scala b/jmock-imposters-testdata/src/main/scala/org/jmock/testdata/scalaexample/BadlyAnnotated.scala new file mode 100644 index 000000000..34dfa6a92 --- /dev/null +++ b/jmock-imposters-testdata/src/main/scala/org/jmock/testdata/scalaexample/BadlyAnnotated.scala @@ -0,0 +1,3 @@ +package org.jmock.testdata.scalaexample + +case class BadlyAnnotated(@FieldOnly badlyAnnotated: String) diff --git a/jmock-imposters-testdata/src/main/scala/org/jmock/testdata/scalaexample/FieldOnly.java b/jmock-imposters-testdata/src/main/scala/org/jmock/testdata/scalaexample/FieldOnly.java new file mode 100644 index 000000000..464e4ff9f --- /dev/null +++ b/jmock-imposters-testdata/src/main/scala/org/jmock/testdata/scalaexample/FieldOnly.java @@ -0,0 +1,12 @@ +package org.jmock.testdata.scalaexample; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface FieldOnly { +} + diff --git a/jmock-imposters-tests/pom.xml b/jmock-imposters-tests/pom.xml index c52040353..eaa557444 100644 --- a/jmock-imposters-tests/pom.xml +++ b/jmock-imposters-tests/pom.xml @@ -6,7 +6,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT jmock-imposters-tests @@ -39,6 +39,12 @@ ${project.version} test + + org.jmock + jmock-imposters-testdata + ${project.version} + test + org.junit.jupiter diff --git a/jmock-imposters-tests/src/test/java/org/jmock/test/acceptance/BadAnnotationsAcceptanceTests.java b/jmock-imposters-tests/src/test/java/org/jmock/test/acceptance/BadAnnotationsAcceptanceTests.java new file mode 100644 index 000000000..74180116f --- /dev/null +++ b/jmock-imposters-tests/src/test/java/org/jmock/test/acceptance/BadAnnotationsAcceptanceTests.java @@ -0,0 +1,32 @@ +package org.jmock.test.acceptance; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.api.Imposteriser; +import org.jmock.test.unit.lib.legacy.CodeGeneratingImposteriserParameterResolver; +import org.jmock.testdata.scalaexample.BadlyAnnotated; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import static org.junit.Assert.assertSame; + +public class BadAnnotationsAcceptanceTests { + Mockery context = new Mockery(); + + @ParameterizedTest + @ArgumentsSource(CodeGeneratingImposteriserParameterResolver.class) + public void shouldMockScalaCaseClassWithFieldAnnotationOnParameter(Imposteriser imposteriser) { + context.setImposteriser(imposteriser); + + final BadlyAnnotated mock = context.mock(BadlyAnnotated.class); + final String result = "a mock result"; + context.checking(new Expectations() { + { + oneOf(mock).badlyAnnotated(); + will(returnValue(result)); + } + }); + + assertSame(mock.badlyAnnotated(), result); + } +} diff --git a/jmock-imposters/pom.xml b/jmock-imposters/pom.xml index 03c4bbf15..e495a39b5 100644 --- a/jmock-imposters/pom.xml +++ b/jmock-imposters/pom.xml @@ -6,7 +6,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT jmock-imposters Class mocks are more numerous than interface mocks, so drop the legacy name diff --git a/jmock-imposters/src/main/java/org/jmock/imposters/ByteBuddyClassImposteriser.java b/jmock-imposters/src/main/java/org/jmock/imposters/ByteBuddyClassImposteriser.java index 799ba79c6..3195ae0db 100644 --- a/jmock-imposters/src/main/java/org/jmock/imposters/ByteBuddyClassImposteriser.java +++ b/jmock-imposters/src/main/java/org/jmock/imposters/ByteBuddyClassImposteriser.java @@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import net.bytebuddy.dynamic.scaffold.TypeValidation; import org.jmock.api.Imposteriser; import org.jmock.api.Invocation; import org.jmock.api.Invokable; @@ -134,6 +135,7 @@ static public Object intercept( private Class proxyClass(final Invokable mockObject, final Class mockedType, Class... ancilliaryTypes) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Builder builder = new ByteBuddy() + .with(TypeValidation.DISABLED) .with(new NamingStrategy.SuffixingRandom(JMOCK_KEY, JMOCK_KEY.toLowerCase())) .subclass(mockedType) .implement(ancilliaryTypes) diff --git a/jmock-junit3/pom.xml b/jmock-junit3/pom.xml index 6c5abadf6..2b42cc5f1 100644 --- a/jmock-junit3/pom.xml +++ b/jmock-junit3/pom.xml @@ -7,7 +7,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT jmock-junit3 diff --git a/jmock-junit4/pom.xml b/jmock-junit4/pom.xml index 531ab198d..0af8ea998 100644 --- a/jmock-junit4/pom.xml +++ b/jmock-junit4/pom.xml @@ -10,7 +10,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT diff --git a/jmock-junit4/src/test/java/org/jmock/test/acceptance/junit4/JUnit4TestRunnerTests.java b/jmock-junit4/src/test/java/org/jmock/test/acceptance/junit4/JUnit4TestRunnerTests.java index b8e11e198..0272295e0 100644 --- a/jmock-junit4/src/test/java/org/jmock/test/acceptance/junit4/JUnit4TestRunnerTests.java +++ b/jmock-junit4/src/test/java/org/jmock/test/acceptance/junit4/JUnit4TestRunnerTests.java @@ -1,6 +1,7 @@ package org.jmock.test.acceptance.junit4; import junit.framework.TestCase; +import org.junit.runners.model.InvalidTestClassError; import testdata.jmock.acceptance.junit4.*; public class JUnit4TestRunnerTests extends TestCase { @@ -46,9 +47,10 @@ public void testTheJUnit4TestRunnerReportsIfMoreThanOneMockeryIsFound() { public void testDetectsNonPublicBeforeMethodsCorrectly() { listener.runTestIn(JUnit4TestWithNonPublicBeforeMethod.class); - listener.assertTestFailedWith(Throwable.class); + listener.assertTestFailedWith(InvalidTestClassError.class); assertEquals("should have detected non-public before method", - "Method before() should be public", + "Invalid test class 'testdata.jmock.acceptance.junit4.JUnit4TestWithNonPublicBeforeMethod':\n" + + " 1. Method before() should be public", listener.failure.getMessage()); } diff --git a/jmock-junit5/pom.xml b/jmock-junit5/pom.xml index a67c8e48b..b54ea2cc7 100644 --- a/jmock-junit5/pom.xml +++ b/jmock-junit5/pom.xml @@ -9,7 +9,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT diff --git a/jmock-junit5/src/main/java/org/jmock/junit5/JUnit5Mockery.java b/jmock-junit5/src/main/java/org/jmock/junit5/JUnit5Mockery.java index f5880a541..355206751 100644 --- a/jmock-junit5/src/main/java/org/jmock/junit5/JUnit5Mockery.java +++ b/jmock-junit5/src/main/java/org/jmock/junit5/JUnit5Mockery.java @@ -2,6 +2,7 @@ import java.lang.reflect.Field; import java.util.List; +import java.util.Optional; import org.jmock.Mockery; import org.jmock.auto.internal.Mockomatic; @@ -59,7 +60,7 @@ public JUnit5Mockery() { } @Override - public void beforeEach(ExtensionContext context) throws Exception { + public void beforeEach(ExtensionContext context) { if (context.getTestClass().isPresent()) { Class testCaseClass = context.getTestClass().get(); List allFields = AllDeclaredFields.in(testCaseClass); @@ -70,7 +71,7 @@ public void beforeEach(ExtensionContext context) throws Exception { } @Override - public void afterEach(ExtensionContext context) throws Exception { + public void afterEach(ExtensionContext context) { assertIsSatisfied(); } @@ -79,37 +80,32 @@ private void fillInAutoMocks(final Object target, List allFields) { } private static void checkMockery(ExtensionContext context, Class testCaseClass) { - Field mockeryField = findMockeryField(testCaseClass, context); - try { - // private extension fields are not called - // field will at least be default scope if we're called. - mockeryField.setAccessible(true); - if(mockeryField.get(context.getRequiredTestInstance()) == null) { - throw new IllegalStateException("JUnit5Mockery field should not be null"); + findMockeryField(testCaseClass, context).ifPresent(mockeryField -> { + try { + // private extension fields are not called + // field will at least be default scope if we're called. + mockeryField.setAccessible(true); + if (mockeryField.get(context.getRequiredTestInstance()) == null) { + throw new IllegalStateException("JUnit5Mockery field should not be null"); + } + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new ExtensionConfigurationException("Could not check the mockery", e); } - } catch (IllegalArgumentException e) { - throw new ExtensionConfigurationException("Could not check the mockery", e); - } catch (IllegalAccessException e) { - throw new ExtensionConfigurationException("Could not check the mockery", e); - } + }); } - private static Field findMockeryField(Class testClass, ExtensionContext context) { - Field mockeryField = null; + private static Optional findMockeryField(Class testClass, ExtensionContext context) { + Optional mockeryField = Optional.empty(); for (Field field : AllDeclaredFields.in(testClass)) { if (Mockery.class.isAssignableFrom(field.getType())) { - if (mockeryField != null) { + if (mockeryField.isPresent()) { throw new ExtensionConfigurationException("more than one Mockery found in test class " + testClass); } - mockeryField = field; + mockeryField = Optional.of(field); } } - if (mockeryField == null) { - throw new ExtensionConfigurationException("no Mockery found in test class " + testClass); - } - return mockeryField; } } diff --git a/jmock-junit5/src/test/java/org/jmock/junit5/acceptance/JUnit5TestRunnerTests.java b/jmock-junit5/src/test/java/org/jmock/junit5/acceptance/JUnit5TestRunnerTests.java index 05c8fb199..ed13e0811 100644 --- a/jmock-junit5/src/test/java/org/jmock/junit5/acceptance/JUnit5TestRunnerTests.java +++ b/jmock-junit5/src/test/java/org/jmock/junit5/acceptance/JUnit5TestRunnerTests.java @@ -50,11 +50,11 @@ public void testReportsMocksAreNotSatisfiedWhenExpectedExceptionIsThrown() { listener.assertTestFailedWith(AssertionError.class); } - // See issue JMOCK-219 + // See issue https://github.com/jmock-developers/jmock-library/issues/155 @Test - public void testTheJUnit5TestRunnerReportsIfNoMockeryIsFound() { + public void testTheJUnit5TestRunnerIgnoreIfNoMockeryIsFound() { listener.runTestIn(JUnit5TestThatCreatesNoMockery.class); - listener.assertTestFailedWithInitializationError(); + listener.assertTestSucceeded(); } // See issue JMOCK-219 diff --git a/jmock-legacy/pom.xml b/jmock-legacy/pom.xml index 3e1e31bfb..6f123004b 100644 --- a/jmock-legacy/pom.xml +++ b/jmock-legacy/pom.xml @@ -10,7 +10,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT diff --git a/jmock/pom.xml b/jmock/pom.xml index 54cdd3b54..108080b9e 100644 --- a/jmock/pom.xml +++ b/jmock/pom.xml @@ -10,7 +10,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 7dd55db3b..370f80db5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT pom jMock 2 Parent @@ -47,7 +47,7 @@ true 2.2 - 4.12 + 4.13.2 8.0.1 3.1 2.0b6 @@ -65,6 +65,7 @@ jmock-legacy jmock-example jmock-imposters-tests + jmock-imposters-testdata @@ -83,7 +84,7 @@ - 3.0.5 + 3.6.3 @@ -337,6 +338,10 @@ true ${gpg.keyname} + + --pinentry-mode + loopback + @@ -350,34 +355,39 @@ - - org.apache.maven.plugins - maven-source-plugin + maven-jar-plugin + 3.3.0 - attach-sources + default-jar + package - jar-no-fork + jar - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - attach-javadocs + javadoc-jar + package + + jar + + + javadoc + + + + sources-jar + package jar + + sources + + diff --git a/settings.xml b/settings.xml deleted file mode 100644 index 7f700b222..000000000 --- a/settings.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - ossrh - - ${env.SONATYPE_USERNAME} - - ${env.SONATYPE_PASSWORD} - - - \ No newline at end of file diff --git a/testjar/gpg-agent.conf b/testjar/gpg-agent.conf new file mode 100644 index 000000000..a8d680a1d --- /dev/null +++ b/testjar/gpg-agent.conf @@ -0,0 +1 @@ +allow-loopback-pinentry \ No newline at end of file diff --git a/testjar/pom.xml b/testjar/pom.xml index 0e2dc0588..589957549 100644 --- a/testjar/pom.xml +++ b/testjar/pom.xml @@ -11,7 +11,7 @@ org.jmock jmock-parent - 2.12.0 + 2.13.1-SNAPSHOT ../pom.xml @@ -61,6 +61,10 @@ ${basedir} secret false + + --pinentry-mode + loopback +