├── .git-blame-ignore-revs ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .looper.yml ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── LICENSE.txt ├── README.md ├── classloading.md ├── mvnw ├── mvnw.cmd ├── pom.xml ├── reactorpropagation.md ├── takari-plugin-integration-testing └── pom.xml ├── takari-plugin-testing-its ├── pom.xml └── src │ └── test │ ├── java │ └── io │ │ └── takari │ │ └── maven │ │ └── testing │ │ └── test │ │ ├── IntegrationTest.java │ │ ├── JUnit5IntegrationTests.java │ │ └── JUnit5UnitTests.java │ ├── projects │ ├── basic │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── basic │ │ │ │ └── BasicMojo.java │ │ │ └── test │ │ │ ├── java │ │ │ └── basic │ │ │ │ ├── IntegrationTest.java │ │ │ │ ├── TargetVersion.java │ │ │ │ └── UnitTest.java │ │ │ ├── projects │ │ │ └── basic │ │ │ │ └── pom.xml │ │ │ └── test.properties │ ├── classloading │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── test │ │ │ │ └── dependencytree │ │ │ │ └── ClassloadingMojo.java │ │ │ └── test │ │ │ ├── java │ │ │ └── test │ │ │ │ └── dependencytree │ │ │ │ └── test │ │ │ │ └── ClassloadingMojoTest.java │ │ │ └── projects │ │ │ └── basic │ │ │ └── pom.xml │ ├── guicescopes │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── guicescopes │ │ │ │ ├── GuiceScopesMojo.java │ │ │ │ └── MojoScopedComponent.java │ │ │ └── test │ │ │ └── java │ │ │ └── guicescopes │ │ │ └── GuiceScopesTest.java │ ├── pomconfig │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── pomconfig │ │ │ │ └── PomConfigMojo.java │ │ │ └── test │ │ │ ├── java │ │ │ └── pomconfig │ │ │ │ └── PomConfigTest.java │ │ │ └── projects │ │ │ └── basic │ │ │ ├── output.txt-expected │ │ │ └── pom.xml │ └── settings │ │ ├── pom.xml │ │ ├── settings.xml │ │ └── src │ │ └── test │ │ ├── java │ │ └── settings │ │ │ └── SettingsTest.java │ │ └── test.properties │ └── test.properties ├── takari-plugin-testing ├── .settings │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.jdt.ui.prefs ├── pom.xml └── src │ └── main │ └── java │ └── io │ └── takari │ └── maven │ └── testing │ ├── AbstractTestMavenRuntime.java │ ├── AbstractTestResources.java │ ├── Maven30xRuntime.java │ ├── Maven311Runtime.java │ ├── Maven321Runtime.java │ ├── Maven325Runtime.java │ ├── Maven331Runtime.java │ ├── MavenRuntime.java │ ├── TestMavenRuntime.java │ ├── TestMavenRuntime5.java │ ├── TestProperties.java │ ├── TestResources.java │ ├── TestResources5.java │ └── executor │ ├── Embedded3xLauncher.java │ ├── ForkedLauncher.java │ ├── LauncherException.java │ ├── MavenExecution.java │ ├── MavenExecutionResult.java │ ├── MavenInstallationUtils.java │ ├── MavenInstallations.java │ ├── MavenLauncher.java │ ├── MavenRuntime.java │ ├── MavenVersions.java │ └── junit │ ├── ForcedMavenRuntimeBuilderParameterResolver.java │ ├── MavenHomeUtils.java │ ├── MavenInstallationDisplayNameFormatter.java │ ├── MavenInstallationTestInvocationContext.java │ ├── MavenInstallationsTestExtension.java │ ├── MavenJUnitTestRunner.java │ ├── MavenPluginTest.java │ ├── MavenRuntimeBuilderParameterResolver.java │ ├── MavenVersionDisplayNameFormatter.java │ ├── MavenVersionResolver.java │ ├── MavenVersionTestInvocationContext.java │ └── MavenVersionsTestExtension.java └── testproperties.md /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 86c7ff9c091701bc47ccaa5b56fec85ff77b1ddf 2 | 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | permissions: 10 | contents: read 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.ref }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | build: 18 | name: Build 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | persist-credentials: false 24 | - uses: actions/setup-java@v4 25 | with: 26 | java-version: '21' 27 | distribution: 'temurin' 28 | - uses: actions/cache@v4 29 | with: 30 | path: ~/.m2/repository 31 | key: maven-${{ hashFiles('**/pom.xml') }} 32 | - run: ./mvnw clean install -e -B -V 33 | test: 34 | name: Test 35 | runs-on: ubuntu-latest 36 | needs: build 37 | if: (!cancelled()) && needs.build.result == 'success' 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | os: [ "ubuntu-latest", "windows-latest", "macos-latest" ] 42 | jdk: [ "17", "21", "24" ] 43 | steps: 44 | - uses: actions/checkout@v4 45 | with: 46 | persist-credentials: false 47 | - uses: actions/setup-java@v4 48 | with: 49 | java-version: ${{ matrix.jdk }} 50 | distribution: 'temurin' 51 | - uses: actions/cache@v4 52 | with: 53 | path: ~/.m2/repository 54 | key: maven-${{ hashFiles('**/pom.xml') }} 55 | - run: ./mvnw verify -e -B -V -f takari-plugin-testing-its 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .project 3 | .classpath 4 | .settings/ 5 | .idea 6 | *.iml 7 | -------------------------------------------------------------------------------- /.looper.yml: -------------------------------------------------------------------------------- 1 | language: workflow 2 | 3 | tools: 4 | jdk: 5 | - 1.8.0_91-b14 6 | maven: 7 | - 3.3.9 8 | 9 | flows: 10 | master: 11 | - (on master, name mvn deploy) mvn clean deploy -B 12 | pr: 13 | - call: master 14 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and 10 | b) in the case of each subsequent Contributor: 11 | i) changes to the Program, and 12 | ii) additions to the Program; 13 | where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. 14 | "Contributor" means any person or entity that distributes the Program. 15 | 16 | "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. 17 | 18 | "Program" means the Contributions distributed in accordance with this Agreement. 19 | 20 | "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 21 | 22 | 2. GRANT OF RIGHTS 23 | 24 | a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. 25 | b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. 26 | c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. 27 | d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 28 | 3. REQUIREMENTS 29 | 30 | A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: 31 | 32 | a) it complies with the terms and conditions of this Agreement; and 33 | b) its license agreement: 34 | i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; 35 | ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; 36 | iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and 37 | iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. 38 | When the Program is made available in source code form: 39 | 40 | a) it must be made available under this Agreement; and 41 | b) a copy of this Agreement must be included with each copy of the Program. 42 | Contributors may not remove or alter any copyright notices contained within the Program. 43 | 44 | Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 45 | 46 | 4. COMMERCIAL DISTRIBUTION 47 | 48 | Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. 49 | 50 | For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 51 | 52 | 5. NO WARRANTY 53 | 54 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 55 | 56 | 6. DISCLAIMER OF LIABILITY 57 | 58 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 59 | 60 | 7. GENERAL 61 | 62 | If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 63 | 64 | If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. 65 | 66 | All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. 67 | 68 | Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. 69 | 70 | This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Takari Maven Plugin Testing Framework 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/io.takari.maven.plugins/takari-plugin-testing-project.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.takari.maven.plugins/takari-plugin-testing-project) 4 | [![Verify](https://github.com/takari/takari-plugin-testing-project/actions/workflows/ci.yml/badge.svg)](https://github.com/takari/takari-plugin-testing-project/actions/workflows/ci.yml) 5 | [![Reproducible Builds](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/io/takari/maven/plugins/takari-plugin-testing/badge.json)](https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/io/takari/maven/plugins/takari-plugin-testing/README.md) 6 | 7 | Small, cohesive, one-stop library for developing unit and integration tests for 8 | Maven plugins. Provides alternative to, and arguably supersedes, 9 | maven-plugin-testing-harness and maven-verifier. 10 | 11 | Features and benefits 12 | 13 | * Convenient junit4-based API 14 | * Convenient junit5-based API 15 | * Flexible unit test mojo configuration API simplifies test project setup 16 | and maintenance 17 | * Collocate main and test code in the same build module 18 | * No need to install or deploy plugins to run tests 19 | * Run plugins integration tests against multiple Maven versions 20 | * Integration with takari-lifecycle and incrementalbuild library 21 | * Fully supported by Maven Development Tools m2e extension 22 | * [2.1.0+] full support for all maven versions 3.0 to 3.6.2 23 | * [3.0.0+] full support for all maven versions 3.6.3+, requires Java11+ 24 | 25 | ### Unit testing 26 | 27 | pom.xml 28 | 29 | takari-maven-plugin 30 | 31 | 32 | io.takari.maven.plugins 33 | takari-plugin-testing 34 | 2.8.0 35 | test 36 | 37 | 38 | 39 | 40 | org.apache.maven 41 | maven-core 42 | ${mavenVersion} 43 | test 44 | 45 | 46 | org.apache.maven 47 | maven-compat 48 | ${mavenVersion} 49 | test 50 | 51 | 52 | JUnit 4 test code 53 | 54 | public class PluginUnitTest { 55 | @Rule 56 | public final TestResources resources = new TestResources(); 57 | 58 | @Rule 59 | public final TestMavenRuntime maven = new TestMavenRuntime(); 60 | 61 | @Test 62 | public void test() throws Exception { 63 | File basedir = resources.getBasedir("testproject"); 64 | maven.executeMojo(basedir, "mymojo", newParameter("name", "value"); 65 | assertFilesPresent(basedir, "target/output.txt"); 66 | } 67 | } 68 | 69 | JUnit 5 test code 70 | 71 | class PluginUnitTest { 72 | @RegisterExtension 73 | final TestResources5 resources = new TestResources5(); 74 | 75 | @RegisterExtension 76 | final TestMavenRuntime5 maven = new TestMavenRuntime5(); 77 | 78 | @Test 79 | void test() throws Exception { 80 | File basedir = resources.getBasedir("testproject"); 81 | maven.executeMojo(basedir, "mymojo", newParameter("name", "value"); 82 | assertFilesPresent(basedir, "target/output.txt"); 83 | } 84 | } 85 | 86 | ### Integration testing 87 | 88 | pom.xml 89 | 90 | takari-maven-plugin 91 | 92 | 93 | io.takari.maven.plugins 94 | takari-plugin-testing 95 | 3.0.0 96 | test 97 | 98 | 99 | io.takari.maven.plugins 100 | takari-plugin-integration-testing 101 | 3.0.0 102 | pom 103 | test 104 | 105 | 106 | JUnit 4 test 107 | 108 | @RunWith(MavenJUnitTestRunner.class) 109 | @MavenVersions({"3.2.3", "3.2.5"}) 110 | public class PluginIntegrationTest { 111 | @Rule 112 | public final TestResources resources = new TestResources(); 113 | 114 | public final MavenRuntime maven; 115 | 116 | public PluginIntegrationTest(MavenRuntimeBuilder mavenBuilder) { 117 | this.maven = mavenBuilder.withCliOptions("-B", "-U").build(); 118 | } 119 | 120 | @Test 121 | public void test() throws Exception { 122 | File basedir = resources.getBasedir("basic"); 123 | maven.forProject(basedir) 124 | .withCliOption("-Dproperty=value") 125 | .withCliOption("-X") 126 | .execute("deploy") 127 | .assertErrorFreeLog() 128 | .assertLogText("some build message"); 129 | } 130 | } 131 | 132 | JUnit 5 test 133 | 134 | @MavenVersions({"3.2.3", "3.2.5"}) 135 | class PluginIntegrationTest { 136 | @RegisterExtension 137 | final TestResources5 resources = new TestResources5(); 138 | 139 | private final MavenRuntime maven; 140 | 141 | PluginIntegrationTest(MavenRuntimeBuilder mavenBuilder) { 142 | this.maven = mavenBuilder.withCliOptions("-B", "-U").build(); 143 | } 144 | 145 | @MavenPluginTest 146 | void test() throws Exception { 147 | File basedir = resources.getBasedir("basic"); 148 | maven.forProject(basedir) 149 | .withCliOption("-Dproperty=value") 150 | .withCliOption("-X") 151 | .execute("deploy") 152 | .assertErrorFreeLog() 153 | .assertLogText("some build message"); 154 | } 155 | } 156 | 157 | Note that test pom.xml can use `${it-project.version}` to reference the version of the takari-maven-plugin or takari-maven-component project being build (i.e. the project which defines the test). For example, 158 | 159 | 160 | 161 | 162 | example.groupId 163 | example-plugin 164 | ${it-project.version} 165 | ... 166 | 167 | ... 168 | 169 | 170 | ### Hudson users beware 171 | 172 | Hudson Maven 3 does not use -s/--settings standard maven command 173 | option to enable "managed" settings.xml files in maven builds. Because of this, test 174 | JVMs launched by Maven builds are not able to use the settings.xml files will 175 | fail with artifact resolution errors. To workaround, use filesystem-based settings xml 176 | files and pass them using -s/--settings options. 177 | -------------------------------------------------------------------------------- /classloading.md: -------------------------------------------------------------------------------- 1 | Unit test classloading 2 | ============ 3 | Unit test harness (maven surefire, to be more precise) uses the same 4 | `scope=test` classpath to load Maven core, plugin and plugin test classes. 5 | This is different from normal Maven invocation, where each plugin is loaded 6 | in a separate classloader with access to only a subset of Maven core classes 7 | and dependencies. 8 | 9 | This implies that version of common dependencies, Guava in particular, used 10 | during unit tests must be compatible with both Maven core and the plugin being 11 | tested. In practical terms, this means that version of Guava used by a plugin 12 | must match version of Maven the plugin depends on during the build. 13 | For example, if a plugin depends on Maven `3.3.3`, the plugin must use 14 | Guava `18.0` to be able to use unit test harness. Consider writing integration 15 | tests if the plugin requires incompatible versions of Guava and Maven core. 16 | 17 | Integration test classloading 18 | ============ 19 | 20 | As during normal Maven invocation, Maven core classes and each plugin are 21 | loaded into separate classloaders during integration tests. Only limited number 22 | of Mavencore classes are visible to plugin classloaders, see 23 | [Maven classloading](http://takari.io/book/91-maven-classloading.html) for more 24 | details. 25 | 26 | There are two ways to run integration tests. By default, integration tests are 27 | executed in separate classloader(s) within the same test jvm, a.k.a. "embedded" 28 | integration test execution. This allows debugger breakpoints anywhere in Maven, 29 | plugin or test code. All embedded integration test executions share jvm system 30 | and extension classloaders, which may cause problem when testing with 31 | Maven `3.2.5` and earlier (see below). There are no known problems with 32 | embedded integration test execution when using Maven `3.3.1` and newer. 33 | 34 | Integration tests can also be executed in a forked jvm, i.e. exactly the same 35 | way Maven is executed during normal command-line invocation. Forked execution 36 | provides "perfect" classloading environment, but it does not support end-to-end 37 | debugging and is noticeably slower compared to embedded execution. 38 | 39 | @RunWith(MavenJUnitTestRunner.class) 40 | @MavenVersions({"3.0.5", "3.2.5"}) 41 | public class PluginIntegrationTest { 42 | 43 | public final MavenRuntime maven; 44 | 45 | public PluginIntegrationTest(MavenRuntimeBuilder mavenBuilder) { 46 | this.maven = mavenBuilder.forkedBuilder().build(); 47 | } 48 | 49 | ... 50 | } 51 | 52 | ----- 53 | 54 | ## Test dependencies "leak" into integration test runtime (Maven 3.2.5 and older) 55 | 56 | This problem affects integration tests under the following circumstances 57 | 58 | * The integration test uses Maven `3.2.5` or older 59 | * The integration test is collocated with the plugin and/or unit tests in the 60 | Maven project. Or, more precisely, Maven core artifacts are present on 61 | integration test classpath 62 | * The plugin uses `ClassLoader#loadClass` to provide different behaviour 63 | depending on version of Maven used. Only `maven-dependency-tree` is known to 64 | use this technique. 65 | 66 | Maven `3.2.5` and older uses jvm system classloader is the parent of both 67 | plugin and project extensions class realms. This results in duplicate maven 68 | core classes visible to the plugins during integration tests. Here is little 69 | ascii-art diagram to illustrate the problem 70 | 71 | +------------------------+ 72 | | extensions classloader | 73 | +------------------------+ 74 | / \ 75 | / \ 76 | +-------------------------+ +-----------------------------+ 77 | | system classloader | | integration test maven core | 78 | | unit test maven core | | | 79 | +-------------------------+ +-----------------------------+ 80 | \ / 81 | (parent) \ / (import) 82 | +--------------+ 83 | | plugin realm | 84 | +--------------+ 85 | 86 | The reason this works for most integration tests, is because ClassRealm 87 | implementaion uses imported classes before it delegates to parent classloader, 88 | so expected integration test maven core classes are used. 89 | 90 | The duplicate maven-core classes cause problems for maven plugins that use 91 | `ClassLoader#loadClass` and access unexpected maven core classses from system 92 | classloader. For example, `maven-dependency-tree` uses presence of 93 | `org.eclipse.aether.*` classes as indication of maven 3.1+ and misbehaves 94 | for projects that are compiled with maven 3.1+ but integration tested with 95 | maven 3.0.x. 96 | 97 | ## Workarounds 98 | 99 | ### Separate integration test project module (recommended) 100 | 101 | It should be possible to setup integration test project module such that it 102 | does not have maven core dependencies. Although test dependencies will still 103 | be visible to plugin and project extension realms, these dependencies most 104 | likely won't cause problems. 105 | 106 | ### Forked integration test maven runtime 107 | 108 | Forked maven runtime provides "perfect" classpath but it is significantly 109 | slower and does not support end-to-end debugging from m2e workspace. 110 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 4.0.0 13 | 14 | 15 | io.takari 16 | takari 17 | 70 18 | 19 | 20 | io.takari.maven.plugins 21 | takari-plugin-testing-project 22 | 3.1.2-SNAPSHOT 23 | pom 24 | 25 | ${project.groupId}:${project.artifactId} 26 | 27 | 28 | takari-plugin-testing 29 | takari-plugin-integration-testing 30 | takari-plugin-testing-its 31 | 32 | 33 | 34 | scm:git:git@github.com:takari/takari-plugin-testing-project.git 35 | scm:git:git@github.com:takari/takari-plugin-testing-project.git 36 | HEAD 37 | https://github.com/takari/takari-plugin-testing-project 38 | 39 | 40 | 41 | 8 42 | 2.3.0 43 | 44 | 3.9.9 45 | 0.9.0.M4 46 | 6.0.0 47 | 1.9.23 48 | 1.7.36 49 | 50 | 51 | 52 | 53 | 54 | junit 55 | junit 56 | 4.13.2 57 | 58 | 59 | org.junit 60 | junit-bom 61 | 5.13.0 62 | pom 63 | import 64 | 65 | 66 | org.codehaus.plexus 67 | plexus-utils 68 | 3.6.0 69 | 70 | 71 | org.slf4j 72 | slf4j-api 73 | 1.7.36 74 | 75 | 76 | org.slf4j 77 | slf4j-simple 78 | 1.7.36 79 | 80 | 81 | 82 | 83 | org.apache.maven 84 | maven-core 85 | ${mavenVersion} 86 | 87 | 88 | org.apache.maven 89 | maven-plugin-api 90 | ${mavenVersion} 91 | 92 | 93 | org.apache.maven 94 | maven-model 95 | ${mavenVersion} 96 | 97 | 98 | org.apache.maven 99 | maven-resolver-provider 100 | ${mavenVersion} 101 | 102 | 103 | org.apache.maven 104 | maven-artifact 105 | ${mavenVersion} 106 | 107 | 108 | org.apache.maven 109 | maven-embedder 110 | ${mavenVersion} 111 | 112 | 113 | org.apache.maven 114 | maven-settings 115 | ${mavenVersion} 116 | 117 | 118 | org.apache.maven 119 | maven-settings-builder 120 | ${mavenVersion} 121 | 122 | 123 | org.apache.maven.resolver 124 | maven-resolver-api 125 | ${aetherVersion} 126 | 127 | 128 | javax.inject 129 | javax.inject 130 | 1 131 | 132 | 133 | org.eclipse.sisu 134 | org.eclipse.sisu.inject 135 | ${sisuVersion} 136 | 137 | 138 | org.eclipse.sisu 139 | org.eclipse.sisu.plexus 140 | ${sisuVersion} 141 | 142 | 143 | com.google.inject 144 | guice 145 | ${sisuGuiceVersion} 146 | classes 147 | 148 | 149 | 150 | 151 | org.apache.commons 152 | commons-exec 153 | 1.5.0 154 | 155 | 156 | org.codehaus.plexus 157 | plexus-classworlds 158 | 2.9.0 159 | 160 | 161 | io.takari.m2e.workspace 162 | org.eclipse.m2e.workspace.cli 163 | 0.4.0 164 | 165 | 166 | 167 | 168 | 169 | org.apache.commons 170 | commons-compress 171 | 1.26.0 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | org.apache.maven.plugins 181 | maven-release-plugin 182 | 183 | takari-plugin-testing-@{project.version} 184 | 185 | 186 | 187 | org.gaul 188 | modernizer-maven-plugin 189 | 190 | 191 | false 192 | 193 | 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /reactorpropagation.md: -------------------------------------------------------------------------------- 1 | Reactor propagation 2 | =================== 3 | 4 | Reactor propagation is a concept (and corresponding implementation) that allows 5 | Maven build resolve dependencies directly from "outer" reactor build, without 6 | the need to install or deploy outer build artifacts to a shared artifact 7 | repository. 8 | 9 | Reactor propagation allows integration build resolve plugin artifact(s) from 10 | the "outer" plugin build. It also allows Maven resolve dependencies from m2e 11 | workspace when run/debug "as maven build" 12 | 13 | 14 | ## Integration test execution during maven plugin build 15 | 16 | +--------------------+ 17 | | outer plugin build | 18 | +--------------------+ 19 | | (forked) 20 | | 21 | | +-------------------------+ 22 | \ | test jvm | 23 | | +-------------------+ | 24 | | | integration test | | 25 | | | build (embedded) | | 26 | | +-------------------+ | 27 | +-------------------------+ 28 | 29 | Outer build: 30 | 31 | * takari-lifecycle-plugin:testProperties 32 | * puts location of m2e reactor reader artifact, i.e. 33 | org.eclipse.m2e.workspace.cli, to the test properties 34 | * generates reactor state file and puts its location to the test properties 35 | * writes the test properties to target/test-classes/test.properties 36 | * Surefire forks the test jvm with target/test-classes on classpath 37 | 38 | Test jvm: 39 | 40 | * pligin test harness 41 | * it reads reactor state file location from test.properties and adds it as 42 | -D parameter to the maven executor (embedded in the ascii-diagram above) 43 | * it adds reactor reader artifact, i.e. , to the test maven runtime 44 | classworlds.conf file 45 | * test Maven reads the -D parameter and puts it into System.properties 46 | * reactor reader uses the System.property to locate outer reactor state 47 | 48 | ## Forked integration test execution 49 | 50 | +--------------------+ 51 | | outer plugin build | 52 | +--------------------+ 53 | | (forked) 54 | | 55 | | +------------+ 56 | \ | test jvm | 57 | +------------+ 58 | | (forked) 59 | | 60 | | +-------------------+ 61 | \ | integration test | 62 | | build (embedded) | 63 | +-------------------+ 64 | 65 | Not much different from the embedded integration test execution. 66 | 67 | ## m2e run/debug as junit and maven junit test 68 | 69 | +---------------+ 70 | | m2e workspace | 71 | +---------------+ 72 | | (forked) 73 | | 74 | | +-------------------------+ 75 | \ | test jvm | 76 | | +-------------------+ | 77 | | | integration test | | 78 | | | build (embedded) | | 79 | | +-------------------+ | 80 | +-------------------------+ 81 | 82 | Workspace artifacts are written to the reactor state file by m2e; 83 | location of the file is passed as -D system property to the test jvm. 84 | 85 | > Note that even though takari-lifecycle-plugin:testProperties runs during 86 | > Eclipse workspace build and creates target/test-classes/test.properties, 87 | > **it does not generate reactor state file** because there is no reactor 88 | > build. Test harness must use -D System.property to locate reactor state 89 | > file create by m2e. 90 | 91 | Test harness passes location of the reactor state file as -D parameter to 92 | the test maven build, which converts it to System.property, which is then 93 | used by the reactor reader. 94 | 95 | ## m2e run/debug as maven build 96 | 97 | +---------------+ 98 | | m2e workspace | 99 | +---------------+ 100 | | (forked) 101 | | 102 | | +---------------+ 103 | \ | maven build | 104 | +---------------+ 105 | 106 | Workspace artifacts are written to the reactor state file by m2e; 107 | location of the file is passed as -D system property to the test jvm. 108 | 109 | ## Recursive integration test execution 110 | 111 | Reactor propagation can be applied recursively, when the "inner" build executes 112 | inner build of its own 113 | 114 | +--------------------+ 115 | | outer plugin build | 116 | +--------------------+ 117 | | (forked) 118 | | 119 | | +-------------------------+ 120 | \ | test jvm 1 | 121 | | +-------------------+ | 122 | | | integration test | | 123 | | | build (embedded) | | 124 | | +-------------------+ | 125 | +-------------------------+ 126 | | (forked) 127 | | 128 | | +-------------------------+ 129 | \ | test jvm 2 | 130 | | +-------------------+ | 131 | | | integration test | | 132 | | | build (embedded) | | 133 | | +-------------------+ | 134 | +-------------------------+ 135 | 136 | In this case, takari-lifecycle-plugin:testProperties executed in the first 137 | "inner" integration test build is expected to merge reactor state provided 138 | by the "outer" build with the inner build reactor. The merged reactors state 139 | is then passed to the second "inner" test build, which makes both the outer 140 | and the first inner build artifacts resolvable from the second inner build. 141 | 142 | Note that Surefire, somewhat unhelpfully, propagates build jvm's 143 | System.properties to the forked test jvm. This means the test harness must 144 | for reactor state location in test.properties first, otherwise second inner 145 | build will not be able to resolve artifacts from the first inner build. 146 | 147 | 148 | -------------------------------------------------------------------------------- /takari-plugin-integration-testing/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 4.0.0 13 | 14 | 15 | io.takari.maven.plugins 16 | takari-plugin-testing-project 17 | 3.1.2-SNAPSHOT 18 | 19 | 20 | takari-plugin-integration-testing 21 | pom 22 | 23 | ${project.groupId}:${project.artifactId} 24 | 25 | 26 | 27 | io.takari.maven.plugins 28 | takari-plugin-testing 29 | ${project.version} 30 | 31 | 32 | 33 | org.apache.commons 34 | commons-exec 35 | 36 | 37 | org.codehaus.plexus 38 | plexus-classworlds 39 | 40 | 41 | io.takari.m2e.workspace 42 | org.eclipse.m2e.workspace.cli 43 | 44 | 45 | 46 | 47 | org.apache.commons 48 | commons-compress 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 4.0.0 13 | 14 | 15 | io.takari.maven.plugins 16 | takari-plugin-testing-project 17 | 3.1.2-SNAPSHOT 18 | 19 | 20 | takari-plugin-testing-its 21 | takari-jar 22 | 23 | ${project.groupId}:${project.artifactId} 24 | 25 | 26 | 27 | junit 28 | junit 29 | test 30 | 31 | 32 | org.junit.jupiter 33 | junit-jupiter-api 34 | test 35 | 36 | 37 | org.junit.jupiter 38 | junit-jupiter-params 39 | test 40 | 41 | 42 | org.junit.jupiter 43 | junit-jupiter-engine 44 | test 45 | 46 | 47 | 48 | org.junit.vintage 49 | junit-vintage-engine 50 | test 51 | 52 | 53 | 54 | io.takari.maven.plugins 55 | takari-plugin-testing 56 | ${project.version} 57 | test 58 | 59 | 60 | io.takari.maven.plugins 61 | takari-plugin-integration-testing 62 | ${project.version} 63 | pom 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.eclipse.m2e 73 | lifecycle-mapping 74 | 1.0.0 75 | 76 | 77 | 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-dependency-plugin 82 | [2.8,) 83 | 84 | unpack 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | io.takari.maven.plugins 100 | takari-lifecycle-plugin 101 | 102 | 103 | testProperties 104 | 105 | testProperties 106 | 107 | process-test-resources 108 | 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-dependency-plugin 114 | 115 | 116 | unpack 117 | 118 | unpack 119 | 120 | generate-test-resources 121 | 122 | ${project.build.directory}/maven-installation 123 | 124 | 125 | org.apache.maven 126 | apache-maven 127 | 3.6.3 128 | bin 129 | tar.gz 130 | 131 | 132 | org.apache.maven 133 | apache-maven 134 | 3.8.8 135 | bin 136 | tar.gz 137 | 138 | 139 | org.apache.maven 140 | apache-maven 141 | 3.9.9 142 | bin 143 | tar.gz 144 | 145 | 146 | org.apache.maven 147 | apache-maven 148 | 4.0.0-rc-3 149 | bin 150 | tar.gz 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/java/io/takari/maven/testing/test/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package io.takari.maven.testing.test; 2 | 3 | import io.takari.maven.testing.TestResources; 4 | import io.takari.maven.testing.executor.MavenRuntime; 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.OutputStreamWriter; 9 | import java.io.Writer; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import org.junit.Assume; 14 | import org.junit.Rule; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | import org.junit.runners.Parameterized; 18 | import org.junit.runners.Parameterized.Parameters; 19 | 20 | /** 21 | *
    22 | *
  • The test project is built with maven 3.6.3.
  • 23 | *
  • The test project is built against specific versions of maven, as specified in individual test 24 | * methods
  • 25 | *
  • The test project is not able to resolve test harness from the reactor, hence the outer build 26 | * must run at least install phase.
  • 27 | *
28 | * 29 | * TODO: DOES NOT WORK WITH MAVEN 4! 30 | */ 31 | @RunWith(Parameterized.class) 32 | public class IntegrationTest { 33 | 34 | @Parameters(name = "{0}") 35 | public static List versions() { 36 | List parameters = new ArrayList<>(); 37 | parameters.add(new Object[] {"3.6.3"}); 38 | parameters.add(new Object[] {"3.8.8"}); 39 | parameters.add(new Object[] {"3.9.9"}); 40 | return parameters; 41 | } 42 | 43 | @Rule 44 | public final TestResources resources = new TestResources(); 45 | 46 | public final MavenRuntime maven; 47 | 48 | private final String version; 49 | 50 | public IntegrationTest(String version) throws Exception { 51 | this.version = version; 52 | File mavenHome = new File("target/maven-installation/apache-maven-" + version); 53 | this.maven = MavenRuntime.builder(mavenHome, null).forkedBuilder().build(); 54 | } 55 | 56 | @Test 57 | public void testBasic() throws Exception { 58 | File basedir = resources.getBasedir("basic"); 59 | write( 60 | new File(basedir, "src/test/java/basic/TargetVersion.java"), 61 | "package basic; class TargetVersion { static final String VERSION = \"" + version + "\"; }"); 62 | maven.forProject(basedir) // 63 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 64 | .execute("package") // 65 | .assertErrorFreeLog(); 66 | } 67 | 68 | private void write(File file, String string) throws IOException { 69 | try (Writer w = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { 70 | w.write(string); 71 | } 72 | } 73 | 74 | @Test 75 | public void testGuiceScopes() throws Exception { 76 | // scopes were introduced in 3.2.1 https://issues.apache.org/jira/browse/MNG-5530 77 | Assume.assumeFalse(version.startsWith("3.0") || version.startsWith("3.1")); 78 | 79 | File basedir = resources.getBasedir("guicescopes"); 80 | maven.forProject(basedir) // 81 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 82 | .execute("package") // 83 | .assertErrorFreeLog(); 84 | } 85 | 86 | @Test 87 | public void testPomConfig() throws Exception { 88 | File basedir = resources.getBasedir("pomconfig"); 89 | maven.forProject(basedir) // 90 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 91 | .execute("package") // 92 | .assertErrorFreeLog(); 93 | } 94 | 95 | @Test 96 | public void testUnitTestHarnessHonoursUserSettings() throws Exception { 97 | File basedir = resources.getBasedir("settings"); 98 | maven.forProject(basedir) // 99 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 100 | .execute("test") // 101 | .assertErrorFreeLog(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/java/io/takari/maven/testing/test/JUnit5IntegrationTests.java: -------------------------------------------------------------------------------- 1 | package io.takari.maven.testing.test; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | 5 | import io.takari.maven.testing.TestResources5; 6 | import io.takari.maven.testing.executor.MavenInstallations; 7 | import io.takari.maven.testing.executor.MavenRuntime; 8 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 9 | import io.takari.maven.testing.executor.junit.MavenPluginTest; 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import org.junit.jupiter.api.Assumptions; 14 | import org.junit.jupiter.api.extension.RegisterExtension; 15 | 16 | @MavenInstallations({ 17 | "target/maven-installation/apache-maven-3.6.3", 18 | "target/maven-installation/apache-maven-3.8.8", 19 | "target/maven-installation/apache-maven-3.9.9", 20 | "target/maven-installation/apache-maven-4.0.0-rc-3" 21 | }) 22 | public class JUnit5IntegrationTests { 23 | 24 | @RegisterExtension 25 | final TestResources5 resources = new TestResources5(); 26 | 27 | private final MavenRuntime maven; 28 | 29 | private final String version; 30 | 31 | JUnit5IntegrationTests(MavenRuntimeBuilder builder) throws Exception { 32 | this.maven = builder.withCliOptions("-B", "-e").build(); 33 | this.version = this.maven.getMavenVersion(); 34 | } 35 | 36 | @MavenPluginTest 37 | void testBasic() throws Exception { 38 | File basedir = this.resources.getBasedir("basic"); 39 | this.write( 40 | new File(basedir, "src/test/java/basic/TargetVersion.java"), 41 | "package basic; class TargetVersion { static final String VERSION = \"" + this.version + "\"; }"); 42 | 43 | this.maven 44 | .forProject(basedir) 45 | .withCliOption("-e") // 46 | .execute("package") // 47 | .assertErrorFreeLog(); 48 | } 49 | 50 | private void write(File file, String string) throws IOException { 51 | Files.write(file.toPath(), string.getBytes(UTF_8)); 52 | } 53 | 54 | @MavenPluginTest 55 | void testGuiceScopes() throws Exception { 56 | // scopes were introduced in 3.2.1 https://issues.apache.org/jira/browse/MNG-5530 57 | Assumptions.assumeFalse(this.version.startsWith("3.0") || this.version.startsWith("3.1")); 58 | 59 | File basedir = this.resources.getBasedir("guicescopes"); 60 | this.maven 61 | .forProject(basedir) // 62 | .execute("package") // 63 | .assertErrorFreeLog(); 64 | } 65 | 66 | @MavenPluginTest 67 | void testPomConfig() throws Exception { 68 | File basedir = this.resources.getBasedir("pomconfig"); 69 | this.maven 70 | .forProject(basedir) // 71 | .execute("package") // 72 | .assertErrorFreeLog(); 73 | } 74 | 75 | @MavenPluginTest 76 | void testUnitTestHarnessHonoursUserSettings() throws Exception { 77 | File basedir = this.resources.getBasedir("settings"); 78 | this.maven 79 | .forProject(basedir) // 80 | .execute("test") // 81 | .assertErrorFreeLog(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/java/io/takari/maven/testing/test/JUnit5UnitTests.java: -------------------------------------------------------------------------------- 1 | package io.takari.maven.testing.test; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | import static org.junit.jupiter.api.Assumptions.assumeFalse; 5 | 6 | import io.takari.maven.testing.TestResources5; 7 | import io.takari.maven.testing.executor.MavenRuntime; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.stream.Stream; 15 | import org.junit.jupiter.api.extension.ExtensionContext; 16 | import org.junit.jupiter.api.extension.RegisterExtension; 17 | import org.junit.jupiter.params.ParameterizedTest; 18 | import org.junit.jupiter.params.provider.Arguments; 19 | import org.junit.jupiter.params.provider.ArgumentsProvider; 20 | import org.junit.jupiter.params.provider.ArgumentsSource; 21 | 22 | /** 23 | *
    24 | *
  • The test project is built with maven 3.6.3.
  • 25 | *
  • The test project is built against specific versions of maven, as specified in individual test 26 | * methods
  • 27 | *
  • The test project is not able to resolve test harness from the reactor, hence the outer build 28 | * must run at least install phase.
  • 29 | *
30 | * 31 | * TODO: DOES NOT WORK WITH MAVEN 4! 32 | */ 33 | class JUnit5UnitTests { 34 | 35 | @RegisterExtension 36 | final TestResources5 resources = new TestResources5(); 37 | 38 | @ParameterizedTest 39 | @ArgumentsSource(MavenVersionsSource.class) 40 | void testBasic(MavenRuntime maven, String version) throws Exception { 41 | File basedir = this.resources.getBasedir("basic"); 42 | this.write( 43 | new File(basedir, "src/test/java/basic/TargetVersion.java"), 44 | "package basic; class TargetVersion { static final String VERSION = \"" + version + "\"; }"); 45 | maven.forProject(basedir) // 46 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 47 | .execute("package") // 48 | .assertErrorFreeLog(); 49 | } 50 | 51 | private void write(File file, String string) throws IOException { 52 | Files.write(file.toPath(), string.getBytes(UTF_8)); 53 | } 54 | 55 | @ParameterizedTest 56 | @ArgumentsSource(MavenVersionsSource.class) 57 | void testGuiceScopes(MavenRuntime maven, String version) throws Exception { 58 | // scopes were introduced in 3.2.1 https://issues.apache.org/jira/browse/MNG-5530 59 | assumeFalse(version.startsWith("3.0") || version.startsWith("3.1")); 60 | 61 | File basedir = this.resources.getBasedir("guicescopes"); 62 | maven.forProject(basedir) // 63 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 64 | .execute("package") // 65 | .assertErrorFreeLog(); 66 | } 67 | 68 | @ParameterizedTest 69 | @ArgumentsSource(MavenVersionsSource.class) 70 | void testPomConfig(MavenRuntime maven, String version) throws Exception { 71 | File basedir = this.resources.getBasedir("pomconfig"); 72 | maven.forProject(basedir) // 73 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 74 | .execute("package") // 75 | .assertErrorFreeLog(); 76 | } 77 | 78 | @ParameterizedTest 79 | @ArgumentsSource(MavenVersionsSource.class) 80 | void testUnitTestHarnessHonoursUserSettings(MavenRuntime maven, String version) throws Exception { 81 | File basedir = this.resources.getBasedir("settings"); 82 | maven.forProject(basedir) // 83 | .withCliOptions("-B", "-e", "-DmavenVersion=" + version) // 84 | .execute("test") // 85 | .assertErrorFreeLog(); 86 | } 87 | 88 | static final class MavenVersionsSource implements ArgumentsProvider { 89 | 90 | private List getMavenVersions() { 91 | return Arrays.asList("3.6.3", "3.8.8", "3.9.9"); 92 | } 93 | 94 | @Override 95 | public Stream provideArguments(ExtensionContext context) throws Exception { 96 | List mavenVersions = this.getMavenVersions(); 97 | List arguments = new ArrayList<>(mavenVersions.size()); 98 | for (String version : mavenVersions) { 99 | File mavenHome = new File("target/maven-installation/apache-maven-" + version); 100 | MavenRuntime maven = 101 | MavenRuntime.builder(mavenHome, null).forkedBuilder().build(); 102 | arguments.add(Arguments.of(maven, version)); 103 | } 104 | return arguments.stream(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 13 | 4.0.0 14 | 15 | takari-plugin-testing-its 16 | basic 17 | 1.0-SNAPSHOT 18 | takari-maven-plugin 19 | 20 | 21 | 2.3.0 22 | 3.6.3 23 | 3.6.0 24 | 2.6.0-SNAPSHOT 25 | 26 | 27 | 28 | 29 | org.apache.maven 30 | maven-plugin-api 31 | ${mavenVersion} 32 | provided 33 | 34 | 35 | org.apache.maven.plugin-tools 36 | maven-plugin-annotations 37 | ${mavenPluginPluginVersion} 38 | provided 39 | 40 | 41 | 42 | 43 | junit 44 | junit 45 | 4.13.2 46 | test 47 | 48 | 49 | org.apache.maven 50 | maven-core 51 | ${mavenVersion} 52 | test 53 | 54 | 55 | org.apache.maven 56 | maven-compat 57 | ${mavenVersion} 58 | test 59 | 60 | 61 | io.takari.maven.plugins 62 | takari-plugin-testing 63 | ${it-project.version} 64 | test 65 | 66 | 67 | io.takari.maven.plugins 68 | takari-plugin-integration-testing 69 | ${it-project.version} 70 | pom 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | io.takari.maven.plugins 79 | takari-lifecycle-plugin 80 | ${takariLifecycleVersion} 81 | true 82 | 83 | none 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-dependency-plugin 90 | 2.10 91 | 92 | 93 | properties 94 | initialize 95 | 96 | properties 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.eclipse.m2e 107 | lifecycle-mapping 108 | 1.0.0 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-dependency-plugin 116 | [2.10,) 117 | 118 | properties 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/src/main/java/basic/BasicMojo.java: -------------------------------------------------------------------------------- 1 | package basic; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | 7 | import org.apache.maven.plugin.AbstractMojo; 8 | import org.apache.maven.plugin.MojoExecutionException; 9 | import org.apache.maven.plugin.MojoFailureException; 10 | import org.apache.maven.plugins.annotations.Mojo; 11 | import org.apache.maven.plugins.annotations.Parameter; 12 | 13 | @Mojo(name = "basic") 14 | public class BasicMojo extends AbstractMojo { 15 | 16 | @Parameter(defaultValue = "${project.build.directory}/output.txt") 17 | private File output; 18 | 19 | @Override 20 | public void execute() throws MojoExecutionException, MojoFailureException { 21 | output.getParentFile().mkdirs(); 22 | try { 23 | new FileOutputStream(output).close(); 24 | } catch (IOException e) { 25 | throw new MojoExecutionException("Could not create output file", e); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/src/test/java/basic/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package basic; 2 | 3 | import io.takari.maven.testing.TestResources; 4 | import io.takari.maven.testing.executor.MavenRuntime; 5 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 6 | import io.takari.maven.testing.executor.MavenVersions; 7 | import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner; 8 | 9 | import java.io.File; 10 | 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | @RunWith(MavenJUnitTestRunner.class) 16 | @MavenVersions({TargetVersion.VERSION}) 17 | public class IntegrationTest { 18 | 19 | @Rule 20 | public final TestResources resources = new TestResources(); 21 | 22 | public final MavenRuntime maven; 23 | 24 | public IntegrationTest(MavenRuntimeBuilder builder) throws Exception { 25 | this.maven = builder.build(); 26 | } 27 | 28 | @Test 29 | public void testBasic() throws Exception { 30 | File basedir = resources.getBasedir("basic"); 31 | maven.forProject(basedir).withCliOptions("-e").execute("validate").assertErrorFreeLog(); 32 | TestResources.assertFilesPresent(basedir, "target/output.txt"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/src/test/java/basic/TargetVersion.java: -------------------------------------------------------------------------------- 1 | package basic; 2 | 3 | // thic class is written from integration test java code 4 | class TargetVersion { 5 | static final String VERSION = ""; 6 | } 7 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/src/test/java/basic/UnitTest.java: -------------------------------------------------------------------------------- 1 | package basic; 2 | 3 | import io.takari.maven.testing.TestMavenRuntime; 4 | import io.takari.maven.testing.TestResources; 5 | 6 | import java.io.File; 7 | 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | 11 | public class UnitTest { 12 | 13 | @Rule 14 | public final TestResources resources = new TestResources(); 15 | 16 | @Rule 17 | public final TestMavenRuntime maven = new TestMavenRuntime(); 18 | 19 | @Test 20 | public void testBasic() throws Exception { 21 | File basedir = resources.getBasedir("basic"); 22 | maven.executeMojo(basedir, "basic"); 23 | TestResources.assertFilesPresent(basedir, "target/output.txt"); 24 | } 25 | 26 | @Test 27 | public void testPomless() throws Exception { 28 | File basedir = resources.getBasedir(); 29 | maven.executeMojo(basedir, "basic"); 30 | TestResources.assertFilesPresent(basedir, "target/output.txt"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/src/test/projects/basic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 14 | 4.0.0 15 | 16 | takari-plugin-testing-its 17 | basic-basic 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | 23 | takari-plugin-testing-its 24 | basic 25 | 1.0-SNAPSHOT 26 | 27 | 28 | basic 29 | validate 30 | 31 | basic 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/basic/src/test/test.properties: -------------------------------------------------------------------------------- 1 | mavenVersion=${mavenVersion} 2 | workspaceResolver=${io.takari.m2e.workspace:org.eclipse.m2e.workspace.cli:jar} 3 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/classloading/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 14 | 4.0.0 15 | 16 | takari-plugin-testing-its 17 | classloading 18 | 1.0-SNAPSHOT 19 | takari-maven-plugin 20 | 21 | 33 | 34 | 35 | 2.3.0 36 | 3.6.3 37 | 3.6.0 38 | 2.0.0-SNAPSHOT 39 | 40 | 41 | 42 | 43 | org.apache.maven 44 | maven-plugin-api 45 | ${mavenVersion} 46 | provided 47 | 48 | 49 | org.apache.maven.plugin-tools 50 | maven-plugin-annotations 51 | ${mavenPluginPluginVersion} 52 | provided 53 | 54 | 55 | 56 | 57 | junit 58 | junit 59 | 4.13.2 60 | test 61 | 62 | 63 | org.apache.maven 64 | maven-core 65 | ${mavenVersion} 66 | test 67 | 68 | 69 | org.apache.maven 70 | maven-compat 71 | ${mavenVersion} 72 | test 73 | 74 | 75 | io.takari.maven.plugins 76 | takari-plugin-testing 77 | ${it-project.version} 78 | test 79 | 80 | 81 | io.takari.maven.plugins 82 | takari-plugin-integration-testing 83 | ${it-project.version} 84 | pom 85 | test 86 | 87 | 88 | 89 | 90 | 91 | 92 | io.takari.maven.plugins 93 | takari-lifecycle-plugin 94 | ${takariLifecycleVersion} 95 | true 96 | 97 | 98 | 99 | 100 | 101 | 102 | org.apache.maven.plugins 103 | maven-plugin-plugin 104 | ${mavenPluginPluginVersion} 105 | 106 | 107 | takari-maven-plugin 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/classloading/src/main/java/test/dependencytree/ClassloadingMojo.java: -------------------------------------------------------------------------------- 1 | package test.dependencytree; 2 | 3 | import org.apache.maven.plugin.AbstractMojo; 4 | import org.apache.maven.plugin.MojoExecutionException; 5 | import org.apache.maven.plugin.MojoFailureException; 6 | import org.apache.maven.plugins.annotations.Mojo; 7 | 8 | @Mojo(name = "classloading") 9 | public class ClassloadingMojo extends AbstractMojo { 10 | 11 | @Override 12 | public void execute() throws MojoExecutionException, MojoFailureException { 13 | if (isEclipseAether()) { 14 | System.out.println("classloader: org.eclipse.aether"); 15 | } 16 | if (isSonatypeAether()) { 17 | System.out.println("classloader: org.sonatype.aether"); 18 | } 19 | } 20 | 21 | protected static boolean isSonatypeAether() { 22 | return canFindCoreClass("org.sonatype.aether.artifact.Artifact"); 23 | } 24 | 25 | protected static boolean isEclipseAether() { 26 | return canFindCoreClass("org.eclipse.aether.artifact.Artifact"); 27 | } 28 | 29 | private static boolean canFindCoreClass(String className) { 30 | try { 31 | Thread.currentThread().getContextClassLoader().loadClass(className); 32 | return true; 33 | } catch (ClassNotFoundException e) { 34 | return false; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/classloading/src/test/java/test/dependencytree/test/ClassloadingMojoTest.java: -------------------------------------------------------------------------------- 1 | package test.dependencytree.test; 2 | 3 | import io.takari.maven.testing.TestResources; 4 | import io.takari.maven.testing.executor.MavenExecutionResult; 5 | import io.takari.maven.testing.executor.MavenRuntime; 6 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 7 | import io.takari.maven.testing.executor.MavenVersions; 8 | import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner; 9 | 10 | import org.apache.maven.artifact.versioning.DefaultArtifactVersion; 11 | import org.apache.maven.artifact.versioning.VersionRange; 12 | import org.junit.Rule; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | 16 | @RunWith(MavenJUnitTestRunner.class) 17 | @MavenVersions({"3.0.5", "3.2.5", "3.2.6-SNAPSHOT"}) 18 | public class ClassloadingMojoTest { 19 | 20 | @Rule 21 | public final TestResources resources = new TestResources(); 22 | 23 | private final MavenRuntime maven; 24 | 25 | public ClassloadingMojoTest(MavenRuntimeBuilder builder) throws Exception { 26 | this.maven = builder.withCliOptions("-B", "-U").build(); 27 | // a workaround 28 | // this.maven = builder.forkedBuilder().withCliOptions("-B", "-U").build(); 29 | } 30 | 31 | @Test 32 | public void testClassloading() throws Exception { 33 | MavenExecutionResult result = 34 | maven.forProject(resources.getBasedir("basic")).execute("validate"); 35 | 36 | DefaultArtifactVersion mavenVersion = new DefaultArtifactVersion(maven.getMavenVersion()); 37 | 38 | VersionRange MAVEN30 = VersionRange.createFromVersionSpec("[3.0,3.1)"); 39 | VersionRange MAVEN31 = VersionRange.createFromVersionSpec("[3.1,)"); 40 | 41 | result.assertErrorFreeLog(); 42 | 43 | if (MAVEN30.containsVersion(mavenVersion)) { 44 | result.assertLogText("classloader: org.sonatype.aether"); 45 | result.assertNoLogText("classloader: org.eclipse.aether"); 46 | } else if (MAVEN31.containsVersion(mavenVersion)) { 47 | result.assertLogText("classloader: org.eclipse.aether"); 48 | result.assertNoLogText("classloader: org.sonatype.aether"); 49 | } else { 50 | throw new AssertionError("Unsupported maven version: " + mavenVersion); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/classloading/src/test/projects/basic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 14 | 4.0.0 15 | 16 | takari-plugin-testing-its 17 | basic-basic 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | 23 | takari-plugin-testing-its 24 | classloading 25 | 1.0-SNAPSHOT 26 | 27 | 28 | classloading 29 | validate 30 | 31 | classloading 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/guicescopes/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 13 | 4.0.0 14 | 15 | takari-plugin-testing-its 16 | guicescopes 17 | 1.0-SNAPSHOT 18 | takari-maven-plugin 19 | 20 | 21 | 2.3.0 22 | 3.6.3 23 | 3.6.0 24 | 2.5.0-SNAPSHOT 25 | 26 | 27 | 28 | 29 | org.apache.maven 30 | maven-core 31 | ${mavenVersion} 32 | provided 33 | 34 | 35 | org.apache.maven 36 | maven-plugin-api 37 | ${mavenVersion} 38 | provided 39 | 40 | 41 | org.apache.maven.plugin-tools 42 | maven-plugin-annotations 43 | ${mavenPluginPluginVersion} 44 | provided 45 | 46 | 47 | 48 | 49 | junit 50 | junit 51 | 4.13.2 52 | test 53 | 54 | 55 | org.apache.maven 56 | maven-compat 57 | ${mavenVersion} 58 | test 59 | 60 | 61 | io.takari.maven.plugins 62 | takari-plugin-testing 63 | ${it-project.version} 64 | test 65 | 66 | 67 | 68 | 69 | 70 | 71 | io.takari.maven.plugins 72 | takari-lifecycle-plugin 73 | ${takariLifecycleVersion} 74 | true 75 | 76 | none 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/guicescopes/src/main/java/guicescopes/GuiceScopesMojo.java: -------------------------------------------------------------------------------- 1 | package guicescopes; 2 | 3 | import org.apache.maven.plugin.AbstractMojo; 4 | import org.apache.maven.plugin.MojoExecutionException; 5 | import org.apache.maven.plugin.MojoFailureException; 6 | import org.apache.maven.plugins.annotations.Component; 7 | import org.apache.maven.plugins.annotations.Mojo; 8 | 9 | @Mojo(name = "guicescopes") 10 | public class GuiceScopesMojo extends AbstractMojo { 11 | 12 | @Component 13 | private MojoScopedComponent component; 14 | 15 | @Override 16 | public void execute() throws MojoExecutionException, MojoFailureException { 17 | if (component == null) { 18 | throw new NullPointerException(); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/guicescopes/src/main/java/guicescopes/MojoScopedComponent.java: -------------------------------------------------------------------------------- 1 | package guicescopes; 2 | 3 | import javax.inject.Inject; 4 | import javax.inject.Named; 5 | 6 | import org.apache.maven.execution.scope.MojoExecutionScoped; 7 | import org.apache.maven.plugin.MojoExecution; 8 | 9 | @Named 10 | @MojoExecutionScoped 11 | public class MojoScopedComponent { 12 | 13 | @Inject 14 | public MojoScopedComponent(MojoExecution execution) {} 15 | 16 | } 17 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/guicescopes/src/test/java/guicescopes/GuiceScopesTest.java: -------------------------------------------------------------------------------- 1 | package guicescopes; 2 | 3 | import io.takari.maven.testing.TestMavenRuntime; 4 | import io.takari.maven.testing.TestResources; 5 | 6 | import java.io.File; 7 | 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | 11 | public class GuiceScopesTest { 12 | 13 | @Rule 14 | public final TestResources resources = new TestResources(); 15 | 16 | @Rule 17 | public final TestMavenRuntime maven = new TestMavenRuntime(); 18 | 19 | @Test 20 | public void test() throws Exception { 21 | File basedir = resources.getBasedir(); 22 | // blows up if @MojoExecutionScoped component can't be injected 23 | maven.executeMojo(basedir, "guicescopes"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/pomconfig/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 13 | 4.0.0 14 | 15 | takari-plugin-testing-its 16 | pomconfig 17 | 1.0-SNAPSHOT 18 | takari-maven-plugin 19 | 20 | 21 | 2.3.0 22 | 3.6.3 23 | 3.6.0 24 | 2.6.0-SNAPSHOT 25 | 26 | 27 | 28 | 29 | org.apache.maven 30 | maven-plugin-api 31 | ${mavenVersion} 32 | provided 33 | 34 | 35 | org.apache.maven.plugin-tools 36 | maven-plugin-annotations 37 | ${mavenPluginPluginVersion} 38 | provided 39 | 40 | 41 | 42 | 43 | junit 44 | junit 45 | 4.13.2 46 | test 47 | 48 | 49 | org.apache.maven 50 | maven-core 51 | ${mavenVersion} 52 | test 53 | 54 | 55 | org.apache.maven 56 | maven-compat 57 | ${mavenVersion} 58 | test 59 | 60 | 61 | io.takari.maven.plugins 62 | takari-plugin-testing 63 | ${it-project.version} 64 | test 65 | 66 | 67 | 68 | 69 | 70 | 71 | io.takari.maven.plugins 72 | takari-lifecycle-plugin 73 | ${takariLifecycleVersion} 74 | true 75 | 76 | none 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/pomconfig/src/main/java/pomconfig/PomConfigMojo.java: -------------------------------------------------------------------------------- 1 | package pomconfig; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.OutputStreamWriter; 7 | import java.io.Writer; 8 | 9 | import org.apache.maven.plugin.AbstractMojo; 10 | import org.apache.maven.plugin.MojoExecutionException; 11 | import org.apache.maven.plugin.MojoFailureException; 12 | import org.apache.maven.plugins.annotations.Mojo; 13 | import org.apache.maven.plugins.annotations.Parameter; 14 | 15 | @Mojo(name = "pomconfig") 16 | public class PomConfigMojo extends AbstractMojo { 17 | 18 | @Parameter(defaultValue = "${project.build.directory}/output.txt") 19 | private File output; 20 | 21 | @Parameter(property = "text", defaultValue = "text") 22 | private String text; 23 | 24 | @Override 25 | public void execute() throws MojoExecutionException, MojoFailureException { 26 | output.getParentFile().mkdirs(); 27 | try (Writer w = new OutputStreamWriter(new FileOutputStream(output), "UTF-8")) { 28 | w.write("text=" + text); 29 | } catch (IOException e) { 30 | throw new MojoExecutionException("Could not create output file", e); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/pomconfig/src/test/java/pomconfig/PomConfigTest.java: -------------------------------------------------------------------------------- 1 | package pomconfig; 2 | 3 | import io.takari.maven.testing.TestMavenRuntime; 4 | import io.takari.maven.testing.TestResources; 5 | 6 | import java.io.File; 7 | 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | 11 | public class PomConfigTest { 12 | 13 | @Rule 14 | public final TestResources resources = new TestResources(); 15 | 16 | @Rule 17 | public final TestMavenRuntime maven = new TestMavenRuntime(); 18 | 19 | @Test 20 | public void testPomConfig() throws Exception { 21 | File basedir = resources.getBasedir("basic"); 22 | maven.executeMojo(basedir, "pomconfig"); 23 | TestResources.assertFileContents(basedir, "output.txt-expected", "target/output.txt"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/pomconfig/src/test/projects/basic/output.txt-expected: -------------------------------------------------------------------------------- 1 | text=pom-text -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/pomconfig/src/test/projects/basic/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 14 | 4.0.0 15 | 16 | takari-plugin-testing-its 17 | pomconfig-basic 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | 23 | takari-plugin-testing-its 24 | pomconfig 25 | 1.0-SNAPSHOT 26 | 27 | 28 | pomconfig 29 | validate 30 | 31 | pomconfig 32 | 33 | 34 | 35 | 36 | pom-text 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/settings/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 13 | 4.0.0 14 | 15 | takari-plugin-testing-its 16 | settings 17 | 1.0-SNAPSHOT 18 | takari-maven-plugin 19 | 20 | 21 | 2.3.0 22 | 3.6.3 23 | 3.6.0 24 | 2.8.0-SNAPSHOT 25 | 26 | 27 | 28 | 29 | org.apache.maven 30 | maven-plugin-api 31 | ${mavenVersion} 32 | provided 33 | 34 | 35 | org.apache.maven.plugin-tools 36 | maven-plugin-annotations 37 | ${mavenPluginPluginVersion} 38 | provided 39 | 40 | 41 | 42 | 43 | junit 44 | junit 45 | 4.13.2 46 | test 47 | 48 | 49 | org.apache.maven 50 | maven-core 51 | ${mavenVersion} 52 | test 53 | 54 | 55 | org.apache.maven 56 | maven-compat 57 | ${mavenVersion} 58 | test 59 | 60 | 61 | io.takari.maven.plugins 62 | takari-plugin-testing 63 | ${it-project.version} 64 | test 65 | 66 | 67 | io.takari.maven.plugins 68 | takari-plugin-integration-testing 69 | ${it-project.version} 70 | pom 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | io.takari.maven.plugins 79 | takari-lifecycle-plugin 80 | ${takariLifecycleVersion} 81 | true 82 | 83 | none 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-dependency-plugin 90 | 2.10 91 | 92 | 93 | properties 94 | initialize 95 | 96 | properties 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | org.eclipse.m2e 107 | lifecycle-mapping 108 | 1.0.0 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-dependency-plugin 116 | [2.10,) 117 | 118 | properties 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/settings/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test 5 | 6 | 7 | central 8 | https://test/test/ 9 | 10 | 11 | 12 | 13 | 14 | test 15 | 16 | 17 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/settings/src/test/java/settings/SettingsTest.java: -------------------------------------------------------------------------------- 1 | package settings; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | 6 | import org.apache.maven.artifact.repository.ArtifactRepository; 7 | import org.apache.maven.project.MavenProject; 8 | import org.junit.Assert; 9 | import org.junit.Rule; 10 | import org.junit.Test; 11 | 12 | import io.takari.maven.testing.TestMavenRuntime; 13 | import io.takari.maven.testing.TestResources; 14 | 15 | public class SettingsTest { 16 | @Rule 17 | public final TestResources resources = new TestResources(); 18 | 19 | @Rule 20 | public final TestMavenRuntime maven = new TestMavenRuntime(); 21 | 22 | @Test 23 | public void testSettings() throws Exception { 24 | File basedir = resources.getBasedir(); 25 | MavenProject project = maven.readMavenProject(basedir); 26 | 27 | List repositories = project.getRemoteArtifactRepositories(); 28 | Assert.assertEquals(1, repositories.size()); 29 | 30 | ArtifactRepository repository = repositories.get(0); 31 | Assert.assertEquals("central", repository.getId()); 32 | Assert.assertEquals("https://test/test/", repository.getUrl()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/projects/settings/src/test/test.properties: -------------------------------------------------------------------------------- 1 | userSettingsFile=${project.basedir}/settings.xml 2 | -------------------------------------------------------------------------------- /takari-plugin-testing-its/src/test/test.properties: -------------------------------------------------------------------------------- 1 | repository.0=https://repo.maven.apache.org/maven2/ 2 | -------------------------------------------------------------------------------- /takari-plugin-testing/.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true 3 | formatter_profile=_TakariGoogleStyle 4 | formatter_settings_version=13 5 | org.eclipse.jdt.ui.ignorelowercasenames=true 6 | org.eclipse.jdt.ui.importorder=java;javax;org;com; 7 | org.eclipse.jdt.ui.ondemandthreshold=99 8 | org.eclipse.jdt.ui.staticondemandthreshold=99 9 | sp_cleanup.add_default_serial_version_id=true 10 | sp_cleanup.add_generated_serial_version_id=false 11 | sp_cleanup.add_missing_annotations=true 12 | sp_cleanup.add_missing_deprecated_annotations=true 13 | sp_cleanup.add_missing_methods=false 14 | sp_cleanup.add_missing_nls_tags=false 15 | sp_cleanup.add_missing_override_annotations=true 16 | sp_cleanup.add_missing_override_annotations_interface_methods=true 17 | sp_cleanup.add_serial_version_id=false 18 | sp_cleanup.always_use_blocks=true 19 | sp_cleanup.always_use_parentheses_in_expressions=false 20 | sp_cleanup.always_use_this_for_non_static_field_access=false 21 | sp_cleanup.always_use_this_for_non_static_method_access=false 22 | sp_cleanup.convert_to_enhanced_for_loop=false 23 | sp_cleanup.correct_indentation=false 24 | sp_cleanup.format_source_code=true 25 | sp_cleanup.format_source_code_changes_only=false 26 | sp_cleanup.make_local_variable_final=false 27 | sp_cleanup.make_parameters_final=false 28 | sp_cleanup.make_private_fields_final=true 29 | sp_cleanup.make_type_abstract_if_missing_method=false 30 | sp_cleanup.make_variable_declarations_final=true 31 | sp_cleanup.never_use_blocks=false 32 | sp_cleanup.never_use_parentheses_in_expressions=true 33 | sp_cleanup.on_save_use_additional_actions=false 34 | sp_cleanup.organize_imports=true 35 | sp_cleanup.qualify_static_field_accesses_with_declaring_class=false 36 | sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true 37 | sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true 38 | sp_cleanup.qualify_static_member_accesses_with_declaring_class=false 39 | sp_cleanup.qualify_static_method_accesses_with_declaring_class=false 40 | sp_cleanup.remove_private_constructors=true 41 | sp_cleanup.remove_trailing_whitespaces=false 42 | sp_cleanup.remove_trailing_whitespaces_all=true 43 | sp_cleanup.remove_trailing_whitespaces_ignore_empty=false 44 | sp_cleanup.remove_unnecessary_casts=true 45 | sp_cleanup.remove_unnecessary_nls_tags=false 46 | sp_cleanup.remove_unused_imports=false 47 | sp_cleanup.remove_unused_local_variables=false 48 | sp_cleanup.remove_unused_private_fields=true 49 | sp_cleanup.remove_unused_private_members=false 50 | sp_cleanup.remove_unused_private_methods=true 51 | sp_cleanup.remove_unused_private_types=true 52 | sp_cleanup.sort_members=false 53 | sp_cleanup.sort_members_all=false 54 | sp_cleanup.use_blocks=false 55 | sp_cleanup.use_blocks_only_for_return_and_throw=false 56 | sp_cleanup.use_parentheses_in_expressions=false 57 | sp_cleanup.use_this_for_non_static_field_access=false 58 | sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true 59 | sp_cleanup.use_this_for_non_static_method_access=false 60 | sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true 61 | -------------------------------------------------------------------------------- /takari-plugin-testing/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 4.0.0 13 | 14 | 15 | io.takari.maven.plugins 16 | takari-plugin-testing-project 17 | 3.1.2-SNAPSHOT 18 | 19 | 20 | takari-plugin-testing 21 | takari-jar 22 | 23 | ${project.groupId}:${project.artifactId} 24 | 25 | 26 | 27 | junit 28 | junit 29 | 30 | 31 | org.junit.jupiter 32 | junit-jupiter-api 33 | provided 34 | 35 | 36 | org.codehaus.plexus 37 | plexus-utils 38 | 39 | 40 | org.slf4j 41 | slf4j-api 42 | 43 | 44 | org.slf4j 45 | slf4j-simple 46 | runtime 47 | 48 | 49 | com.google.inject 50 | guice 51 | classes 52 | 53 | 54 | javax.inject 55 | javax.inject 56 | 57 | 58 | org.eclipse.sisu 59 | org.eclipse.sisu.inject 60 | 61 | 62 | org.eclipse.sisu 63 | org.eclipse.sisu.plexus 64 | 65 | 66 | 67 | 68 | org.apache.maven 69 | maven-core 70 | provided 71 | 72 | 73 | org.apache.maven 74 | maven-plugin-api 75 | provided 76 | 77 | 78 | org.apache.maven 79 | maven-model 80 | provided 81 | 82 | 83 | org.apache.maven 84 | maven-resolver-provider 85 | provided 86 | 87 | 88 | org.apache.maven 89 | maven-artifact 90 | provided 91 | 92 | 93 | org.apache.maven 94 | maven-settings 95 | provided 96 | 97 | 98 | org.apache.maven 99 | maven-settings-builder 100 | provided 101 | 102 | 103 | org.apache.maven.resolver 104 | maven-resolver-api 105 | provided 106 | 107 | 108 | 109 | 110 | org.apache.commons 111 | commons-exec 112 | provided 113 | 114 | 115 | org.codehaus.plexus 116 | plexus-classworlds 117 | provided 118 | 119 | 120 | io.takari.m2e.workspace 121 | org.eclipse.m2e.workspace.cli 122 | provided 123 | 124 | 125 | 126 | 127 | org.apache.commons 128 | commons-compress 129 | provided 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/AbstractTestMavenRuntime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.Module; 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import java.util.LinkedHashMap; 17 | import java.util.LinkedHashSet; 18 | import java.util.Map; 19 | import java.util.Objects; 20 | import java.util.Properties; 21 | import java.util.Set; 22 | import org.apache.maven.artifact.Artifact; 23 | import org.apache.maven.artifact.DefaultArtifact; 24 | import org.apache.maven.artifact.handler.ArtifactHandler; 25 | import org.apache.maven.artifact.versioning.DefaultArtifactVersion; 26 | import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; 27 | import org.apache.maven.artifact.versioning.VersionRange; 28 | import org.apache.maven.execution.MavenSession; 29 | import org.apache.maven.plugin.Mojo; 30 | import org.apache.maven.plugin.MojoExecution; 31 | import org.apache.maven.project.MavenProject; 32 | import org.codehaus.plexus.DefaultPlexusContainer; 33 | import org.codehaus.plexus.util.xml.Xpp3Dom; 34 | 35 | public abstract class AbstractTestMavenRuntime { 36 | 37 | private static final DefaultArtifactVersion MAVEN_VERSION; 38 | 39 | static { 40 | DefaultArtifactVersion version = null; 41 | String path = "/META-INF/maven/org.apache.maven/maven-core/pom.properties"; 42 | try (InputStream is = AbstractTestMavenRuntime.class.getResourceAsStream(path)) { 43 | Properties properties = new Properties(); 44 | if (is != null) { 45 | properties.load(is); 46 | } 47 | String property = properties.getProperty("version"); 48 | if (property != null) { 49 | version = new DefaultArtifactVersion(property); 50 | } 51 | } catch (IOException e) { 52 | // odd, where did this come from 53 | } 54 | MAVEN_VERSION = version; 55 | } 56 | 57 | @FunctionalInterface 58 | private static interface RuntimeFactory { 59 | MavenRuntime newInstance(Module[] modules) throws Exception; 60 | } 61 | 62 | // ordered map of supported maven runtime factories 63 | private static final Map FACTORIES; 64 | 65 | static { 66 | Map factories = new LinkedHashMap<>(); 67 | try { 68 | factories.put(VersionRange.createFromVersionSpec("[3.0,3.1.1)"), Maven30xRuntime::new); 69 | factories.put(VersionRange.createFromVersionSpec("[3.1.1,3.2.1)"), Maven311Runtime::new); 70 | factories.put(VersionRange.createFromVersionSpec("[3.2.1,3.2.5)"), Maven321Runtime::create); 71 | factories.put(VersionRange.createFromVersionSpec("[3.2.5]"), Maven325Runtime::new); 72 | // the last entry is expected to handle everything else 73 | factories.put(VersionRange.createFromVersionSpec("(3.2.5,]"), Maven331Runtime::new); 74 | } catch (InvalidVersionSpecificationException e) { 75 | throw new RuntimeException(e); 76 | } 77 | FACTORIES = Collections.unmodifiableMap(factories); 78 | } 79 | 80 | private final Module[] modules; 81 | private MavenRuntime runtime; 82 | 83 | public class TestDependency { 84 | 85 | private final File file; 86 | private String groupId = "test"; 87 | private String artifactId; 88 | private String classifier; 89 | private String version = "1.0"; 90 | private String type = "jar"; 91 | private String scope = Artifact.SCOPE_COMPILE; 92 | private boolean optional; 93 | 94 | private TestDependency(File artifact) { 95 | this.file = artifact; 96 | this.artifactId = artifact.getName(); 97 | } 98 | 99 | public TestDependency setArtifactId(String artifactId) { 100 | this.artifactId = artifactId; 101 | 102 | return this; 103 | } 104 | 105 | public TestDependency setGroupId(String groupId) { 106 | this.groupId = groupId; 107 | 108 | return this; 109 | } 110 | 111 | public TestDependency setVersion(String version) { 112 | this.version = version; 113 | 114 | return this; 115 | } 116 | 117 | public TestDependency setType(String type) { 118 | this.type = type; 119 | 120 | return this; 121 | } 122 | 123 | public TestDependency setOptional(boolean optional) { 124 | this.optional = optional; 125 | 126 | return this; 127 | } 128 | 129 | public TestDependency setClassifier(String classifier) { 130 | this.classifier = classifier; 131 | 132 | return this; 133 | } 134 | 135 | public TestDependency setScope(String scope) { 136 | this.scope = scope; 137 | 138 | return this; 139 | } 140 | 141 | public TestDependency addTo(MavenProject project) throws Exception { 142 | return addTo(project, true); 143 | } 144 | 145 | public TestDependency addTo(MavenProject project, boolean direct) throws Exception { 146 | ArtifactHandler handler = getContainer().lookup(ArtifactHandler.class, type); 147 | DefaultArtifact artifact = 148 | new DefaultArtifact(groupId, artifactId, version, scope, type, classifier, handler); 149 | artifact.setFile(file); 150 | artifact.setOptional(optional); 151 | Set artifacts = project.getArtifacts(); 152 | artifacts.add(artifact); 153 | project.setArtifacts(artifacts); 154 | if (direct) { 155 | Set directDependencies = project.getDependencyArtifacts(); 156 | directDependencies = directDependencies == null 157 | ? new LinkedHashSet() 158 | : new LinkedHashSet<>(directDependencies); 159 | directDependencies.add(artifact); 160 | project.setDependencyArtifacts(directDependencies); 161 | } 162 | 163 | return this; 164 | } 165 | } 166 | 167 | AbstractTestMavenRuntime() { 168 | this(new Module[0]); 169 | } 170 | 171 | AbstractTestMavenRuntime(Module... modules) { 172 | this.modules = modules; 173 | } 174 | 175 | void createMavenRuntime() throws Exception { 176 | runtime = newMavenRuntime(modules); 177 | } 178 | 179 | void shutDownMavenRuntime() { 180 | runtime.shutdown(); 181 | runtime = null; 182 | } 183 | 184 | private MavenRuntime newMavenRuntime(Module[] modules) throws Exception { 185 | for (Map.Entry entry : FACTORIES.entrySet()) { 186 | if (entry.getKey().containsVersion(MAVEN_VERSION)) { 187 | return entry.getValue().newInstance(modules); 188 | } 189 | } 190 | throw new AssertionError(String.format( 191 | "Maven version %s is not supported, supprted versions: %s", MAVEN_VERSION, FACTORIES.entrySet())); 192 | } 193 | 194 | public MavenProject readMavenProject(File basedir) throws Exception { 195 | MavenProject project = runtime.readMavenProject(basedir); 196 | Objects.requireNonNull(project); 197 | return project; 198 | } 199 | 200 | public MavenSession newMavenSession(MavenProject project) throws Exception { 201 | MavenSession session = runtime.newMavenSession(project.getBasedir()); 202 | session.setCurrentProject(project); 203 | session.setProjects(Arrays.asList(project)); 204 | return session; 205 | } 206 | 207 | /** @since 2.9 */ 208 | public MojoExecution newMojoExecution(String goal, Xpp3Dom... parameters) { 209 | MojoExecution execution = runtime.newMojoExecution(goal); 210 | if (parameters != null) { 211 | // TODO decide if this should go to runtime.newMojoExecution 212 | Xpp3Dom configuration = execution.getConfiguration(); 213 | for (Xpp3Dom parameter : parameters) { 214 | configuration.addChild(parameter); 215 | } 216 | } 217 | return execution; 218 | } 219 | 220 | public void executeMojo(File basedir, String goal, Xpp3Dom... parameters) throws Exception { 221 | MavenProject project = readMavenProject(basedir); 222 | MavenSession session = newMavenSession(project); 223 | executeMojo(session, project, goal, parameters); 224 | } 225 | 226 | public void executeMojo(MavenSession session, MavenProject project, String goal, Xpp3Dom... parameters) 227 | throws Exception { 228 | MojoExecution execution = newMojoExecution(goal, parameters); 229 | executeMojo(session, project, execution); 230 | } 231 | 232 | public void executeMojo(MavenProject project, String goal, Xpp3Dom... parameters) throws Exception { 233 | MavenSession session = newMavenSession(project); 234 | executeMojo(session, project, goal, parameters); 235 | } 236 | 237 | public void executeMojo(MavenSession session, MavenProject project, MojoExecution execution) throws Exception { 238 | runtime.executeMojo(session, project, execution); 239 | } 240 | 241 | public Mojo lookupConfiguredMojo(MavenSession session, MojoExecution execution) throws Exception { 242 | return runtime.lookupConfiguredMojo(session, execution); 243 | } 244 | 245 | public DefaultPlexusContainer getContainer() { 246 | return runtime.getContainer(); 247 | } 248 | 249 | public T lookup(Class role) throws Exception { 250 | return runtime.lookup(role); 251 | } 252 | 253 | public static Xpp3Dom newParameter(String name, String value) { 254 | Xpp3Dom child = new Xpp3Dom(name); 255 | child.setValue(value); 256 | return child; 257 | } 258 | 259 | public TestDependency newDependency(File artifact) { 260 | return new TestDependency(artifact); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/AbstractTestResources.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.InputStreamReader; 16 | import java.util.Collection; 17 | import java.util.Collections; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.Properties; 21 | import java.util.Set; 22 | import java.util.TreeSet; 23 | import org.codehaus.plexus.util.DirectoryScanner; 24 | import org.codehaus.plexus.util.FileUtils; 25 | import org.junit.Assert; 26 | 27 | /** 28 | * Abstract base class to extract and assert test resources. 29 | */ 30 | public abstract class AbstractTestResources { 31 | 32 | private final String projectsDir; 33 | 34 | private final String workDir; 35 | 36 | private String name; 37 | 38 | AbstractTestResources() { 39 | this("src/test/projects", "target/test-projects"); 40 | } 41 | 42 | AbstractTestResources(String projectsDir, String workDir) { 43 | this.projectsDir = projectsDir; 44 | this.workDir = workDir; 45 | } 46 | 47 | void starting(Class testClass, String methodName) { 48 | if (methodName != null) { 49 | methodName = methodName.replace('/', '_').replace('\\', '_'); 50 | } 51 | name = testClass.getSimpleName() + "_" + methodName; 52 | } 53 | 54 | abstract String getRequiredAnnotationClassName(); 55 | 56 | /** 57 | * Creates new clean copy of test project directory structure. The copy is named after both the test being executed and test project name, which allows the same test project can be used by multiple 58 | * tests and by different instances of the same parametrized tests.
59 | * TODO Provide alternative working directory naming for Windows, which still limits path names to ~250 charecters 60 | */ 61 | public File getBasedir(String project) throws IOException { 62 | if (name == null) { 63 | throw new IllegalStateException(getClass().getSimpleName() + " must be a test class field annotated with " 64 | + getRequiredAnnotationClassName()); 65 | } 66 | File basedir = new File(workDir, name + "_" + project).getCanonicalFile(); 67 | FileUtils.deleteDirectory(basedir); 68 | Assert.assertTrue("Test project working directory created", basedir.mkdirs()); 69 | File src = new File(projectsDir, project).getCanonicalFile(); 70 | Assert.assertTrue("Test project directory does not exist: " + src.getPath(), src.isDirectory()); 71 | FileUtils.copyDirectoryStructure(src, basedir); 72 | return basedir; 73 | } 74 | 75 | /** 76 | * Creates new clean test work directory. The directory is named after test being executed. 77 | * 78 | * @since 2.2 79 | */ 80 | public File getBasedir() throws IOException { 81 | if (name == null) { 82 | throw new IllegalStateException(getClass().getSimpleName() + " must be a test class field annotated with " 83 | + getRequiredAnnotationClassName()); 84 | } 85 | File basedir = new File(workDir, name).getCanonicalFile(); 86 | FileUtils.deleteDirectory(basedir); 87 | Assert.assertTrue("Test project working directory created", basedir.mkdirs()); 88 | return basedir; 89 | } 90 | 91 | // static helpers 92 | 93 | public static void cp(File basedir, String from, String to) throws IOException { 94 | // TODO ensure destination lastModified timestamp changes 95 | FileUtils.copyFile(new File(basedir, from), new File(basedir, to)); 96 | } 97 | 98 | public static void assertFileContents(File basedir, String expectedPath, String actualPath) throws IOException { 99 | String expected = fileRead(new File(basedir, expectedPath), true); 100 | String actual = fileRead(new File(basedir, actualPath), true); 101 | Assert.assertEquals(expected, actual); 102 | } 103 | 104 | private static String fileRead(File file, boolean normalizeEOL) throws IOException { 105 | StringBuilder sb = new StringBuilder(); 106 | try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) { 107 | if (normalizeEOL) { 108 | String str; 109 | while ((str = r.readLine()) != null) { 110 | sb.append(str).append('\n'); 111 | } 112 | } else { 113 | int ch; 114 | while ((ch = r.read()) != -1) { 115 | sb.append((char) ch); 116 | } 117 | } 118 | } 119 | return sb.toString(); 120 | } 121 | 122 | public static void assertFileContents(String expectedContents, File basedir, String path) throws IOException { 123 | String actualContents = fileRead(new File(basedir, path), true); 124 | Assert.assertEquals(expectedContents, actualContents); 125 | } 126 | 127 | public static void assertDirectoryContents(File dir, String... expectedPaths) { 128 | DirectoryScanner scanner = new DirectoryScanner(); 129 | scanner.setBasedir(dir); 130 | scanner.addDefaultExcludes(); 131 | scanner.scan(); 132 | 133 | Set actual = new TreeSet(); 134 | for (String path : scanner.getIncludedFiles()) { 135 | actual.add(path.replace(File.separatorChar, '/')); 136 | } 137 | for (String path : scanner.getIncludedDirectories()) { 138 | if (path.length() > 0) { 139 | actual.add(path.replace(File.separatorChar, '/') + "/"); 140 | } 141 | } 142 | 143 | Set expected = new TreeSet(); 144 | if (expectedPaths != null) { 145 | for (String path : expectedPaths) { 146 | expected.add(path.replace(File.separatorChar, '/')); 147 | } 148 | } 149 | 150 | // compare textual representation to make diff easier to understand 151 | Assert.assertEquals(toString(expected), toString(actual)); 152 | } 153 | 154 | private static String toString(Collection strings) { 155 | StringBuilder sb = new StringBuilder(); 156 | for (String string : strings) { 157 | sb.append(string).append('\n'); 158 | } 159 | return sb.toString(); 160 | } 161 | 162 | public static void touch(File basedir, String path) throws InterruptedException { 163 | touch(new File(basedir, path)); 164 | } 165 | 166 | public static void touch(File file) throws InterruptedException { 167 | if (!file.isFile()) { 168 | throw new IllegalArgumentException("Not a file " + file); 169 | } 170 | long lastModified = file.lastModified(); 171 | file.setLastModified(System.currentTimeMillis()); 172 | 173 | // TODO do modern filesystems still have this silly lastModified resolution? 174 | if (lastModified == file.lastModified()) { 175 | Thread.sleep(1000L); 176 | file.setLastModified(System.currentTimeMillis()); 177 | } 178 | } 179 | 180 | public static void rm(File basedir, String path) { 181 | Assert.assertTrue("delete " + path, new File(basedir, path).delete()); 182 | } 183 | 184 | public static void create(File basedir, String... paths) throws IOException { 185 | if (paths == null || paths.length == 0) { 186 | throw new IllegalArgumentException(); 187 | } 188 | for (String path : paths) { 189 | File file = new File(basedir, path); 190 | file.getParentFile().mkdirs(); 191 | Assert.assertTrue(file.getParentFile().isDirectory()); 192 | file.createNewFile(); 193 | Assert.assertTrue(file.isFile() && file.canRead()); 194 | } 195 | } 196 | 197 | public static void assertFilesPresent(File basedir, String... paths) { 198 | if (basedir == null || paths == null || paths.length <= 0) { 199 | throw new IllegalArgumentException(); 200 | } 201 | if (paths.length == 1) { 202 | Assert.assertTrue(paths[0] + " PRESENT", new File(basedir, paths[0]).isFile()); 203 | } else { 204 | StringBuilder expected = new StringBuilder(); 205 | StringBuilder actual = new StringBuilder(); 206 | for (String path : paths) { 207 | expected.append(path).append("\n"); 208 | if (!new File(basedir, path).isFile()) { 209 | actual.append("NOT PRESENT "); 210 | } 211 | actual.append(path).append("\n"); 212 | } 213 | Assert.assertEquals(expected.toString(), actual.toString()); 214 | } 215 | } 216 | 217 | public static void assertFilesNotPresent(File basedir, String... paths) { 218 | if (basedir == null || paths == null || paths.length <= 0) { 219 | throw new IllegalArgumentException(); 220 | } 221 | if (paths.length == 1) { 222 | Assert.assertFalse(paths[0] + " NOT PRESENT", new File(basedir, paths[0]).isFile()); 223 | } else { 224 | StringBuilder expected = new StringBuilder(); 225 | StringBuilder actual = new StringBuilder(); 226 | for (String path : paths) { 227 | expected.append(path).append("\n"); 228 | if (new File(basedir, path).isFile()) { 229 | actual.append("PRESENT "); 230 | } 231 | actual.append(path).append("\n"); 232 | } 233 | Assert.assertEquals(expected.toString(), actual.toString()); 234 | } 235 | } 236 | 237 | /** 238 | * @since 2.2 239 | */ 240 | public static Map readProperties(File basedir, String path) throws IOException { 241 | Properties properties = new Properties(); 242 | try (InputStream is = new FileInputStream(new File(basedir, path))) { 243 | properties.load(is); 244 | } 245 | Map result = new HashMap<>(); 246 | for (String key : properties.stringPropertyNames()) { 247 | result.put(key, properties.getProperty(key)); 248 | } 249 | return Collections.unmodifiableMap(result); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/Maven311Runtime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.Module; 11 | import java.io.File; 12 | import org.apache.maven.DefaultMaven; 13 | import org.apache.maven.Maven; 14 | import org.apache.maven.execution.DefaultMavenExecutionResult; 15 | import org.apache.maven.execution.MavenExecutionRequest; 16 | import org.apache.maven.execution.MavenExecutionResult; 17 | import org.apache.maven.execution.MavenSession; 18 | import org.apache.maven.project.MavenProject; 19 | import org.apache.maven.project.ProjectBuilder; 20 | import org.apache.maven.project.ProjectBuildingRequest; 21 | import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 22 | import org.eclipse.aether.RepositorySystemSession; 23 | 24 | class Maven311Runtime extends Maven30xRuntime { 25 | 26 | public Maven311Runtime(Module[] modules) throws Exception { 27 | super(modules); 28 | } 29 | 30 | @Override 31 | public MavenProject readMavenProject(File basedir) throws Exception { 32 | File pom = new File(basedir, "pom.xml"); 33 | MavenExecutionRequest request = newExecutionRequest(); 34 | request.setBaseDirectory(basedir); 35 | ProjectBuildingRequest configuration = getProjectBuildingRequest(request); 36 | return container 37 | .lookup(ProjectBuilder.class) 38 | .build(getPomFile(pom), configuration) 39 | .getProject(); 40 | } 41 | 42 | @Override 43 | protected ProjectBuildingRequest getProjectBuildingRequest(MavenExecutionRequest request) 44 | throws ComponentLookupException { 45 | ProjectBuildingRequest configuration = request.getProjectBuildingRequest(); 46 | configuration.setRepositorySession(newRepositorySession(request)); 47 | return configuration; 48 | } 49 | 50 | @SuppressWarnings("deprecation") 51 | @Override 52 | public MavenSession newMavenSession(File basedir) throws Exception { 53 | MavenExecutionRequest request = newExecutionRequest(); 54 | RepositorySystemSession repositorySession = newRepositorySession(request); 55 | 56 | MavenExecutionResult result = new DefaultMavenExecutionResult(); 57 | return new MavenSession(container, repositorySession, request, result); 58 | } 59 | 60 | protected RepositorySystemSession newRepositorySession(MavenExecutionRequest request) 61 | throws ComponentLookupException { 62 | return ((DefaultMaven) container.lookup(Maven.class)).newRepositorySession(request); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/Maven321Runtime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.AbstractModule; 11 | import com.google.inject.Module; 12 | import org.apache.maven.execution.MavenSession; 13 | import org.apache.maven.execution.MojoExecutionEvent; 14 | import org.apache.maven.execution.MojoExecutionListener; 15 | import org.apache.maven.execution.scope.MojoExecutionScoped; 16 | import org.apache.maven.execution.scope.internal.MojoExecutionScope; 17 | import org.apache.maven.plugin.Mojo; 18 | import org.apache.maven.plugin.MojoExecution; 19 | import org.apache.maven.project.MavenProject; 20 | 21 | class Maven321Runtime extends Maven311Runtime { 22 | 23 | private static class MojoExecutionScopeModule extends AbstractModule { 24 | @Override 25 | protected void configure() { 26 | MojoExecutionScope scope = new MojoExecutionScope(); 27 | bind(MojoExecutionScope.class).toInstance(scope); 28 | 29 | bindScope(MojoExecutionScoped.class, scope); 30 | 31 | // standard scope bindings 32 | bind(MavenProject.class) 33 | .toProvider(MojoExecutionScope.seededKeyProvider()) 34 | .in(scope); 35 | bind(MojoExecution.class) 36 | .toProvider(MojoExecutionScope.seededKeyProvider()) 37 | .in(scope); 38 | } 39 | } 40 | 41 | public static Maven321Runtime create(Module[] modules) throws Exception { 42 | Module[] joined = new Module[modules.length + 1]; 43 | joined[0] = new MojoExecutionScopeModule(); 44 | System.arraycopy(modules, 0, joined, 1, modules.length); 45 | return new Maven321Runtime(joined); 46 | } 47 | 48 | protected Maven321Runtime(Module[] modules) throws Exception { 49 | super(modules); 50 | } 51 | 52 | @Override 53 | public Mojo executeMojo(MavenSession session, MavenProject project, MojoExecution execution) throws Exception { 54 | Object sessionScope = container.lookup("org.apache.maven.SessionScope"); 55 | try { 56 | enter(sessionScope); 57 | seed(sessionScope, MavenSession.class, session); 58 | 59 | MojoExecutionScope executionScope = container.lookup(MojoExecutionScope.class); 60 | try { 61 | executionScope.enter(); 62 | 63 | executionScope.seed(MavenProject.class, project); 64 | executionScope.seed(MojoExecution.class, execution); 65 | 66 | Mojo mojo = lookupConfiguredMojo(session, execution); 67 | mojo.execute(); 68 | 69 | MojoExecutionEvent event = new MojoExecutionEvent(session, project, execution, mojo); 70 | for (MojoExecutionListener listener : container.lookupList(MojoExecutionListener.class)) { 71 | listener.afterMojoExecutionSuccess(event); 72 | } 73 | 74 | return mojo; 75 | } finally { 76 | executionScope.exit(); 77 | } 78 | } finally { 79 | exit(sessionScope); 80 | } 81 | } 82 | 83 | private static void enter(Object scope) throws Exception { 84 | scope.getClass().getMethod("enter").invoke(scope); 85 | } 86 | 87 | private static void seed(Object scope, Class type, Object instance) throws Exception { 88 | scope.getClass().getMethod("seed", Class.class, Object.class).invoke(scope, type, instance); 89 | } 90 | 91 | private static void exit(Object scope) throws Exception { 92 | scope.getClass().getMethod("exit").invoke(scope); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/Maven325Runtime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.Module; 11 | import org.apache.maven.execution.MavenSession; 12 | import org.apache.maven.execution.MojoExecutionEvent; 13 | import org.apache.maven.execution.MojoExecutionListener; 14 | import org.apache.maven.execution.scope.internal.MojoExecutionScope; 15 | import org.apache.maven.plugin.Mojo; 16 | import org.apache.maven.plugin.MojoExecution; 17 | import org.apache.maven.project.MavenProject; 18 | import org.apache.maven.session.scope.internal.SessionScope; 19 | 20 | class Maven325Runtime extends Maven321Runtime { 21 | 22 | public Maven325Runtime(Module[] modules) throws Exception { 23 | super(modules); 24 | } 25 | 26 | @Override 27 | public Mojo executeMojo(MavenSession session, MavenProject project, MojoExecution execution) throws Exception { 28 | SessionScope sessionScope = container.lookup(SessionScope.class); 29 | try { 30 | sessionScope.enter(); 31 | sessionScope.seed(MavenSession.class, session); 32 | 33 | MojoExecutionScope executionScope = container.lookup(MojoExecutionScope.class); 34 | try { 35 | executionScope.enter(); 36 | 37 | executionScope.seed(MavenProject.class, project); 38 | executionScope.seed(MojoExecution.class, execution); 39 | 40 | Mojo mojo = lookupConfiguredMojo(session, execution); 41 | mojo.execute(); 42 | 43 | MojoExecutionEvent event = new MojoExecutionEvent(session, project, execution, mojo); 44 | for (MojoExecutionListener listener : container.lookupList(MojoExecutionListener.class)) { 45 | listener.afterMojoExecutionSuccess(event); 46 | } 47 | 48 | return mojo; 49 | } finally { 50 | executionScope.exit(); 51 | } 52 | } finally { 53 | sessionScope.exit(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/Maven331Runtime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.Module; 11 | import java.io.File; 12 | import org.apache.maven.execution.DefaultMavenExecutionResult; 13 | import org.apache.maven.execution.MavenExecutionRequest; 14 | import org.apache.maven.execution.MavenExecutionResult; 15 | import org.apache.maven.execution.MavenSession; 16 | import org.apache.maven.lifecycle.MojoExecutionConfigurator; 17 | import org.apache.maven.plugin.Mojo; 18 | import org.apache.maven.plugin.MojoExecution; 19 | import org.apache.maven.plugin.PluginParameterExpressionEvaluator; 20 | import org.apache.maven.plugin.descriptor.MojoDescriptor; 21 | import org.apache.maven.project.MavenProject; 22 | import org.codehaus.plexus.component.configurator.ComponentConfigurator; 23 | import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 24 | import org.codehaus.plexus.configuration.PlexusConfiguration; 25 | import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; 26 | import org.eclipse.aether.RepositorySystemSession; 27 | 28 | class Maven331Runtime extends Maven325Runtime { 29 | 30 | public Maven331Runtime(Module[] modules) throws Exception { 31 | super(modules); 32 | } 33 | 34 | @Override 35 | public Mojo lookupConfiguredMojo(MavenSession session, MojoExecution execution) throws Exception { 36 | MavenProject project = session.getCurrentProject(); 37 | MojoDescriptor mojoDescriptor = execution.getMojoDescriptor(); 38 | 39 | Mojo mojo = container.lookup(Mojo.class, mojoDescriptor.getRoleHint()); 40 | 41 | ExpressionEvaluator evaluator = new PluginParameterExpressionEvaluator(session, execution); 42 | mojoExecutionConfigurator(execution).configure(project, execution, true); 43 | finalizeMojoConfiguration(execution); 44 | PlexusConfiguration mojoConfiguration = new XmlPlexusConfiguration(execution.getConfiguration()); 45 | 46 | String configuratorHint = "basic"; 47 | if (mojoDescriptor.getComponentConfigurator() != null) { 48 | configuratorHint = mojoDescriptor.getComponentConfigurator(); 49 | } 50 | 51 | ComponentConfigurator configurator = container.lookup(ComponentConfigurator.class, configuratorHint); 52 | 53 | configurator.configureComponent(mojo, mojoConfiguration, evaluator, container.getContainerRealm()); 54 | 55 | return mojo; 56 | } 57 | 58 | private MojoExecutionConfigurator mojoExecutionConfigurator(MojoExecution mojoExecution) throws Exception { 59 | String configuratorId = mojoExecution.getMojoDescriptor().getComponentConfigurator(); 60 | if (configuratorId == null) { 61 | configuratorId = "default"; 62 | } 63 | return container.lookup(MojoExecutionConfigurator.class, configuratorId); 64 | } 65 | 66 | @SuppressWarnings("deprecation") 67 | @Override 68 | public MavenSession newMavenSession(File basedir) throws Exception { 69 | MavenExecutionRequest request = newExecutionRequest(); 70 | request.setMultiModuleProjectDirectory(basedir); 71 | RepositorySystemSession repositorySession = newRepositorySession(request); 72 | 73 | MavenExecutionResult result = new DefaultMavenExecutionResult(); 74 | return new MavenSession(container, repositorySession, request, result); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/MavenRuntime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import java.io.File; 11 | import org.apache.maven.execution.MavenSession; 12 | import org.apache.maven.plugin.Mojo; 13 | import org.apache.maven.plugin.MojoExecution; 14 | import org.apache.maven.project.MavenProject; 15 | import org.codehaus.plexus.DefaultPlexusContainer; 16 | import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 17 | 18 | interface MavenRuntime { 19 | 20 | void shutdown(); 21 | 22 | MavenProject readMavenProject(File basedir) throws Exception; 23 | 24 | MavenSession newMavenSession(File baseir) throws Exception; 25 | 26 | MojoExecution newMojoExecution(String goal); 27 | 28 | Mojo executeMojo(MavenSession session, MavenProject project, MojoExecution execution) throws Exception; 29 | 30 | Mojo lookupConfiguredMojo(MavenSession session, MojoExecution execution) throws Exception; 31 | 32 | DefaultPlexusContainer getContainer(); 33 | 34 | T lookup(Class role) throws ComponentLookupException; 35 | } 36 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/TestMavenRuntime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.Module; 11 | import org.junit.rules.TestRule; 12 | import org.junit.runner.Description; 13 | import org.junit.runners.model.Statement; 14 | 15 | public class TestMavenRuntime extends AbstractTestMavenRuntime implements TestRule { 16 | 17 | public TestMavenRuntime() { 18 | super(); 19 | } 20 | 21 | public TestMavenRuntime(Module... modules) { 22 | super(modules); 23 | } 24 | 25 | @Override 26 | public Statement apply(final Statement base, final Description description) { 27 | return new Statement() { 28 | @Override 29 | public void evaluate() throws Throwable { 30 | createMavenRuntime(); 31 | try { 32 | base.evaluate(); 33 | } finally { 34 | shutDownMavenRuntime(); 35 | } 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/TestMavenRuntime5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import com.google.inject.Module; 11 | import org.junit.jupiter.api.extension.AfterEachCallback; 12 | import org.junit.jupiter.api.extension.BeforeEachCallback; 13 | import org.junit.jupiter.api.extension.ExtensionContext; 14 | 15 | /** 16 | * Like {@link TestMavenRuntime} but for JUnit 5. 17 | * 18 | * @author Philippe Marschall 19 | */ 20 | public class TestMavenRuntime5 extends AbstractTestMavenRuntime implements BeforeEachCallback, AfterEachCallback { 21 | 22 | public TestMavenRuntime5() { 23 | super(); 24 | } 25 | 26 | public TestMavenRuntime5(Module... modules) { 27 | super(modules); 28 | } 29 | 30 | @Override 31 | public void beforeEach(ExtensionContext context) throws Exception { 32 | createMavenRuntime(); 33 | } 34 | 35 | @Override 36 | public void afterEach(ExtensionContext context) { 37 | shutDownMavenRuntime(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/TestProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.Properties; 20 | import java.util.StringTokenizer; 21 | import java.util.TreeMap; 22 | import org.junit.Assert; 23 | 24 | public class TestProperties { 25 | 26 | public static final String PROP_CLASSPATH = "classpath"; 27 | 28 | /** @deprecated use {@link #PROP_USER_SETTING_FILE} */ 29 | public static final String PROP_USER_SETTING = "userSettings"; 30 | 31 | public static final String PROP_USER_SETTING_FILE = "userSettingsFile"; 32 | 33 | public static final String PROP_GLOBAL_SETTING_FILE = "globalSettingsFile"; 34 | 35 | public static final String PROP_LOCAL_REPOSITORY = "localRepository"; 36 | 37 | public static final String PROP_OFFLINE = "offline"; 38 | 39 | public static final String PROP_UPDATESNAPSHOTS = "updateSnapshots"; 40 | 41 | public static final String PROP_REPOSITORY = "repository."; 42 | 43 | private final Map properties; 44 | 45 | public TestProperties() { 46 | try { 47 | this.properties = loadProperties(); 48 | } catch (IOException e) { 49 | throw new RuntimeException(e); 50 | } 51 | } 52 | 53 | protected Map loadProperties() throws IOException { 54 | Properties p = new Properties(); 55 | try (InputStream os = getClass().getClassLoader().getResourceAsStream("test.properties")) { 56 | Assert.assertNotNull( 57 | "test.properties must be present on test classpath, see https://github.com/takari/takari-plugin-testing-project/blob/master/testproperties.md for me details", 58 | os); 59 | p.load(os); 60 | } 61 | Map properties = new HashMap<>(); 62 | for (String key : p.stringPropertyNames()) { 63 | properties.put(key, p.getProperty(key)); 64 | } 65 | return Collections.unmodifiableMap(properties); 66 | } 67 | 68 | public String get(String key) { 69 | return properties.get(key); 70 | } 71 | 72 | public File getUserSettings() { 73 | // can be null 74 | String path = properties.get(PROP_USER_SETTING_FILE); 75 | if (path == null) { 76 | path = properties.get(PROP_USER_SETTING); 77 | } 78 | if (path == null) { 79 | return null; 80 | } 81 | File file = new File(path); 82 | Assert.assertTrue("Can read user settings.xml", file.canRead()); 83 | return file; 84 | } 85 | 86 | public File getGlobalSettings() { 87 | // can be null 88 | String path = properties.get(PROP_GLOBAL_SETTING_FILE); 89 | if (path == null) { 90 | return null; 91 | } 92 | File file = new File(path); 93 | Assert.assertTrue("Can read global settings.xml", file.canRead()); 94 | return file; 95 | } 96 | 97 | public File getLocalRepository() { 98 | String path = properties.get(PROP_LOCAL_REPOSITORY); 99 | Assert.assertNotNull("Local repository specified", path); 100 | return new File(path); 101 | } 102 | 103 | public boolean getOffline() { 104 | String value = properties.get(PROP_OFFLINE); 105 | return value != null ? Boolean.parseBoolean(value) : false; 106 | } 107 | 108 | public boolean getUpdateSnapshots() { 109 | String value = properties.get(PROP_UPDATESNAPSHOTS); 110 | return value != null ? Boolean.parseBoolean(value) : false; 111 | } 112 | 113 | public String getPluginVersion() { 114 | return properties.get("project.version"); 115 | } 116 | 117 | /** 118 | * Returns location of the current project classes, i.e. target/classes directory, and all project dependencies with scope=runtime. 119 | *

120 | * Useful for testing maven core extensions, {@link MavenRuntimeBuilder#withExtensions(java.util.Collection)} 121 | */ 122 | public List getRuntimeClasspath() { 123 | StringTokenizer st = new StringTokenizer(properties.get(PROP_CLASSPATH), File.pathSeparator); 124 | List dependencies = new ArrayList<>(); 125 | while (st.hasMoreTokens()) { 126 | dependencies.add(new File(st.nextToken())); 127 | } 128 | return dependencies; 129 | } 130 | 131 | public List getRepositories() { 132 | TreeMap repositories = new TreeMap<>(); 133 | for (Map.Entry property : properties.entrySet()) { 134 | if (property.getKey().startsWith(PROP_REPOSITORY)) { 135 | repositories.put(property.getKey(), property.getValue()); 136 | } 137 | } 138 | return new ArrayList<>(repositories.values()); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/TestResources.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import org.junit.Rule; 11 | import org.junit.rules.TestRule; 12 | import org.junit.runner.Description; 13 | import org.junit.runners.model.Statement; 14 | 15 | /** 16 | * Junit4 test {@link Rule} to extract and assert test resources. 17 | */ 18 | public class TestResources extends AbstractTestResources implements TestRule { 19 | 20 | public TestResources() { 21 | super(); 22 | } 23 | 24 | public TestResources(String projectsDir, String workDir) { 25 | super(projectsDir, workDir); 26 | } 27 | 28 | @Override 29 | public Statement apply(Statement base, Description d) { 30 | return new Statement() { 31 | @Override 32 | public void evaluate() throws Throwable { 33 | starting(d.getTestClass(), d.getMethodName()); 34 | base.evaluate(); 35 | } 36 | }; 37 | } 38 | 39 | @Override 40 | String getRequiredAnnotationClassName() { 41 | return "org.junit.Rule"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/TestResources5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing; 9 | 10 | import java.lang.reflect.Method; 11 | import org.junit.jupiter.api.extension.BeforeEachCallback; 12 | import org.junit.jupiter.api.extension.ExtensionContext; 13 | 14 | /** 15 | * JUnit 5 extension to extract and assert test resources. 16 | */ 17 | public class TestResources5 extends AbstractTestResources implements BeforeEachCallback { 18 | 19 | public TestResources5() { 20 | super(); 21 | } 22 | 23 | public TestResources5(String projectsDir, String workDir) { 24 | super(projectsDir, workDir); 25 | } 26 | 27 | @Override 28 | public void beforeEach(ExtensionContext context) throws Exception { 29 | String methodName = context.getTestMethod().map(Method::getName).orElse(null); 30 | starting(context.getRequiredTestClass(), methodName); 31 | } 32 | 33 | @Override 34 | String getRequiredAnnotationClassName() { 35 | return "org.junit.jupiter.api.extension.RegisterExtension"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/ForkedLauncher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | /* 11 | * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 12 | * agreements. See the NOTICE file distributed with this work for additional information regarding 13 | * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 14 | * "License"); you may not use this file except in compliance with the License. You may obtain a 15 | * copy of the License at 16 | * 17 | * http://www.apache.org/licenses/LICENSE-2.0 18 | * 19 | * Unless required by applicable law or agreed to in writing, software distributed under the License 20 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 21 | * or implied. See the License for the specific language governing permissions and limitations under 22 | * the License. 23 | */ 24 | 25 | import static io.takari.maven.testing.executor.MavenInstallationUtils.MAVEN4_MAIN_CLASS; 26 | import static io.takari.maven.testing.executor.MavenInstallationUtils.SYSPROP_MAVEN_MAIN_CLASS; 27 | 28 | import java.io.File; 29 | import java.io.FileOutputStream; 30 | import java.io.IOException; 31 | import java.io.OutputStream; 32 | import java.io.PrintStream; 33 | import java.nio.charset.Charset; 34 | import java.nio.file.Files; 35 | import java.util.Collections; 36 | import java.util.HashMap; 37 | import java.util.Iterator; 38 | import java.util.List; 39 | import java.util.Map; 40 | import java.util.regex.Matcher; 41 | import java.util.regex.Pattern; 42 | import org.apache.commons.exec.CommandLine; 43 | import org.apache.commons.exec.DefaultExecutor; 44 | import org.apache.commons.exec.ExecuteException; 45 | import org.apache.commons.exec.PumpStreamHandler; 46 | import org.apache.commons.exec.ShutdownHookProcessDestroyer; 47 | import org.codehaus.plexus.util.Os; 48 | 49 | /** 50 | * @author Benjamin Bentmann 51 | */ 52 | class ForkedLauncher implements MavenLauncher { 53 | 54 | private final File mavenHome; 55 | 56 | private final File classworldsJar; 57 | 58 | private final Map envVars; 59 | 60 | private final List extensions; 61 | 62 | private final List args; 63 | 64 | private final List jvmArgs; 65 | 66 | public ForkedLauncher( 67 | File mavenHome, 68 | File classworldsConf, 69 | List extensions, 70 | Map envVars, 71 | List args, 72 | List jvmArgs) { 73 | this.args = args; 74 | this.jvmArgs = jvmArgs; 75 | if (mavenHome == null) { 76 | throw new NullPointerException(); 77 | } 78 | if (classworldsConf != null) { 79 | throw new IllegalArgumentException("Custom classworlds configuration file is not supported"); 80 | } 81 | 82 | this.mavenHome = mavenHome; 83 | this.envVars = envVars; 84 | this.extensions = extensions; 85 | 86 | File classworldsJar = null; 87 | File[] files = new File(mavenHome, "boot").listFiles(); 88 | if (files != null) { 89 | for (File file : files) { 90 | String name = file.getName(); 91 | if (name.startsWith("plexus-classworlds-") && name.endsWith(".jar")) { 92 | classworldsJar = file; 93 | break; 94 | } 95 | } 96 | } 97 | if (classworldsJar == null) { 98 | throw new IllegalArgumentException("Invalid maven home " + mavenHome); 99 | } 100 | this.classworldsJar = classworldsJar; 101 | } 102 | 103 | public int run( 104 | String[] cliArgs, 105 | Map envVars, 106 | File multiModuleProjectDirectory, 107 | File workingDirectory, 108 | File logFile) 109 | throws IOException, LauncherException { 110 | String javaHome; 111 | if (envVars == null || envVars.get("JAVA_HOME") == null) { 112 | javaHome = System.getProperty("java.home"); 113 | } else { 114 | javaHome = envVars.get("JAVA_HOME"); 115 | } 116 | 117 | File executable = new File(javaHome, Os.isFamily(Os.FAMILY_WINDOWS) ? "bin/javaw.exe" : "bin/java"); 118 | 119 | CommandLine cli = new CommandLine(executable); 120 | cli.addArgument("-classpath").addArgument(classworldsJar.getAbsolutePath()); 121 | cli.addArgument("-Dclassworlds.conf=" + new File(mavenHome, "bin/m2.conf").getAbsolutePath()); 122 | cli.addArgument("-Dmaven.home=" + mavenHome.getAbsolutePath()); 123 | cli.addArgument("-Dmaven.multiModuleProjectDirectory=" + multiModuleProjectDirectory.getAbsolutePath()); 124 | cli.addArgument("-D" + SYSPROP_MAVEN_MAIN_CLASS + "=" + MAVEN4_MAIN_CLASS); 125 | 126 | cli.addArguments(jvmArgs.toArray(new String[0])); 127 | 128 | cli.addArgument("org.codehaus.plexus.classworlds.launcher.Launcher"); 129 | 130 | cli.addArguments(args.toArray(new String[0])); 131 | if (extensions != null && !extensions.isEmpty()) { 132 | cli.addArgument("-Dmaven.ext.class.path=" + toPath(extensions)); 133 | } 134 | 135 | cli.addArguments(cliArgs); 136 | 137 | Map env = new HashMap<>(); 138 | env.put("M2_HOME", mavenHome.getAbsolutePath()); 139 | if (envVars != null) { 140 | env.putAll(envVars); 141 | } 142 | if (envVars == null || envVars.get("JAVA_HOME") == null) { 143 | env.put("JAVA_HOME", System.getProperty("java.home")); 144 | } 145 | 146 | DefaultExecutor executor = DefaultExecutor.builder() 147 | .setWorkingDirectory(workingDirectory.getAbsoluteFile()) 148 | .get(); 149 | executor.setProcessDestroyer(new ShutdownHookProcessDestroyer()); 150 | 151 | try (OutputStream log = new FileOutputStream(logFile)) { 152 | PrintStream out = new PrintStream(log); 153 | out.format("Maven Executor implementation: %s\n", getClass().getName()); 154 | out.format("Maven home: %s\n", mavenHome); 155 | out.format("Build work directory: %s\n", workingDirectory); 156 | out.format("Environment: %s\n", env); 157 | out.format("Command line: %s\n\n", cli.toString()); 158 | out.flush(); 159 | 160 | PumpStreamHandler streamHandler = new PumpStreamHandler(log); 161 | executor.setStreamHandler(streamHandler); 162 | return executor.execute(cli, env); // this throws ExecuteException if process return code != 0 163 | } catch (ExecuteException e) { 164 | throw new LauncherException("Failed to run Maven: " + e.getMessage() + "\n" + cli, e); 165 | } 166 | } 167 | 168 | private static String toPath(List strings) { 169 | StringBuilder sb = new StringBuilder(); 170 | for (String string : strings) { 171 | if (sb.length() > 0) { 172 | sb.append(File.pathSeparatorChar); 173 | } 174 | sb.append(string); 175 | } 176 | return sb.toString(); 177 | } 178 | 179 | @Override 180 | public int run(String[] cliArgs, File multiModuleProjectDirectory, File workingDirectory, File logFile) 181 | throws IOException, LauncherException { 182 | return run(cliArgs, envVars, multiModuleProjectDirectory, workingDirectory, logFile); 183 | } 184 | 185 | @Override 186 | public String getMavenVersion() throws IOException, LauncherException { 187 | // TODO cleanup, there is no need to write log file, for example 188 | 189 | File logFile; 190 | try { 191 | logFile = File.createTempFile("maven", "log"); 192 | } catch (IOException e) { 193 | throw new LauncherException("Error creating temp file", e); 194 | } 195 | 196 | // disable EMMA runtime controller port allocation, should be harmless if EMMA is not used 197 | Map envVars = Collections.singletonMap("MAVEN_OPTS", "-Demma.rt.control=false"); 198 | run(new String[] {"--version"}, envVars, new File(""), new File(""), logFile); 199 | 200 | List logLines = Files.readAllLines(logFile.toPath(), Charset.defaultCharset()); 201 | // noinspection ResultOfMethodCallIgnored 202 | logFile.delete(); 203 | 204 | String version = extractMavenVersion(logLines); 205 | 206 | if (version == null) { 207 | throw new LauncherException( 208 | "Illegal Maven output: String 'Maven' not found in the following output:\n" + join(logLines, "\n")); 209 | } else { 210 | return version; 211 | } 212 | } 213 | 214 | private String join(List lines, String eol) { 215 | StringBuilder sb = new StringBuilder(); 216 | for (String line : lines) { 217 | sb.append(line).append(eol); 218 | } 219 | return sb.toString(); 220 | } 221 | 222 | static String extractMavenVersion(List logLines) { 223 | String version = null; 224 | 225 | final Pattern mavenVersion = Pattern.compile("(?i).*Maven.*? ([0-9]\\.\\S*).*"); 226 | 227 | for (Iterator it = logLines.iterator(); version == null && it.hasNext(); ) { 228 | String line = it.next(); 229 | 230 | Matcher m = mavenVersion.matcher(line); 231 | if (m.matches()) { 232 | version = m.group(1); 233 | } 234 | } 235 | 236 | return version; 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/LauncherException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | /* 11 | * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 12 | * agreements. See the NOTICE file distributed with this work for additional information regarding 13 | * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 14 | * "License"); you may not use this file except in compliance with the License. You may obtain a 15 | * copy of the License at 16 | * 17 | * http://www.apache.org/licenses/LICENSE-2.0 18 | * 19 | * Unless required by applicable law or agreed to in writing, software distributed under the License 20 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 21 | * or implied. See the License for the specific language governing permissions and limitations under 22 | * the License. 23 | */ 24 | 25 | /** 26 | * @author Benjamin Bentmann 27 | */ 28 | @SuppressWarnings("serial") 29 | class LauncherException extends Exception { 30 | 31 | public LauncherException(String message) { 32 | super(message); 33 | } 34 | 35 | public LauncherException(String message, Throwable cause) { 36 | super(message, cause); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenExecution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | import io.takari.maven.testing.TestProperties; 11 | import java.io.File; 12 | import java.nio.file.Files; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class MavenExecution { 17 | 18 | private final MavenLauncher launcher; 19 | 20 | private final TestProperties properties; 21 | 22 | private final File multiModuleProjectDirectory; 23 | 24 | private final File basedir; 25 | 26 | private final List cliOptions = new ArrayList<>(); 27 | 28 | MavenExecution( 29 | MavenLauncher launcher, TestProperties properties, File multiModuleProjectDirectory, String moduleRelpath) { 30 | this.launcher = launcher; 31 | this.properties = properties; 32 | this.multiModuleProjectDirectory = multiModuleProjectDirectory; 33 | this.basedir = moduleRelpath != null 34 | ? new File(multiModuleProjectDirectory, moduleRelpath) 35 | : multiModuleProjectDirectory; 36 | } 37 | 38 | public MavenExecutionResult execute(String... goals) throws Exception { 39 | File logFile = new File(basedir, "log.txt"); 40 | 41 | List args = new ArrayList<>(); 42 | 43 | File userSettings = properties.getUserSettings(); 44 | if (userSettings != null && userSettings.isFile() && !isOption(cliOptions, "-s", true)) { 45 | args.add("-s"); 46 | args.add(userSettings.getAbsolutePath()); 47 | } 48 | if (!isOption(cliOptions, "-Dmaven.repo.local=", false)) { 49 | args.add("-Dmaven.repo.local=" + properties.getLocalRepository().getAbsolutePath()); 50 | } 51 | args.add("-Dit-plugin.version=" + properties.getPluginVersion()); // TODO deprecated and remove 52 | args.add("-Dit-project.version=" + properties.getPluginVersion()); 53 | args.addAll(cliOptions); 54 | 55 | for (String goal : goals) { 56 | args.add(goal); 57 | } 58 | 59 | try { 60 | launcher.run(args.toArray(new String[args.size()]), multiModuleProjectDirectory, basedir, logFile); 61 | } catch (Exception e) { 62 | String ciEnvar = System.getenv("CONTINUOUS_INTEGRATION"); 63 | if (ciEnvar != null && ciEnvar.equalsIgnoreCase("true")) { 64 | String logFileContent = new String(Files.readAllBytes(logFile.toPath())); 65 | System.out.println(logFileContent); 66 | } 67 | throw e; 68 | } 69 | 70 | return new MavenExecutionResult(basedir, logFile); 71 | } 72 | 73 | private boolean isOption(List args, String str, boolean exact) { 74 | for (String arg : args) { 75 | if (exact && str.equals(arg)) { 76 | return true; 77 | } else if (!exact && arg.startsWith(str)) { 78 | return true; 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | public MavenExecution withCliOption(String string) { 85 | cliOptions.add(string); 86 | return this; 87 | } 88 | 89 | public MavenExecution withCliOptions(String... strings) { 90 | for (String string : strings) { 91 | cliOptions.add(string); 92 | } 93 | return this; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenExecutionResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.nio.charset.Charset; 13 | import java.nio.file.Files; 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.List; 17 | import org.junit.Assert; 18 | 19 | // wraps maven invocation results 20 | public class MavenExecutionResult { 21 | 22 | private final File basedir; 23 | private final List log; 24 | 25 | MavenExecutionResult(File basedir, File logFile) throws IOException { 26 | this.basedir = basedir; 27 | List log = new ArrayList<>(); 28 | if (logFile.canRead()) { 29 | for (String line : Files.readAllLines(logFile.toPath(), Charset.defaultCharset())) { 30 | log.add(line); 31 | } 32 | } 33 | this.log = Collections.unmodifiableList(log); 34 | } 35 | 36 | public MavenExecutionResult assertErrorFreeLog() throws Exception { 37 | List errors = new ArrayList<>(); 38 | for (String line : log) { 39 | if (line.contains("[ERROR]")) { 40 | errors.add(line); 41 | } 42 | } 43 | Assert.assertTrue(errors.toString(), errors.isEmpty()); 44 | return this; 45 | } 46 | 47 | public MavenExecutionResult assertLogText(String text) { 48 | for (String line : log) { 49 | if (line.contains(text)) { 50 | return this; 51 | } 52 | } 53 | Assert.fail("Log line not present: " + text); 54 | return this; 55 | } 56 | 57 | public MavenExecutionResult assertNoLogText(String text) { 58 | for (String line : log) { 59 | if (line.contains(text)) { 60 | Assert.fail("Log line present: " + text); 61 | } 62 | } 63 | return this; 64 | } 65 | 66 | public File getBasedir() { 67 | return basedir; 68 | } 69 | 70 | /** 71 | * @since 2.9.2 72 | */ 73 | public List getLog() { 74 | return log; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenInstallationUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | import java.io.BufferedInputStream; 11 | import java.io.File; 12 | import java.io.FileInputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.net.URL; 16 | import java.util.Properties; 17 | import java.util.zip.ZipEntry; 18 | import java.util.zip.ZipFile; 19 | import org.codehaus.plexus.classworlds.ClassWorldException; 20 | import org.codehaus.plexus.classworlds.launcher.ConfigurationException; 21 | import org.codehaus.plexus.classworlds.launcher.ConfigurationHandler; 22 | import org.codehaus.plexus.classworlds.launcher.ConfigurationParser; 23 | import org.codehaus.plexus.classworlds.realm.DuplicateRealmException; 24 | import org.codehaus.plexus.classworlds.realm.NoSuchRealmException; 25 | 26 | public class MavenInstallationUtils { 27 | 28 | public static final String MAVEN_CORE_POMPROPERTIES = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; 29 | 30 | public static final String SYSPROP_MAVEN_HOME = "maven.home"; 31 | 32 | public static final String SYSPROP_CLASSWORLDSCONF = "classworlds.conf"; 33 | 34 | public static final String SYSPROP_MAVEN_MAIN_CLASS = "maven.mainClass"; 35 | 36 | public static final String MAVEN3_MAIN_CLASS = "org.apache.maven.cli.MavenCli"; 37 | 38 | public static final String MAVEN4_MAIN_CLASS = "org.apache.maven.cling.MavenCling"; 39 | 40 | public static String getMavenVersion(Class clazz) throws IOException { 41 | try (InputStream is = clazz.getResourceAsStream("/" + MAVEN_CORE_POMPROPERTIES)) { 42 | return getMavenVersion(is); 43 | } 44 | } 45 | 46 | public static String getMavenVersion(InputStream is) throws IOException { 47 | Properties props = new Properties(); 48 | if (is != null) { 49 | props.load(is); 50 | } 51 | return props.getProperty("version"); 52 | } 53 | 54 | public static String getMavenVersion(File mavenHome, File classworldsConf) { 55 | classworldsConf = getClassworldsConf(mavenHome, classworldsConf); 56 | 57 | @SuppressWarnings("serial") 58 | class MavenVersionFoundException extends RuntimeException { 59 | public final String version; 60 | 61 | MavenVersionFoundException(String version) { 62 | this.version = version; 63 | } 64 | } 65 | 66 | class VersionConfigHandler implements ConfigurationHandler { 67 | 68 | @Override 69 | public void setAppMain(String mainClassName, String mainRealmName) {} 70 | 71 | @Override 72 | public void addRealm(String realmName) throws DuplicateRealmException {} 73 | 74 | @Override 75 | public void addImportFrom(String relamName, String importSpec) throws NoSuchRealmException {} 76 | 77 | @Override 78 | public void addLoadFile(File file) { 79 | String version = null; 80 | try { 81 | if (file.isFile()) { 82 | try (ZipFile zip = new ZipFile(file)) { 83 | ZipEntry entry = zip.getEntry(MAVEN_CORE_POMPROPERTIES); 84 | if (entry != null) { 85 | try (InputStream is = zip.getInputStream(entry)) { 86 | version = getMavenVersion(is); 87 | } 88 | } 89 | } 90 | } else { 91 | try (InputStream is = new BufferedInputStream( 92 | new FileInputStream(new File(file, MAVEN_CORE_POMPROPERTIES)))) { 93 | version = getMavenVersion(is); 94 | } 95 | } 96 | if (version != null) { 97 | throw new MavenVersionFoundException(version); 98 | } 99 | } catch (IOException e) { 100 | // assume the file does not have maven version 101 | } 102 | } 103 | 104 | @Override 105 | public void addLoadURL(URL url) {} 106 | } 107 | ; 108 | 109 | try { 110 | VersionConfigHandler configHandler = new VersionConfigHandler(); 111 | Properties properties = new Properties(System.getProperties()); 112 | properties.setProperty(SYSPROP_MAVEN_MAIN_CLASS, MAVEN4_MAIN_CLASS); // must for maven4, ignored by maven3 113 | properties.setProperty(SYSPROP_MAVEN_HOME, mavenHome.getCanonicalPath()); 114 | ConfigurationParser configParser = new ConfigurationParser(configHandler, properties); 115 | try (InputStream is = new BufferedInputStream(new FileInputStream(classworldsConf))) { 116 | configParser.parse(is); 117 | } 118 | } catch (IOException | ClassWorldException | ConfigurationException e) { 119 | throw new IllegalArgumentException("Could not determine Maven version", e); 120 | } catch (MavenVersionFoundException e) { 121 | return e.version; 122 | } 123 | 124 | throw new IllegalArgumentException("Could not determine Maven version"); 125 | } 126 | 127 | public static File getForcedClassworldsConf() { 128 | File configFile = null; 129 | String classworldConf = System.getProperty(SYSPROP_CLASSWORLDSCONF); 130 | String mavenHome = System.getProperty(SYSPROP_MAVEN_HOME); 131 | if (classworldConf != null) { 132 | configFile = new File(classworldConf); 133 | } 134 | if (configFile == null) { 135 | if (mavenHome != null) { 136 | configFile = new File(mavenHome, "bin/m2.conf"); 137 | } 138 | } 139 | return configFile; 140 | } 141 | 142 | public static File getForcedMavenHome() { 143 | String mavenHome = System.getProperty(SYSPROP_MAVEN_HOME); 144 | if (mavenHome != null) { 145 | return new File(mavenHome); 146 | } 147 | return null; 148 | } 149 | 150 | public static File getClassworldsConf(File mavenHome, File classworldsConf) { 151 | if (classworldsConf == null) { 152 | classworldsConf = new File(mavenHome, "bin/m2.conf"); 153 | } 154 | return classworldsConf; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenInstallations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | import java.lang.annotation.ElementType; 11 | import java.lang.annotation.Inherited; 12 | import java.lang.annotation.Retention; 13 | import java.lang.annotation.RetentionPolicy; 14 | import java.lang.annotation.Target; 15 | 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.TYPE) 18 | @Inherited 19 | public @interface MavenInstallations { 20 | 21 | /* 22 | * Note on installation names. In most cases it will be desirable to give custom distributions a meaningful name and use that name as part of test name. Most likely this will be useful not only for 23 | * integration tests but in other scenarios too (Hudson and m2e immediately come to mind), so the name/version need to be embedded in the maven installation itself. For example, it can be a file 24 | * under ${maven.home}/conf directory, similar to /etc/issue used by linux distributions. It is also possible to include distribution name/version in one of the jars, similar to how maven version is 25 | * already included in META-INF/maven/org.apache.maven/maven-core/pom.properties. 26 | */ 27 | public String[] value(); 28 | } 29 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenLauncher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | /* 11 | * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 12 | * agreements. See the NOTICE file distributed with this work for additional information regarding 13 | * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 14 | * "License"); you may not use this file except in compliance with the License. You may obtain a 15 | * copy of the License at 16 | * 17 | * http://www.apache.org/licenses/LICENSE-2.0 18 | * 19 | * Unless required by applicable law or agreed to in writing, software distributed under the License 20 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 21 | * or implied. See the License for the specific language governing permissions and limitations under 22 | * the License. 23 | */ 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | 28 | /** 29 | * @author Benjamin Bentmann 30 | */ 31 | interface MavenLauncher { 32 | 33 | int run(String[] cliArgs, File multiModuleProjectDirectory, File workingDirectory, File logFile) 34 | throws IOException, LauncherException; 35 | 36 | String getMavenVersion() throws IOException, LauncherException; 37 | } 38 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenRuntime.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | import static org.eclipse.m2e.workspace.WorkspaceState2.SYSPROP_STATEFILE_LOCATION; 11 | 12 | import io.takari.maven.testing.TestProperties; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.util.ArrayList; 16 | import java.util.Collection; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | public class MavenRuntime { 22 | private final MavenLauncher launcher; 23 | 24 | private final TestProperties properties; 25 | 26 | public static class MavenRuntimeBuilder { 27 | 28 | protected final TestProperties properties; 29 | 30 | protected final File mavenHome; 31 | 32 | protected final String mavenVersion; 33 | 34 | protected final File classworldsConf; 35 | 36 | protected final List extensions = new ArrayList<>(); 37 | 38 | protected final List args = new ArrayList<>(); 39 | 40 | MavenRuntimeBuilder(File mavenHome, File classworldsConf) { 41 | this.properties = new TestProperties(); 42 | this.mavenHome = mavenHome; 43 | this.mavenVersion = MavenInstallationUtils.getMavenVersion(mavenHome, classworldsConf); 44 | this.classworldsConf = classworldsConf; 45 | 46 | StringBuilder workspaceState = new StringBuilder(); 47 | appendLocation(workspaceState, System.getProperty(SYSPROP_STATEFILE_LOCATION)); 48 | appendLocation(workspaceState, properties.get("workspaceStateProperties")); 49 | 50 | String workspaceResolver = properties.get("workspaceResolver"); 51 | if (workspaceState.length() > 0 && isFile(workspaceResolver)) { 52 | if ("3.2.1".equals(mavenVersion)) { 53 | throw new IllegalArgumentException( 54 | "Maven 3.2.1 is not supported, see https://jira.codehaus.org/browse/MNG-5591"); 55 | } 56 | args.add("-D" + SYSPROP_STATEFILE_LOCATION + "=" + workspaceState.toString()); 57 | extensions.add(workspaceResolver); 58 | } 59 | // TODO decide if workspace resolution must be enabled and enforced 60 | } 61 | 62 | private void appendLocation(StringBuilder workspaceState, String location) { 63 | if (location != null) { 64 | if (!isFile(location)) { 65 | throw new IllegalArgumentException("Not a file " + location); 66 | } 67 | if (workspaceState.length() > 0) { 68 | workspaceState.append(File.pathSeparator); 69 | } 70 | workspaceState.append(location); 71 | } 72 | } 73 | 74 | MavenRuntimeBuilder(File mavenHome, File classworldsConf, List extensions, List args) { 75 | this.properties = new TestProperties(); 76 | this.mavenHome = mavenHome; 77 | this.mavenVersion = MavenInstallationUtils.getMavenVersion(mavenHome, classworldsConf); 78 | this.classworldsConf = classworldsConf; 79 | this.extensions.addAll(extensions); 80 | this.args.addAll(args); 81 | } 82 | 83 | private static boolean isFile(String path) { 84 | return path != null && new File(path).isFile(); 85 | } 86 | 87 | public MavenRuntimeBuilder withExtension(File extensionLocation) { 88 | assertFileExists("No such file or directory: " + extensionLocation, extensionLocation); 89 | extensions.add(extensionLocation.getAbsolutePath()); 90 | return this; 91 | } 92 | 93 | public MavenRuntimeBuilder withExtensions(Collection extensionLocations) { 94 | for (File extensionLocation : extensionLocations) { 95 | assertFileExists("No such file or directory: " + extensionLocation, extensionLocation); 96 | extensions.add(extensionLocation.getAbsolutePath()); 97 | } 98 | return this; 99 | } 100 | 101 | private void assertFileExists(String message, File file) { 102 | if (!file.exists()) { 103 | throw new AssertionError("No such file or directory: " + file); 104 | } 105 | } 106 | 107 | public MavenRuntimeBuilder withCliOptions(String... options) { 108 | for (String option : options) { 109 | args.add(option); 110 | } 111 | return this; 112 | } 113 | 114 | public ForkedMavenRuntimeBuilder forkedBuilder() { 115 | return new ForkedMavenRuntimeBuilder(mavenHome, classworldsConf, extensions, args); 116 | } 117 | 118 | public MavenRuntime build() throws Exception { 119 | Embedded3xLauncher launcher = 120 | Embedded3xLauncher.createFromMavenHome(mavenHome, classworldsConf, extensions, args); 121 | return new MavenRuntime(launcher, properties); 122 | } 123 | } 124 | 125 | public static class ForkedMavenRuntimeBuilder extends MavenRuntimeBuilder { 126 | 127 | private Map environment; 128 | private final List jvmArgs = new ArrayList<>(); 129 | 130 | ForkedMavenRuntimeBuilder(File mavenHome, File classworldsConf) { 131 | super(mavenHome, classworldsConf); 132 | } 133 | 134 | ForkedMavenRuntimeBuilder(File mavenHome, File classworldsConf, List extensions, List args) { 135 | super(mavenHome, classworldsConf, extensions, args); 136 | } 137 | 138 | public ForkedMavenRuntimeBuilder withEnvironment(Map environment) { 139 | this.environment = new HashMap<>(environment); 140 | return this; 141 | } 142 | 143 | @Override 144 | public ForkedMavenRuntimeBuilder withExtension(File extensionLocation) { 145 | super.withExtension(extensionLocation); 146 | return this; 147 | } 148 | 149 | @Override 150 | public ForkedMavenRuntimeBuilder withExtensions(Collection extensionLocations) { 151 | super.withExtensions(extensionLocations); 152 | return this; 153 | } 154 | 155 | @Override 156 | public ForkedMavenRuntimeBuilder withCliOptions(String... options) { 157 | super.withCliOptions(options); 158 | return this; 159 | } 160 | 161 | /** 162 | * Adds a JVM option, as opposed to application option that can be added via {@link #withCliOptions(String...)}. Use this method to add options you would normally pass to {@code mvn/mvn.bat} via 163 | * {@code MAVEN_OPTS} environment variable. 164 | * 165 | * @param jvmOption the JVM option to add 166 | * @return this {@link ForkedMavenRuntimeBuilder} 167 | */ 168 | public ForkedMavenRuntimeBuilder withJvmOption(String jvmOption) { 169 | this.jvmArgs.add(jvmOption); 170 | return this; 171 | } 172 | 173 | /** 174 | * Add a JVM options, as opposed to application options that can be added via {@link #withCliOptions(String...)}. Use this method to add options you would normally pass to {@code mvn/mvn.bat} via 175 | * {@code MAVEN_OPTS} environment variable. 176 | * 177 | * @param jvmOptions the JVM options to add 178 | * @return this {@link ForkedMavenRuntimeBuilder} 179 | */ 180 | public ForkedMavenRuntimeBuilder withJvmOptions(String... jvmOptions) { 181 | for (String jvmArg : jvmOptions) { 182 | this.jvmArgs.add(jvmArg); 183 | } 184 | return this; 185 | } 186 | 187 | /** 188 | * Add a JVM options, as opposed to application options that can be added via {@link #withCliOptions(String...)}. Use this method to add options you would normally pass to {@code mvn/mvn.bat} via 189 | * {@code MAVEN_OPTS} environment variable. 190 | * 191 | * @param jvmOptions the JVM options to add 192 | * @return this {@link ForkedMavenRuntimeBuilder} 193 | */ 194 | public ForkedMavenRuntimeBuilder withJvmOptions(Collection jvmOptions) { 195 | this.jvmArgs.addAll(jvmOptions); 196 | return this; 197 | } 198 | 199 | @Override 200 | public MavenRuntime build() { 201 | ForkedLauncher launcher = 202 | new ForkedLauncher(mavenHome, classworldsConf, extensions, environment, args, jvmArgs); 203 | return new MavenRuntime(launcher, properties); 204 | } 205 | } 206 | 207 | MavenRuntime(MavenLauncher launcher, TestProperties properties) { 208 | this.launcher = launcher; 209 | this.properties = properties; 210 | } 211 | 212 | public static MavenRuntimeBuilder builder(File mavenHome, File classworldsConf) { 213 | return new MavenRuntimeBuilder(mavenHome, classworldsConf); 214 | } 215 | 216 | public static ForkedMavenRuntimeBuilder forkedBuilder(File mavenHome) { 217 | return new ForkedMavenRuntimeBuilder(mavenHome, null); 218 | } 219 | 220 | public MavenExecution forProject(File multiModuleProjectDirectory) { 221 | return new MavenExecution(launcher, properties, multiModuleProjectDirectory, null); 222 | } 223 | 224 | /** 225 | * @since 2.9 226 | */ 227 | public MavenExecution forProject(File multiModuleProjectDirectory, String moduleRelpath) 228 | throws IOException, LauncherException { 229 | String mavenVersion = launcher.getMavenVersion(); 230 | if (!isVersion330plus(mavenVersion)) { 231 | throw new UnsupportedOperationException( 232 | "Explicit multiModuleProjectDirectory requires Maven 3.3 or newer, current version is " 233 | + mavenVersion); 234 | } 235 | return new MavenExecution(launcher, properties, multiModuleProjectDirectory, moduleRelpath); 236 | } 237 | 238 | private boolean isVersion330plus(String version) { 239 | String[] split = version.split("\\."); 240 | if (split.length < 2) { 241 | return false; // can't parse 242 | } 243 | if (Integer.parseInt(split[0]) < 3) { 244 | return false; 245 | } 246 | return Integer.parseInt(split[1]) >= 3; 247 | } 248 | 249 | /** 250 | * @since 2.0 251 | */ 252 | public String getMavenVersion() throws IOException, LauncherException { 253 | return launcher.getMavenVersion(); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/MavenVersions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor; 9 | 10 | import java.lang.annotation.ElementType; 11 | import java.lang.annotation.Inherited; 12 | import java.lang.annotation.Retention; 13 | import java.lang.annotation.RetentionPolicy; 14 | import java.lang.annotation.Target; 15 | 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.TYPE) 18 | @Inherited 19 | public @interface MavenVersions { 20 | public String[] value(); 21 | } 22 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/ForcedMavenRuntimeBuilderParameterResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import io.takari.maven.testing.executor.MavenInstallationUtils; 11 | import io.takari.maven.testing.executor.MavenRuntime; 12 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 13 | import java.io.File; 14 | import org.junit.jupiter.api.extension.ExtensionContext; 15 | import org.junit.jupiter.api.extension.ParameterContext; 16 | import org.junit.jupiter.api.extension.ParameterResolutionException; 17 | import org.junit.jupiter.api.extension.ParameterResolver; 18 | 19 | /** 20 | * Provides a {@link MavenRuntimeBuilder} based on {@code -Dmaven.home} 21 | * and optionally {@code -Dclassworlds.conf}. 22 | * 23 | * @author Philippe Marschall 24 | */ 25 | final class ForcedMavenRuntimeBuilderParameterResolver implements ParameterResolver { 26 | 27 | ForcedMavenRuntimeBuilderParameterResolver() { 28 | super(); 29 | } 30 | 31 | @Override 32 | public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { 33 | return (parameterContext.getParameter().getType() == MavenRuntimeBuilder.class) && MavenHomeUtils.isForced(); 34 | } 35 | 36 | @Override 37 | public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) 38 | throws ParameterResolutionException { 39 | File forcedMavenHome = MavenInstallationUtils.getForcedMavenHome(); 40 | File forcedClassworldsConf = MavenInstallationUtils.getForcedClassworldsConf(); 41 | 42 | if (forcedMavenHome != null) { 43 | if (forcedMavenHome.isDirectory() || ((forcedClassworldsConf != null) && forcedClassworldsConf.isFile())) { 44 | return MavenRuntime.builder(forcedMavenHome, forcedClassworldsConf); 45 | } 46 | } 47 | throw new ParameterResolutionException("Invalid -Dmaven.home=" + forcedMavenHome.getAbsolutePath()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenHomeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import io.takari.maven.testing.executor.MavenInstallationUtils; 11 | import java.io.File; 12 | import org.junit.jupiter.api.extension.ExtensionConfigurationException; 13 | 14 | final class MavenHomeUtils { 15 | 16 | private MavenHomeUtils() { 17 | throw new AssertionError("not instantiable"); 18 | } 19 | 20 | static boolean isForced() { 21 | 22 | File forcedMavenHome = MavenInstallationUtils.getForcedMavenHome(); 23 | File forcedClassworldsConf = MavenInstallationUtils.getForcedClassworldsConf(); 24 | 25 | if (forcedMavenHome != null) { 26 | if (forcedMavenHome.isDirectory() || (forcedClassworldsConf != null && forcedClassworldsConf.isFile())) { 27 | return true; 28 | } 29 | throw new ExtensionConfigurationException("Invalid -Dmaven.home=" + forcedMavenHome.getAbsolutePath()); 30 | } 31 | return false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenInstallationDisplayNameFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | final class MavenInstallationDisplayNameFormatter { 11 | 12 | private final String displayName; 13 | 14 | MavenInstallationDisplayNameFormatter(String displayName) { 15 | this.displayName = displayName; 16 | } 17 | 18 | String format(String mavenInstallation) { 19 | return this.displayName + '[' + mavenInstallation + ']'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenInstallationTestInvocationContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.util.Collections; 13 | import java.util.List; 14 | import org.junit.jupiter.api.extension.Extension; 15 | import org.junit.jupiter.api.extension.ExtensionConfigurationException; 16 | import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 17 | 18 | final class MavenInstallationTestInvocationContext implements TestTemplateInvocationContext { 19 | 20 | private final MavenInstallationDisplayNameFormatter formatter; 21 | private final String installation; 22 | 23 | MavenInstallationTestInvocationContext(String installation, MavenInstallationDisplayNameFormatter formatter) { 24 | this.installation = installation; 25 | this.formatter = formatter; 26 | } 27 | 28 | @Override 29 | public String getDisplayName(int invocationIndex) { 30 | return this.formatter.format(this.installation); 31 | } 32 | 33 | @Override 34 | public List getAdditionalExtensions() { 35 | File mavenHome; 36 | try { 37 | mavenHome = new File(this.installation).getCanonicalFile(); 38 | } catch (IOException e) { 39 | throw new ExtensionConfigurationException("could not access maven installation: " + this.installation, e); 40 | } 41 | if (mavenHome.isDirectory()) { 42 | return Collections.singletonList(new MavenRuntimeBuilderParameterResolver(mavenHome)); 43 | } else { 44 | throw new ExtensionConfigurationException("Invalid maven installation location " + installation); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenInstallationsTestExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import io.takari.maven.testing.executor.MavenInstallations; 11 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 12 | import java.util.Arrays; 13 | import java.util.stream.Stream; 14 | import org.junit.jupiter.api.extension.ExtensionContext; 15 | import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 16 | import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; 17 | 18 | /** 19 | * Provides a {@link TestTemplateInvocationContext} with a {@link MavenRuntimeBuilder} 20 | * for each Maven installation configured through {@link MavenInstallations}. 21 | *

22 | * Not active if Maven home is forced through {@code -Dmaven.home}. 23 | * 24 | * @author Philippe Marschall 25 | */ 26 | final class MavenInstallationsTestExtension implements TestTemplateInvocationContextProvider { 27 | 28 | @Override 29 | public boolean supportsTestTemplate(ExtensionContext context) { 30 | if (MavenHomeUtils.isForced()) { 31 | return false; 32 | } 33 | // @formatter:off 34 | return context.getTestClass() 35 | .map(clazz -> clazz.isAnnotationPresent(MavenInstallations.class)) 36 | .orElse(false); 37 | // @formatter:on 38 | } 39 | 40 | @Override 41 | public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { 42 | String displayName = context.getDisplayName(); 43 | String[] installations = context.getTestClass() 44 | .orElseThrow() 45 | .getAnnotation(MavenInstallations.class) 46 | .value(); 47 | return Arrays.stream(installations) 48 | .map(installation -> new MavenInstallationTestInvocationContext( 49 | installation, new MavenInstallationDisplayNameFormatter(displayName))); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenJUnitTestRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import io.takari.maven.testing.executor.MavenInstallationUtils; 11 | import io.takari.maven.testing.executor.MavenInstallations; 12 | import io.takari.maven.testing.executor.MavenRuntime; 13 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 14 | import io.takari.maven.testing.executor.MavenVersions; 15 | import java.io.File; 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.List; 19 | import org.junit.runner.Runner; 20 | import org.junit.runner.notification.RunNotifier; 21 | import org.junit.runners.BlockJUnit4ClassRunner; 22 | import org.junit.runners.Suite; 23 | import org.junit.runners.model.FrameworkMethod; 24 | import org.junit.runners.model.InitializationError; 25 | import org.junit.runners.model.Statement; 26 | 27 | /** 28 | * Runs JUnit4 tests with one or more Maven runtimes. The test class must have public constructor with single parameter of type {@linkplain MavenRuntimeBuilder MavenRuntimeBuilder}. 29 | *

30 | * Test Maven runtimes are located in the following order: 31 | * 32 | *

    33 | *
  1. If {@code -Dmaven.home} (and optionally {@code -Dclassworlds.conf}) is specified, the tests will be executed with the specified Maven installation. This is what is used in Eclipse Maven JUnit 34 | * Test launch configuration to implement "Override test Maven runtime". Can also be useful to run tests with custom or snapshot Maven build specified from pom.xml.
  2. 35 | *
  3. If {@linkplain MavenInstallations @MavenInstallations} and/or {@linkplain MavenVersions @MavenVersions} is specified, the tests will run with all configured Maven installations and versions
  4. 36 | *
37 | */ 38 | public class MavenJUnitTestRunner extends Suite { 39 | 40 | private static class SingleMavenInstallationRunner extends BlockJUnit4ClassRunner { 41 | 42 | private final File mavenHome; 43 | 44 | private final File classworldsConf; 45 | 46 | private final String name; 47 | 48 | SingleMavenInstallationRunner(Class klass, File mavenHome, File classworldsConf, String name) 49 | throws InitializationError { 50 | super(klass); 51 | this.mavenHome = mavenHome; 52 | this.classworldsConf = classworldsConf; 53 | this.name = name; 54 | } 55 | 56 | @Override 57 | protected Object createTest() throws Exception { 58 | MavenRuntimeBuilder builder = MavenRuntime.builder(mavenHome, classworldsConf); 59 | return getTestClass() 60 | .getJavaClass() 61 | .getConstructor(MavenRuntimeBuilder.class) 62 | .newInstance(builder); 63 | } 64 | 65 | @Override 66 | protected void validateZeroArgConstructor(List errors) { 67 | try { 68 | getTestClass().getJavaClass().getConstructor(MavenRuntimeBuilder.class); 69 | } catch (NoSuchMethodException e) { 70 | errors.add(e); 71 | } 72 | } 73 | 74 | @Override 75 | protected String getName() { 76 | return "[" + name + "]"; 77 | } 78 | 79 | @Override 80 | protected String testName(FrameworkMethod method) { 81 | return method.getName() + getName(); 82 | } 83 | 84 | @Override 85 | protected Statement classBlock(RunNotifier notifier) { 86 | return childrenInvoker(notifier); 87 | } 88 | } 89 | 90 | public MavenJUnitTestRunner(Class clazz) throws Throwable { 91 | super(clazz, getRunners(clazz)); 92 | } 93 | 94 | private static List getRunners(final Class clazz) throws Throwable { 95 | File forcedMavenHome = MavenInstallationUtils.getForcedMavenHome(); 96 | File forcedClassworldsConf = MavenInstallationUtils.getForcedClassworldsConf(); 97 | 98 | if (forcedMavenHome != null) { 99 | if (forcedMavenHome.isDirectory() || (forcedClassworldsConf != null && forcedClassworldsConf.isFile())) { 100 | String version = MavenInstallationUtils.getMavenVersion(forcedMavenHome, forcedClassworldsConf); 101 | return Collections.singletonList( 102 | new SingleMavenInstallationRunner(clazz, forcedMavenHome, forcedClassworldsConf, version)); 103 | } 104 | throw new InitializationError(new Exception("Invalid -Dmaven.home=" + forcedMavenHome.getAbsolutePath())); 105 | } 106 | 107 | final List errors = new ArrayList<>(); 108 | final List runners = new ArrayList<>(); 109 | 110 | MavenInstallations installations = clazz.getAnnotation(MavenInstallations.class); 111 | if (installations != null) { 112 | for (String installation : installations.value()) { 113 | File mavenHome = new File(installation).getCanonicalFile(); 114 | if (mavenHome.isDirectory()) { 115 | runners.add(new SingleMavenInstallationRunner(clazz, mavenHome, null, installation)); 116 | } else { 117 | errors.add(new Exception("Invalid maven installation location " + installation)); 118 | } 119 | } 120 | } 121 | 122 | MavenVersions versions = clazz.getAnnotation(MavenVersions.class); 123 | if (versions != null) { 124 | new MavenVersionResolver() { 125 | @Override 126 | protected void resolved(File mavenHome, String version) throws InitializationError { 127 | runners.add(new SingleMavenInstallationRunner(clazz, mavenHome, null, version)); 128 | } 129 | 130 | @Override 131 | protected void error(String version, Exception cause) { 132 | errors.add(new Exception("Could not resolve maven version " + version, cause)); 133 | } 134 | }.resolve(versions.value()); 135 | } 136 | 137 | if (!errors.isEmpty()) { 138 | throw new InitializationError(errors); 139 | } 140 | 141 | if (runners.isEmpty()) { 142 | throw new InitializationError(new Exception("No configured test maven runtime")); 143 | } 144 | 145 | return runners; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenPluginTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 11 | import static java.lang.annotation.ElementType.METHOD; 12 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 13 | 14 | import java.lang.annotation.Documented; 15 | import java.lang.annotation.Inherited; 16 | import java.lang.annotation.Retention; 17 | import java.lang.annotation.Target; 18 | import org.junit.jupiter.api.TestTemplate; 19 | import org.junit.jupiter.api.extension.ExtendWith; 20 | 21 | /** 22 | * Meta-annotation for Maven plugin integration tests, registers all necessary extensions. 23 | * 24 | * @author Philippe Marschall 25 | */ 26 | @Documented 27 | @Retention(RUNTIME) 28 | @Target({ANNOTATION_TYPE, METHOD}) 29 | @TestTemplate 30 | @Inherited 31 | @ExtendWith({ 32 | ForcedMavenRuntimeBuilderParameterResolver.class, 33 | MavenInstallationsTestExtension.class, 34 | MavenVersionsTestExtension.class 35 | }) 36 | public @interface MavenPluginTest {} 37 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenRuntimeBuilderParameterResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import io.takari.maven.testing.executor.MavenRuntime; 11 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 12 | import java.io.File; 13 | import org.junit.jupiter.api.extension.ExtensionContext; 14 | import org.junit.jupiter.api.extension.ParameterContext; 15 | import org.junit.jupiter.api.extension.ParameterResolutionException; 16 | import org.junit.jupiter.api.extension.ParameterResolver; 17 | 18 | final class MavenRuntimeBuilderParameterResolver implements ParameterResolver { 19 | 20 | private final File mavenHome; 21 | 22 | MavenRuntimeBuilderParameterResolver(File mavenHome) { 23 | this.mavenHome = mavenHome; 24 | } 25 | 26 | @Override 27 | public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { 28 | return parameterContext.getParameter().getType() == MavenRuntimeBuilder.class; 29 | } 30 | 31 | @Override 32 | public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) 33 | throws ParameterResolutionException { 34 | return MavenRuntime.builder(this.mavenHome, null); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenVersionDisplayNameFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | final class MavenVersionDisplayNameFormatter { 11 | 12 | private final String displayName; 13 | 14 | MavenVersionDisplayNameFormatter(String displayName) { 15 | this.displayName = displayName; 16 | } 17 | 18 | String format(String mavenVersion) { 19 | return this.displayName + '[' + mavenVersion + ']'; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenVersionTestInvocationContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import java.io.File; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import org.junit.jupiter.api.extension.Extension; 14 | import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 15 | 16 | final class MavenVersionTestInvocationContext implements TestTemplateInvocationContext { 17 | 18 | private final String mavenVersion; 19 | private final File mavenHome; 20 | private final MavenVersionDisplayNameFormatter formatter; 21 | 22 | MavenVersionTestInvocationContext(String mavenVersion, File mavenHome, MavenVersionDisplayNameFormatter formatter) { 23 | this.mavenVersion = mavenVersion; 24 | this.mavenHome = mavenHome; 25 | this.formatter = formatter; 26 | } 27 | 28 | @Override 29 | public String getDisplayName(int invocationIndex) { 30 | return this.formatter.format(this.mavenVersion); 31 | } 32 | 33 | @Override 34 | public List getAdditionalExtensions() { 35 | return Collections.singletonList(new MavenRuntimeBuilderParameterResolver(this.mavenHome)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /takari-plugin-testing/src/main/java/io/takari/maven/testing/executor/junit/MavenVersionsTestExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2024 Takari, Inc. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * https://www.eclipse.org/legal/epl-v10.html 7 | */ 8 | package io.takari.maven.testing.executor.junit; 9 | 10 | import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder; 11 | import io.takari.maven.testing.executor.MavenVersions; 12 | import java.io.File; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.stream.Stream; 16 | import org.junit.jupiter.api.extension.ExtensionConfigurationException; 17 | import org.junit.jupiter.api.extension.ExtensionContext; 18 | import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 19 | import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; 20 | import org.junit.runners.model.InitializationError; 21 | 22 | /** 23 | * Provides a {@link TestTemplateInvocationContext} with a {@link MavenRuntimeBuilder} 24 | * for each Maven version configured through {@link MavenVersions}. 25 | *

26 | * Not active if Maven home is forced through {@code -Dmaven.home}. 27 | * 28 | * @author Philippe Marschall 29 | */ 30 | final class MavenVersionsTestExtension implements TestTemplateInvocationContextProvider { 31 | 32 | @Override 33 | public boolean supportsTestTemplate(ExtensionContext context) { 34 | if (MavenHomeUtils.isForced()) { 35 | return false; 36 | } 37 | // @formatter:off 38 | return context.getTestClass() 39 | .map(clazz -> clazz.isAnnotationPresent(MavenVersions.class)) 40 | .orElse(false); 41 | // @formatter:on 42 | } 43 | 44 | @Override 45 | public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { 46 | String displayName = context.getDisplayName(); 47 | String[] versions = context.getTestClass() 48 | .orElseThrow() 49 | .getAnnotation(MavenVersions.class) 50 | .value(); 51 | List contexts = new ArrayList<>(); 52 | List errors = new ArrayList<>(); 53 | 54 | try { 55 | new MavenVersionResolver() { 56 | @Override 57 | protected void resolved(File mavenHome, String version) throws InitializationError { 58 | contexts.add(new MavenVersionTestInvocationContext( 59 | version, mavenHome, new MavenVersionDisplayNameFormatter(displayName))); 60 | } 61 | 62 | @Override 63 | protected void error(String version, Exception cause) { 64 | errors.add(new Exception("Could not resolve maven version " + version, cause)); 65 | } 66 | }.resolve(versions); 67 | } catch (Exception e) { 68 | throw new ExtensionConfigurationException("Could not resolve maven versions", e); 69 | } 70 | if (!errors.isEmpty()) { 71 | ExtensionConfigurationException extensionConfigurationException = 72 | new ExtensionConfigurationException("Could not resolve maven versions"); 73 | for (Throwable error : errors) { 74 | extensionConfigurationException.addSuppressed(error); 75 | } 76 | throw extensionConfigurationException; 77 | } 78 | return contexts.stream(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /testproperties.md: -------------------------------------------------------------------------------- 1 | test.properties 2 | =============== 3 | 4 | Takari plugin testing harness requires presence of `test.properties` file 5 | on project test classpath. This file is used to pass various aspects of the 6 | running "outer" build to the test jvm. This includes project groupId, 7 | artifactId and version, location of user settings.xml and local repository, 8 | and so on. 9 | 10 | For projects using takari-maven-plugin packaging type, `test.properties` 11 | file is generated by takari-lifecycle-plugin:testProperties goal bound to 12 | the project default lifecycle. No explicit configuration is necessary. 13 | 14 | For other project types, like takari-jar for example, generation of 15 | `test.properties` file must be explicitly configured in project pom.xml file. 16 | 17 | 18 | io.takari.maven.plugins 19 | takari-lifecycle-plugin 20 | ${takariLifecycleVersion} 21 | true 22 | 23 | 24 | testProperties 25 | process-test-resources 26 | 27 | testProperties 28 | 29 | 30 | 31 | 32 | --------------------------------------------------------------------------------