Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
.java-version
.checkstyle
.factorypath
repo/

# VSCode
.vscode/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.maven.api.DependencyScope;
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.di.Inject;
Expand Down Expand Up @@ -77,7 +78,6 @@
import org.apache.maven.impl.InternalSession;
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.model.v4.MavenTransformer;
import org.eclipse.aether.scope.DependencyScope;
import org.eclipse.aether.scope.ScopeManager;

/**
Expand Down Expand Up @@ -1157,6 +1157,25 @@ private void validate20RawDependencies(
}
}

// MNG-8750: New dependency scopes are only supported starting with modelVersion 4.1.0
// When using modelVersion 4.0.0, fail validation if one of the new scopes is present
if (!is41OrBeyond) {
String scope = dependency.getScope();
if (DependencyScope.COMPILE_ONLY.id().equals(scope)
|| DependencyScope.TEST_ONLY.id().equals(scope)
|| DependencyScope.TEST_RUNTIME.id().equals(scope)) {
addViolation(
problems,
Severity.ERROR,
Version.V20,
prefix + prefix2 + "scope",
SourceHint.dependencyManagementKey(dependency),
"scope '" + scope + "' is not supported with modelVersion 4.0.0; "
+ "use modelVersion 4.1.0 or remove this scope.",
dependency);
}
}

if (equals("LATEST", dependency.getVersion()) || equals("RELEASE", dependency.getVersion())) {
addViolation(
problems,
Expand Down Expand Up @@ -1272,7 +1291,7 @@ private void validateEffectiveDependencies(
SourceHint.dependencyManagementKey(dependency),
dependency,
scopeManager.getDependencyScopeUniverse().stream()
.map(DependencyScope::getId)
.map(org.eclipse.aether.scope.DependencyScope::getId)
.distinct()
.toArray(String[]::new),
false);
Expand All @@ -1282,7 +1301,7 @@ private void validateEffectiveDependencies(
ScopeManager scopeManager =
InternalSession.from(session).getSession().getScopeManager();
Set<String> scopes = scopeManager.getDependencyScopeUniverse().stream()
.map(DependencyScope::getId)
.map(org.eclipse.aether.scope.DependencyScope::getId)
.collect(Collectors.toCollection(HashSet::new));
scopes.add("import");
validateDependencyScope(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.it;

import java.io.File;
import java.io.IOException;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Integration tests for Maven 4 new dependency scopes: compile-only, test-only, and test-runtime.
*
* Verifies that:
* - compile-only dependencies appear in compile classpath but not runtime classpath
* - test-only dependencies appear in test compile classpath but not test runtime classpath
* - test-runtime dependencies appear in test runtime classpath but not test compile classpath
*
* Each test method runs a small test project (under src/test/resources/mng-8750-new-scopes)
* that writes out classpath files and asserts expected inclusion/exclusion behavior.
*/
public class MavenITmng8750NewScopesTest extends AbstractMavenIntegrationTestCase {

public MavenITmng8750NewScopesTest() {
super("[4.0.0,)");
}

@BeforeEach
void installDependencies() throws VerificationException, IOException {
File testDir = extractResources("/mng-8750-new-scopes");

File depsDir = new File(testDir, "deps");
Verifier deps = newVerifier(depsDir.getAbsolutePath());
deps.addCliArgument("install");
deps.execute();
deps.verifyErrorFreeLog();
}

/**
* compile-only: available during compilation only.
* Behavior validated by the test project:
* - Present on compile classpath
* - Not present on runtime classpath
* - Not transitive
*
* @throws Exception in case of failure
*/
@Test
public void testCompileOnlyScope() throws Exception {
File testDir = extractResources("/mng-8750-new-scopes");
File projectDir = new File(testDir, "compile-only-test");

Verifier verifier = newVerifier(projectDir.getAbsolutePath());
verifier.addCliArgument("clean");
verifier.addCliArgument("test");
verifier.execute();
verifier.verifyErrorFreeLog();

// Verify execution completed; detailed checks are within the test project
verifier.verifyErrorFreeLog();

// Verify classpath files were generated
File compileClasspath = new File(projectDir, "target/compile-classpath.txt");
File runtimeClasspath = new File(projectDir, "target/runtime-classpath.txt");

assertTrue(compileClasspath.exists(), "Compile classpath file should exist");
assertTrue(runtimeClasspath.exists(), "Runtime classpath file should exist");
}

/**
* test-only: available during test compilation only.
* Behavior validated by the test project:
* - Present on test compile classpath
* - Not present on test runtime classpath
* - Not transitive
*
* @throws Exception in case of failure
*/
@Test
public void testTestOnlyScope() throws Exception {
File testDir = extractResources("/mng-8750-new-scopes");
File projectDir = new File(testDir, "test-only-test");

Verifier verifier = newVerifier(projectDir.getAbsolutePath());
verifier.addCliArgument("clean");
verifier.addCliArgument("test");
verifier.execute();
verifier.verifyErrorFreeLog();

// Verify execution completed; detailed checks are within the test project
verifier.verifyErrorFreeLog();

// Verify classpath files were generated
File testCompileClasspath = new File(projectDir, "target/test-compile-classpath.txt");
File testRuntimeClasspath = new File(projectDir, "target/test-runtime-classpath.txt");

assertTrue(testCompileClasspath.exists(), "Test compile classpath file should exist");
assertTrue(testRuntimeClasspath.exists(), "Test runtime classpath file should exist");
}

/**
* test-runtime: available during test runtime only.
* Behavior validated by the test project:
* - Not present on test compile classpath
* - Present on test runtime classpath
* - Transitive
*
* @throws Exception in case of failure
*/
@Test
public void testTestRuntimeScope() throws Exception {
File testDir = extractResources("/mng-8750-new-scopes");
File projectDir = new File(testDir, "test-runtime-test");

Verifier verifier = newVerifier(projectDir.getAbsolutePath());
verifier.addCliArgument("clean");
verifier.addCliArgument("test");
verifier.execute();
verifier.verifyErrorFreeLog();

// Verify execution completed; detailed checks are within the test project
verifier.verifyErrorFreeLog();

// Verify classpath files were generated
File testCompileClasspath = new File(projectDir, "target/test-compile-classpath.txt");
File testRuntimeClasspath = new File(projectDir, "target/test-runtime-classpath.txt");

assertTrue(testCompileClasspath.exists(), "Test compile classpath file should exist");
assertTrue(testRuntimeClasspath.exists(), "Test runtime classpath file should exist");
}

/**
* Comprehensive scenario exercising all new scopes together.
* The test project asserts the expected inclusion/exclusion on all classpaths.
*
* @throws Exception in case of failure
*/
@Test
public void testAllNewScopesTogether() throws Exception {
File testDir = extractResources("/mng-8750-new-scopes");
File projectDir = new File(testDir, "comprehensive-test");

Verifier verifier = newVerifier(projectDir.getAbsolutePath());
verifier.addCliArgument("clean");
verifier.addCliArgument("test");
verifier.execute();
verifier.verifyErrorFreeLog();

// Verify execution completed; detailed checks are within the test project
verifier.verifyErrorFreeLog();

// Verify all classpath files were generated
File compileClasspath = new File(projectDir, "target/compile-classpath.txt");
File runtimeClasspath = new File(projectDir, "target/runtime-classpath.txt");
File testCompileClasspath = new File(projectDir, "target/test-compile-classpath.txt");
File testRuntimeClasspath = new File(projectDir, "target/test-runtime-classpath.txt");

assertTrue(compileClasspath.exists(), "Compile classpath file should exist");
assertTrue(runtimeClasspath.exists(), "Runtime classpath file should exist");
assertTrue(testCompileClasspath.exists(), "Test compile classpath file should exist");
assertTrue(testRuntimeClasspath.exists(), "Test runtime classpath file should exist");
}

/**
* Validation rule: modelVersion 4.0.0 must reject new scopes (compile-only, test-only, test-runtime).
* This test uses a POM with modelVersion 4.0.0 and new scopes and expects validation to fail.
*
* @throws Exception in case of failure
*/
@Test
public void testValidationFailureWithModelVersion40() throws Exception {
File testDir = extractResources("/mng-8750-new-scopes");
File projectDir = new File(testDir, "validation-failure-test");

Verifier verifier = newVerifier(projectDir.getAbsolutePath());
verifier.addCliArgument("clean");
verifier.addCliArgument("validate");

assertThrows(
VerificationException.class,
verifier::execute,
"Expected validation to fail when using new scopes with modelVersion 4.0.0");
String log = verifier.loadLogContent();
assertTrue(
log.contains("is not supported") || log.contains("Unknown scope"),
"Error should indicate unsupported/unknown scope");
}

/**
* Validation rule: modelVersion 4.1.0 (and later) accepts new scopes.
* This test uses a POM with modelVersion 4.1.0 and new scopes and expects validation to succeed.
*
* @throws Exception in case of failure
*/
@Test
public void testValidationSuccessWithModelVersion41() throws Exception {
File testDir = extractResources("/mng-8750-new-scopes");
File projectDir = new File(testDir, "validation-success-test");

Verifier verifier = newVerifier(projectDir.getAbsolutePath());
verifier.addCliArgument("clean");
verifier.addCliArgument("validate");
verifier.execute();
verifier.verifyErrorFreeLog();

// Verify that validation succeeded - no errors about unsupported scopes
String log = verifier.loadLogContent();
assertFalse(log.contains("is not supported"), "Validation should succeed with modelVersion 4.1.0");
assertFalse(log.contains("Unknown scope"), "No unknown scope errors should occur with modelVersion 4.1.0");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public TestSuiteOrdering() {
* the tests are to finishing. Newer tests are also more likely to fail, so this is
* a fail fast technique as well.
*/
suite.addTestSuite(MavenITmng8750NewScopesTest.class);
suite.addTestSuite(MavenITgh11055DIServiceInjectionTest.class);
suite.addTestSuite(MavenITgh11084ReactorReaderPreferConsumerPomTest.class);
suite.addTestSuite(MavenITgh10210SettingsXmlDecryptTest.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Re-include the pre-populated test repo for this IT, even though root .gitignore ignores 'repo/'
# The IT preference is to keep a pre-populated test repository in resources.
!.gitignore
!repo/
# But still ignore any transient build outputs within the repo if any appear
repo/**/target/

Loading
Loading