LUCENE-10328: Module path for compiling and running tests is wrong (#571)

This commit is contained in:
Dawid Weiss 2022-01-05 20:42:02 +01:00 committed by GitHub
parent c8651afde7
commit ff547e7bbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 848 additions and 333 deletions

View File

@ -57,7 +57,7 @@ allprojects {
outputDir = project.javadoc.destinationDir
}
if (project.path == ':lucene:luke' || project.path.endsWith(".tests")) {
if (project.path == ':lucene:luke' || !(project in rootProject.ext.mavenProjects)) {
// These projects are not part of the public API so we don't render their javadocs
// as part of the site's creation. A side-effect of this is that javadocs would not
// be linted for these projects. To avoid this, we connect the regular javadoc task
@ -165,26 +165,6 @@ configure(project(":lucene:test-framework")) {
project.tasks.withType(RenderJavadocTask) {
// TODO: fix missing javadocs
javadocMissingLevel = "class"
// TODO: clean up split packages
javadocMissingIgnore = [
"org.apache.lucene.analysis",
"org.apache.lucene.analysis.standard",
"org.apache.lucene.codecs",
"org.apache.lucene.codecs.blockterms",
"org.apache.lucene.codecs.bloom",
"org.apache.lucene.codecs.compressing",
"org.apache.lucene.codecs.uniformsplit",
"org.apache.lucene.codecs.uniformsplit.sharedterms",
"org.apache.lucene.geo",
"org.apache.lucene.index",
"org.apache.lucene.search",
"org.apache.lucene.search.similarities",
"org.apache.lucene.search.spans",
"org.apache.lucene.store",
"org.apache.lucene.util",
"org.apache.lucene.util.automaton",
"org.apache.lucene.util.fst"
]
}
}
@ -195,6 +175,13 @@ configure(project(":lucene:sandbox")) {
}
}
configure(project(":lucene:spatial-test-fixtures")) {
project.tasks.withType(RenderJavadocTask) {
// TODO: fix missing javadocs
javadocMissingLevel = "class"
}
}
configure(project(":lucene:misc")) {
project.tasks.withType(RenderJavadocTask) {
// TODO: fix missing javadocs

View File

@ -15,6 +15,8 @@
* limitations under the License.
*/
import java.nio.file.Path
// Configure miscellaneous aspects required for supporting the java module system layer.
// Debugging utilities.
@ -27,189 +29,162 @@ allprojects {
modularity.inferModulePath.set(false)
}
// Map convention configuration names to "modular" corresponding configurations.
Closure<String> moduleConfigurationNameFor = { String configurationName ->
return "module" + configurationName.capitalize().replace("Classpath", "Path")
}
//
// For each source set, create explicit configurations for declaring modular dependencies.
// These "modular" configurations correspond 1:1 to Gradle's conventions but have a 'module' prefix
// and a capitalized remaining part of the conventional name. For example, an 'api' configuration in
// the main source set would have a corresponding 'moduleApi' configuration for declaring modular
// dependencies.
//
// Gradle's java plugin "convention" configurations extend from their modular counterparts
// so all dependencies end up on classpath by default for backward compatibility with other
// tasks and gradle infrastructure.
//
// At the same time, we also know which dependencies (and their transitive graph of dependencies!)
// should be placed on module-path only.
//
// Note that an explicit configuration of modular dependencies also opens up the possibility of automatically
// validating whether the dependency configuration for a gradle project is consistent with the information in
// the module-info descriptor because there is a (nearly?) direct correspondence between the two:
//
// moduleApi - 'requires transitive'
// moduleImplementation - 'requires'
// moduleCompileOnly - 'requires static'
// Configure modular extensions for each source set.
//
project.sourceSets.all { SourceSet sourceSet ->
ConfigurationContainer configurations = project.configurations
// Create modular configurations for convention configurations.
Closure<Configuration> createModuleConfigurationForConvention = { String configurationName ->
Configuration conventionConfiguration = configurations.maybeCreate(configurationName)
Configuration moduleConfiguration = configurations.maybeCreate(moduleConfigurationNameFor(configurationName))
moduleConfiguration.canBeConsumed(false)
moduleConfiguration.canBeResolved(false)
conventionConfiguration.extendsFrom(moduleConfiguration)
project.logger.info("Created module configuration for '${conventionConfiguration.name}': ${moduleConfiguration.name}")
return moduleConfiguration
}
Configuration moduleApi = createModuleConfigurationForConvention(sourceSet.apiConfigurationName)
Configuration moduleImplementation = createModuleConfigurationForConvention(sourceSet.implementationConfigurationName)
moduleImplementation.extendsFrom(moduleApi)
Configuration moduleRuntimeOnly = createModuleConfigurationForConvention(sourceSet.runtimeOnlyConfigurationName)
Configuration moduleCompileOnly = createModuleConfigurationForConvention(sourceSet.compileOnlyConfigurationName)
// sourceSet.compileOnlyApiConfigurationName // This seems like a very esoteric use case, leave out.
// Set up compilation module path configuration combining corresponding convention configurations.
Closure<Configuration> createResolvableModuleConfiguration = { String configurationName ->
Configuration conventionConfiguration = configurations.maybeCreate(configurationName)
Configuration moduleConfiguration = configurations.maybeCreate(
moduleConfigurationNameFor(conventionConfiguration.name))
moduleConfiguration.canBeConsumed(false)
moduleConfiguration.canBeResolved(true)
moduleConfiguration.attributes {
// Prefer class folders over JARs. The exception is made for tests projects which require a composition
// of classes and resources, otherwise split into two folders.
if (project.name.endsWith(".tests")) {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.JAR))
} else {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.CLASSES))
}
}
project.logger.info("Created resolvable module configuration for '${conventionConfiguration.name}': ${moduleConfiguration.name}")
return moduleConfiguration
}
Configuration compileModulePathConfiguration = createResolvableModuleConfiguration(sourceSet.compileClasspathConfigurationName)
compileModulePathConfiguration.extendsFrom(moduleCompileOnly, moduleImplementation)
Configuration runtimeModulePathConfiguration = createResolvableModuleConfiguration(sourceSet.runtimeClasspathConfigurationName)
runtimeModulePathConfiguration.extendsFrom(moduleRuntimeOnly, moduleImplementation)
// Create and register a source set extension for manipulating classpath/ module-path
ModularPathsExtension modularPaths = new ModularPathsExtension(project, sourceSet,
compileModulePathConfiguration,
runtimeModulePathConfiguration)
ModularPathsExtension modularPaths = new ModularPathsExtension(project, sourceSet)
sourceSet.extensions.add("modularPaths", modularPaths)
// Customized the JavaCompile for this source set so that it has proper module path.
// LUCENE-10344: We have to provide a special-case extension for ECJ because it does not
// support all of the module-specific javac options.
ModularPathsExtension modularPathsForEcj = modularPaths
if (sourceSet.name == SourceSet.TEST_SOURCE_SET_NAME && project.path in [
":lucene:spatial-extras",
":lucene:spatial3d",
]) {
modularPathsForEcj = modularPaths.cloneWithMode(ModularPathsExtension.Mode.CLASSPATH_ONLY)
}
sourceSet.extensions.add("modularPathsForEcj", modularPathsForEcj)
// TODO: the tests of these projects currently don't compile or work in
// module-path mode. Make the modular paths extension use class path only.
if (sourceSet.name == SourceSet.TEST_SOURCE_SET_NAME && project.path in [
// Circular dependency between artifacts or source set outputs,
// causing package split issues at runtime.
":lucene:core",
":lucene:codecs",
":lucene:test-framework",
]) {
modularPaths.mode = ModularPathsExtension.Mode.CLASSPATH_ONLY
}
// Configure the JavaCompile task associated with this source set.
tasks.named(sourceSet.getCompileJavaTaskName()).configure({ JavaCompile task ->
task.dependsOn modularPaths.compileModulePathConfiguration
// LUCENE-10327: don't allow gradle to emit an empty sourcepath as it would break compilation.
// LUCENE-10327: don't allow gradle to emit an empty sourcepath as it would break
// compilation of modules.
task.options.setSourcepath(sourceSet.java.sourceDirectories)
// Add modular dependencies and their transitive dependencies to module path.
task.options.compilerArgumentProviders.add((CommandLineArgumentProvider) {
def modularPathFiles = modularPaths.compileModulePathConfiguration.files
def extraArgs = []
if (!modularPathFiles.isEmpty()) {
if (!modularPaths.hasModuleDescriptor()) {
// We're compiling a non-module so we'll bring everything on module path in
// otherwise things wouldn't be part of the resolved module graph.
extraArgs += ["--add-modules", "ALL-MODULE-PATH"]
}
extraArgs += ["--module-path", modularPathFiles.join(File.pathSeparator)]
}
task.logger.info("Module path for ${task.path}:\n " + modularPathFiles.sort().join("\n "))
return extraArgs
})
task.options.compilerArgumentProviders.add(modularPaths.compilationArguments)
// LUCENE-10304: if we modify the classpath here, IntelliJ no longer sees the dependencies as compile-time
// dependencies, don't know why.
if (!rootProject.ext.isIdea) {
// Modify the default classpath by removing anything already placed on module path.
// This could be done in a fancier way but a set difference is just fine for us here. Use a lazy
// provider to delay computation of the actual path.
task.classpath = files({ ->
def trimmedClasspath = sourceSet.compileClasspath - modularPaths.compileModulePathConfiguration
task.logger.info("Class path for ${task.path}:\n " + trimmedClasspath.files.sort().join("\n "))
return trimmedClasspath
})
task.classpath = modularPaths.compilationClasspath
}
doFirst {
modularPaths.logCompilationPaths(logger)
}
})
// For source sets that contain a module descriptor, configure a jar task that combines
// classes and resources into a single module.
if (sourceSet.name != SourceSet.MAIN_SOURCE_SET_NAME) {
tasks.maybeCreate(sourceSet.getJarTaskName(), org.gradle.jvm.tasks.Jar).configure({
archiveClassifier = sourceSet.name
from(sourceSet.output)
})
}
}
// Connect modular configurations between their "test" and "main" source sets, this reflects
// the conventions set by the Java plugin.
project.configurations {
moduleTestApi.extendsFrom moduleApi
moduleTestImplementation.extendsFrom moduleImplementation
moduleTestRuntimeOnly.extendsFrom moduleRuntimeOnly
moduleTestCompileOnly.extendsFrom moduleCompileOnly
}
// Gradle's java plugin sets the compile and runtime classpath to be a combination
// of configuration dependencies and source set's outputs. For source sets with modules,
// this leads to split class and resource folders.
//
// We tweak the default source set path configurations here by assembling jar task outputs
// of the respective source set, instead of their source set output folders. We also attach
// the main source set's jar to the modular test implementation configuration.
SourceSet mainSourceSet = project.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
boolean mainIsModular = mainSourceSet.modularPaths.hasModuleDescriptor()
boolean mainIsEmpty = mainSourceSet.allJava.isEmpty()
SourceSet testSourceSet = project.sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME)
boolean testIsModular = testSourceSet.modularPaths.hasModuleDescriptor()
// LUCENE-10304: if we modify the classpath here, IntelliJ no longer sees the dependencies as compile-time
// dependencies, don't know why.
if (!rootProject.ext.isIdea) {
def jarTask = project.tasks.getByName(mainSourceSet.getJarTaskName())
def testJarTask = project.tasks.getByName(testSourceSet.getJarTaskName())
// Consider various combinations of module/classpath configuration between the main and test source set.
if (testIsModular) {
if (mainIsModular || mainIsEmpty) {
// If the main source set is empty, skip the jar task.
def jarTaskOutputs = mainIsEmpty ? [] : jarTask.outputs
// Fully modular tests - must have no split packages, proper access, etc.
// Work around the split classes/resources problem by adjusting classpaths to
// rely on JARs rather than source set output folders.
testSourceSet.compileClasspath = project.objects.fileCollection().from(
jarTaskOutputs,
project.configurations.getByName(testSourceSet.getCompileClasspathConfigurationName()),
)
testSourceSet.runtimeClasspath = project.objects.fileCollection().from(
jarTaskOutputs,
testJarTask.outputs,
project.configurations.getByName(testSourceSet.getRuntimeClasspathConfigurationName()),
)
project.dependencies {
moduleTestImplementation files(jarTaskOutputs)
moduleTestRuntimeOnly files(testJarTask.outputs)
}
} else {
// This combination simply does not make any sense (in my opinion).
throw GradleException("Test source set is modular and main source set is class-based, this makes no sense: " + project.path)
}
} else {
if (mainIsModular) {
// This combination is a potential candidate for patching the main sourceset's module with test classes. I could
// not resolve all the difficulties that arise when you try to do it though:
// - either a separate module descriptor is needed that opens test packages, adds dependencies via requires clauses
// or a series of jvm arguments (--add-reads, --add-opens, etc.) has to be generated and maintained. This is
// very low-level (ECJ doesn't support a full set of these instructions, for example).
//
// Fall back to classpath mode.
} else {
// This is the 'plain old classpath' mode: neither the main source set nor the test set are modular.
}
}
}
//
// Configure the (default) test task to use module paths.
// Configures a Test task associated with the provided source set to use module paths.
//
// There is no explicit connection between source sets and test tasks so there is no way (?)
// to do this automatically, convention-style.
//
// This closure can be used to configure a different task, with a different source set, should we
// have the need for it.
Closure<Void> configureTestTaskForSourceSet = { Test task, SourceSet sourceSet ->
task.configure {
Configuration modulePath = task.project.configurations.maybeCreate(
moduleConfigurationNameFor(sourceSet.getRuntimeClasspathConfigurationName()))
ModularPathsExtension modularPaths = sourceSet.modularPaths
task.dependsOn modulePath
dependsOn modularPaths
// Add modular dependencies and their transitive dependencies to module path.
task.jvmArgumentProviders.add((CommandLineArgumentProvider) {
def extraArgs = []
jvmArgumentProviders.add(modularPaths.runtimeArguments)
// Determine whether the source set classes themselves should be appended
// to classpath or module path.
boolean sourceSetIsAModule = sourceSet.modularPaths.hasModuleDescriptor()
// Modify the default classpath.
classpath = modularPaths.runtimeClasspath
if (!modulePath.isEmpty() || sourceSetIsAModule) {
if (sourceSetIsAModule) {
// Add source set outputs to module path.
extraArgs += ["--module-path", (modulePath + sourceSet.output.classesDirs).files.join(File.pathSeparator)]
// Ideally, we should only add the sourceset's module here, everything else would be resolved via the
// module descriptor. But this would require parsing the module descriptor and may cause JVM version conflicts
// so keeping it simple.
extraArgs += ["--add-modules", "ALL-MODULE-PATH"]
} else {
extraArgs += ["--module-path", modulePath.files.join(File.pathSeparator)]
// In this case we're running a non-module against things on the module path so let's bring in
// everything on module path into the resolution graph.
extraArgs += ["--add-modules", "ALL-MODULE-PATH"]
}
}
task.logger.info("Module path for ${task.path}:\n " + modulePath.files.sort().join("\n "))
return extraArgs
})
// Modify the default classpath by removing anything already placed on module path.
// This could be done in a fancier way but a set difference is just fine for us here. Use a lazy
// provider to delay computation of the actual path.
task.classpath = files({ ->
def trimmedClasspath = sourceSet.runtimeClasspath - modulePath
boolean sourceSetIsAModule = sourceSet.modularPaths.hasModuleDescriptor()
if (sourceSetIsAModule) {
// also subtract the sourceSet's output directories.
trimmedClasspath = trimmedClasspath - sourceSet.output.classesDirs
}
task.logger.info("Class path for ${task.path}:\n " + trimmedClasspath.files.sort().join("\n "))
return trimmedClasspath
})
doFirst {
modularPaths.logRuntimePaths(logger)
}
}
}
@ -237,19 +212,233 @@ allprojects {
}
class ModularPathsExtension {
//
// For a source set, create explicit configurations for declaring modular dependencies.
//
// These "modular" configurations correspond 1:1 to Gradle's conventions but have a 'module' prefix
// and a capitalized remaining part of the conventional name. For example, an 'api' configuration in
// the main source set would have a corresponding 'moduleApi' configuration for declaring modular
// dependencies.
//
// Gradle's java plugin "convention" configurations extend from their modular counterparts
// so all dependencies end up on classpath by default for backward compatibility with other
// tasks and gradle infrastructure.
//
// At the same time, we also know which dependencies (and their transitive graph of dependencies!)
// should be placed on module-path only.
//
// Note that an explicit configuration of modular dependencies also opens up the possibility of automatically
// validating whether the dependency configuration for a gradle project is consistent with the information in
// the module-info descriptor because there is a (nearly?) direct correspondence between the two:
//
// moduleApi - 'requires transitive'
// moduleImplementation - 'requires'
// moduleCompileOnly - 'requires static'
//
class ModularPathsExtension implements Cloneable, Iterable<Object> {
/**
* Determines how paths are split between module path and classpath.
*/
enum Mode {
/**
* Dependencies and source set outputs are placed on classpath, even if declared on modular
* configurations. This would be the 'default' backward-compatible mode.
*/
CLASSPATH_ONLY,
/**
* Dependencies from modular configurations are placed on module path. Source set outputs
* are placed on classpath.
*/
DEPENDENCIES_ON_MODULE_PATH
}
Project project
SourceSet sourceSet
Configuration compileModulePathConfiguration
Configuration runtimeModulePathConfiguration
Configuration modulePatchOnlyConfiguration
ModularPathsExtension(Project project, SourceSet sourceSet,
Configuration compileModulePathConfiguration,
Configuration runtimeModulePathConfiguration) {
this.project = project
// The mode of splitting paths for this source set.
Mode mode = Mode.DEPENDENCIES_ON_MODULE_PATH
// More verbose debugging for paths.
private boolean debugPaths
/**
* A list of module name - path provider entries that will be converted
* into {@code --patch-module} options.
*/
private List<Map.Entry<String, Provider<Path>>> modulePatches = new ArrayList<>()
ModularPathsExtension(Project project, SourceSet sourceSet) {
this.project = project
this.sourceSet = sourceSet
this.compileModulePathConfiguration = compileModulePathConfiguration
this.runtimeModulePathConfiguration = runtimeModulePathConfiguration
debugPaths = Boolean.parseBoolean(project.propertyOrDefault('build.debug.paths', 'false'))
ConfigurationContainer configurations = project.configurations
// Create modular configurations for gradle's java plugin convention configurations.
Configuration moduleApi = createModuleConfigurationForConvention(sourceSet.apiConfigurationName)
Configuration moduleImplementation = createModuleConfigurationForConvention(sourceSet.implementationConfigurationName)
Configuration moduleRuntimeOnly = createModuleConfigurationForConvention(sourceSet.runtimeOnlyConfigurationName)
Configuration moduleCompileOnly = createModuleConfigurationForConvention(sourceSet.compileOnlyConfigurationName)
// Apply hierarchy relationships to modular configurations.
moduleImplementation.extendsFrom(moduleApi)
// Patched modules have to end up in the implementation configuration for IDEs, which
// otherwise get terribly confused.
Configuration modulePatchOnly = createModuleConfigurationForConvention(
SourceSet.isMain(sourceSet) ? "patchOnly" : sourceSet.name + "PatchOnly")
modulePatchOnly.canBeResolved(true)
moduleImplementation.extendsFrom(modulePatchOnly)
this.modulePatchOnlyConfiguration = modulePatchOnly
// This part of convention configurations seems like a very esoteric use case, leave out for now.
// sourceSet.compileOnlyApiConfigurationName
// We have to ensure configurations are using assembled resources and classes (jar variant) as a single
// module can't be expanded into multiple folders.
Closure<Void> ensureJarVariant = { Configuration c ->
c.attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR))
}
// Set up compilation module path configuration combining corresponding convention configurations.
Closure<Configuration> createResolvableModuleConfiguration = { String configurationName ->
Configuration conventionConfiguration = configurations.maybeCreate(configurationName)
Configuration moduleConfiguration = configurations.maybeCreate(moduleConfigurationNameFor(conventionConfiguration.name))
moduleConfiguration.canBeConsumed(false)
moduleConfiguration.canBeResolved(true)
ensureJarVariant(moduleConfiguration)
project.logger.info("Created resolvable module configuration for '${conventionConfiguration.name}': ${moduleConfiguration.name}")
return moduleConfiguration
}
ensureJarVariant(configurations.maybeCreate(sourceSet.compileClasspathConfigurationName))
ensureJarVariant(configurations.maybeCreate(sourceSet.runtimeClasspathConfigurationName))
this.compileModulePathConfiguration = createResolvableModuleConfiguration(sourceSet.compileClasspathConfigurationName)
compileModulePathConfiguration.extendsFrom(moduleCompileOnly, moduleImplementation)
this.runtimeModulePathConfiguration = createResolvableModuleConfiguration(sourceSet.runtimeClasspathConfigurationName)
runtimeModulePathConfiguration.extendsFrom(moduleRuntimeOnly, moduleImplementation)
}
/**
* Adds {@code --patch-module} option for the provided module name and the provider of a
* folder or JAR file.
*
* @param moduleName
* @param pathProvider
*/
void patchModule(String moduleName, Provider<Path> pathProvider) {
modulePatches.add(Map.entry(moduleName, pathProvider));
}
private FileCollection getCompilationModulePath() {
if (mode == Mode.CLASSPATH_ONLY) {
return project.files()
}
return compileModulePathConfiguration - modulePatchOnlyConfiguration
}
private FileCollection getRuntimeModulePath() {
if (mode == Mode.CLASSPATH_ONLY) {
if (hasModuleDescriptor()) {
// The source set is itself a module.
throw new GradleException("Source set contains a module but classpath-only" +
" dependencies requested: ${project.path}, source set '${sourceSet.name}'")
}
return project.files()
}
return runtimeModulePathConfiguration - modulePatchOnlyConfiguration
}
FileCollection getCompilationClasspath() {
if (mode == Mode.CLASSPATH_ONLY) {
return sourceSet.compileClasspath
}
// Modify the default classpath by removing anything already placed on module path.
// Use a lazy provider to delay computation.
project.files({ ->
return sourceSet.compileClasspath - compileModulePathConfiguration - modulePatchOnlyConfiguration
})
}
CommandLineArgumentProvider getCompilationArguments() {
return new CommandLineArgumentProvider() {
@Override
Iterable<String> asArguments() {
FileCollection modulePath = ModularPathsExtension.this.compilationModulePath
if (modulePath.isEmpty()) {
return []
}
ArrayList<String> extraArgs = []
extraArgs += ["--module-path", modulePath.join(File.pathSeparator)]
if (!hasModuleDescriptor()) {
// We're compiling what appears to be a non-module source set so we'll
// bring everything on module path in the resolution graph,
// otherwise modular dependencies wouldn't be part of the resolved module graph and this
// would result in class-not-found compilation problems.
extraArgs += ["--add-modules", "ALL-MODULE-PATH"]
}
// Add module-patching.
extraArgs += getPatchModuleArguments(modulePatches)
return extraArgs
}
}
}
FileCollection getRuntimeClasspath() {
if (mode == Mode.CLASSPATH_ONLY) {
return sourceSet.runtimeClasspath
}
// Modify the default classpath by removing anything already placed on module path.
// Use a lazy provider to delay computation.
project.files({ ->
return sourceSet.runtimeClasspath - runtimeModulePath - modulePatchOnlyConfiguration
})
}
CommandLineArgumentProvider getRuntimeArguments() {
return new CommandLineArgumentProvider() {
@Override
Iterable<String> asArguments() {
FileCollection modulePath = ModularPathsExtension.this.runtimeModulePath
if (modulePath.isEmpty()) {
return []
}
def extraArgs = []
// Add source set outputs to module path.
extraArgs += ["--module-path", modulePath.files.join(File.pathSeparator)]
// Ideally, we should only add the sourceset's module here, everything else would be resolved via the
// module descriptor. But this would require parsing the module descriptor and may cause JVM version conflicts
// so keeping it simple.
extraArgs += ["--add-modules", "ALL-MODULE-PATH"]
// Add module-patching.
extraArgs += getPatchModuleArguments(modulePatches)
return extraArgs
}
}
}
boolean hasModuleDescriptor() {
@ -257,4 +446,86 @@ class ModularPathsExtension {
.map(dir -> new File(dir, "module-info.java"))
.anyMatch(file -> file.exists())
}
private List<String> getPatchModuleArguments(List<Map.Entry<String, Provider<Path>>> patches) {
def args = []
patches.each {
args.add("--patch-module");
args.add(it.key + "=" + it.value.get())
}
return args
}
private static String toList(FileCollection files) {
return files.isEmpty() ? " [empty]" : ("\n " + files.sort().join("\n "))
}
private static String toList(List<Map.Entry<String, Provider<Path>>> patches) {
return patches.isEmpty() ? " [empty]" : ("\n " + patches.collect {"${it.key}=${it.value.get()}"}.join("\n "))
}
public void logCompilationPaths(Logger logger) {
def value = "Modular extension, compilation paths, source set=${sourceSet.name}${hasModuleDescriptor() ? " (module)" : ""}, mode=${mode}:\n" +
" Module path:${toList(compilationModulePath)}\n" +
" Class path: ${toList(compilationClasspath)}\n" +
" Patches: ${toList(modulePatches)}"
if (debugPaths) {
logger.lifecycle(value)
} else {
logger.info(value)
}
}
public void logRuntimePaths(Logger logger) {
def value = "Modular extension, runtime paths, source set=${sourceSet.name}${hasModuleDescriptor() ? " (module)" : ""}, mode=${mode}:\n" +
" Module path:${toList(runtimeModulePath)}\n" +
" Class path: ${toList(runtimeClasspath)}\n" +
" Patches : ${toList(modulePatches)}"
if (debugPaths) {
logger.lifecycle(value)
} else {
logger.info(value)
}
}
public ModularPathsExtension clone() {
return (ModularPathsExtension) super.clone()
}
ModularPathsExtension cloneWithMode(Mode newMode) {
def cloned = this.clone()
cloned.mode = newMode
return cloned
}
// Map convention configuration names to "modular" corresponding configurations.
static String moduleConfigurationNameFor(String configurationName) {
return "module" + configurationName.capitalize().replace("Classpath", "Path")
}
// Create module configuration for the corresponding convention configuration.
private Configuration createModuleConfigurationForConvention(String configurationName) {
ConfigurationContainer configurations = project.configurations
Configuration conventionConfiguration = configurations.maybeCreate(configurationName)
Configuration moduleConfiguration = configurations.maybeCreate(moduleConfigurationNameFor(configurationName))
moduleConfiguration.canBeConsumed(false)
moduleConfiguration.canBeResolved(false)
conventionConfiguration.extendsFrom(moduleConfiguration)
project.logger.info("Created module configuration for '${conventionConfiguration.name}': ${moduleConfiguration.name}")
return moduleConfiguration
}
/**
* Provide internal dependencies for tasks willing to depend on this modular paths object.
*/
@Override
Iterator<Object> iterator() {
return [
compileModulePathConfiguration,
runtimeModulePathConfiguration
].iterator()
}
}

View File

@ -38,7 +38,9 @@ configure(rootProject) {
// Exclude the parent container project for analysis modules (no artifacts).
":lucene:analysis",
// Exclude the native module.
":lucene:misc:native"
":lucene:misc:native",
// Exclude test fixtures.
":lucene:spatial-test-fixtures"
]
// Exclude all subprojects that are modular test projects and those explicitly

View File

@ -33,14 +33,20 @@ configure(rootProject) {
def currentJavaVersion = JavaVersion.current()
if (currentJavaVersion < minJavaVersion) {
throw new GradleException("At least Java ${minJavaVersion} is required, you are running Java ${currentJavaVersion} "
+ "[${System.getProperty('java.vm.name')} ${System.getProperty('java.vm.version')}]")
+ "[${System.getProperty('java.vm.name')} ${System.getProperty('java.vm.version')}]")
}
// If we're regenerating the wrapper, skip the check.
if (!gradle.startParameter.taskNames.contains("wrapper")) {
def currentGradleVersion = GradleVersion.current()
if (currentGradleVersion != GradleVersion.version(expectedGradleVersion)) {
throw new GradleException("Gradle ${expectedGradleVersion} is required (hint: use the gradlew script): this gradle is ${currentGradleVersion}")
if (currentGradleVersion.baseVersion == GradleVersion.version(expectedGradleVersion).baseVersion) {
logger.warn("Gradle ${expectedGradleVersion} is required but base version of this gradle matches, proceeding (" +
"this gradle is ${currentGradleVersion})")
} else {
throw new GradleException("Gradle ${expectedGradleVersion} is required (hint: use the gradlew script): " +
"this gradle is ${currentGradleVersion}")
}
}
}
}

View File

@ -74,42 +74,36 @@ allprojects {
args += [ "-properties", file("${resources}/ecj.javadocs.prefs").absolutePath ]
// We depend on modular paths.
def modularPaths = sourceSet.modularPaths
dependsOn modularPaths.compileModulePathConfiguration
def modularPaths = sourceSet.modularPathsForEcj
dependsOn modularPaths
// Add modular dependencies and their transitive dependencies to module path.
task.argumentProviders.add(modularPaths.compilationArguments)
// Add classpath, if needed.
task.argumentProviders.add((CommandLineArgumentProvider) {
// Add classpath locations in a lazy provider (can't resolve the
// configuration at evaluation time). Filter out non-existing entries
// (output folders for non-existing input source dirs like resources).
FileCollection cpath = modularPaths.compilationClasspath.filter { p -> p.exists() }
if (!cpath.isEmpty()) {
return ["-classpath", cpath.join(File.pathSeparator)]
} else {
return []
}
})
// Place input files in an external file to dodge command line argument
// limits. We could pass a directory but ecj seems to be buggy: when it
// encounters a module-info.java file it no longer compiles other source files.
def inputsFile = file("${tmpDst}/ecj-inputs.txt")
task.argumentProviders.add((CommandLineArgumentProvider) {
// Add modular dependencies and their transitive dependencies to module path.
def modularPathFiles = modularPaths.compileModulePathConfiguration.files
def extraArgs = []
if (!modularPathFiles.isEmpty()) {
if (!modularPaths.hasModuleDescriptor()) {
// We're compiling a non-module so we'll bring everything on module path in
// otherwise things wouldn't be part of the resolved module graph.
extraArgs += ["--add-modules", "ALL-MODULE-PATH"]
}
extraArgs += ["--module-path", modularPathFiles.join(File.pathSeparator)]
}
// Add classpath locations in a lazy provider (can't resolve the
// configuration at evaluation time). Filter out non-existing entries
// (output folders for non-existing input source dirs like resources).
def cpath = sourceSet.compileClasspath.filter { p -> p.exists() }
cpath = cpath - modularPathFiles
if (!cpath.isEmpty()) {
extraArgs += ["-classpath", cpath.join(File.pathSeparator)]
}
extraArgs += ["@" + inputsFile.absolutePath]
return extraArgs
return ["@" + inputsFile.absolutePath]
})
doFirst {
modularPaths.logCompilationPaths(logger)
tmpDst.mkdirs()
// escape filename accoring to ECJ's rules:
@ -118,7 +112,12 @@ allprojects {
def escapeFileName = { String s -> s.replaceAll(/ +/, /"$0"/) }
inputsFile.setText(
srcDirs.collectMany { dir ->
project.fileTree(dir: dir, include: "**/*.java" ).files
project.fileTree(
dir: dir,
include: "**/*.java",
// Exclude the benchmark class with dependencies on nekohtml, which causes module-classpath conflicts and breaks ecj.
exclude: "**/DemoHTMLParser.java"
).files
}
// Try to sort all input files; a side-effect of this should be that module-info.java
// is placed first on the list, which works around ECJ bug:

View File

@ -154,12 +154,11 @@ class ValidateSourcePatternsTask extends DefaultTask {
(~$/\n\s*var\s+.*=.*<>.*/$) : 'Diamond operators should not be used with var'
]
def found = 0;
def violations = new TreeSet();
def reportViolation = { f, name ->
logger.error('{}: {}', name, f);
violations.add(name);
found++;
String msg = String.format(Locale.ROOT, "%s: %s", f, name)
logger.error(msg)
violations.add(msg)
}
def javadocsPattern = ~$/(?sm)^\Q/**\E(.*?)\Q*/\E/$;
@ -258,9 +257,10 @@ class ValidateSourcePatternsTask extends DefaultTask {
}
progress.completed()
if (found) {
throw new GradleException(String.format(Locale.ENGLISH, 'Found %d violations in source files (%s).',
found, violations.join(', ')));
if (!violations.isEmpty()) {
throw new GradleException(String.format(Locale.ENGLISH, 'Found %d source violation(s):\n %s',
violations.size(),
violations.join('\n ')))
}
}
}

View File

@ -21,7 +21,7 @@ description = 'Analyzers for indexing content in different languages and domains
dependencies {
moduleApi project(':lucene:core')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}
// Fetch the data and enable regression tests against woorm/ libreoffice dictionaries.

View File

@ -25,5 +25,5 @@ dependencies {
moduleApi 'com.ibm.icu:icu4j'
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -23,5 +23,5 @@ dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:analysis:common')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -21,8 +21,5 @@ description = 'Module tests for :lucene:analysis:morfologik'
dependencies {
moduleTestImplementation project(':lucene:analysis:morfologik')
moduleTestImplementation("junit:junit", {
exclude group: "org.hamcrest"
})
moduleTestImplementation "org.hamcrest:hamcrest"
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -21,7 +21,7 @@ module org.apache.lucene.analysis.morfologik.tests {
requires org.apache.lucene.core;
requires org.apache.lucene.analysis.common;
requires org.apache.lucene.analysis.morfologik;
requires junit;
requires org.apache.lucene.test_framework;
exports org.apache.lucene.analysis.morfologik.tests;
}

View File

@ -19,28 +19,24 @@ package org.apache.lucene.analysis.morfologik.tests;
import org.apache.lucene.analysis.morfologik.MorfologikAnalyzer;
import org.apache.lucene.analysis.uk.UkrainianMorfologikAnalyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Assert;
import org.junit.Test;
public class TestMorfologikAnalyzer {
@Test
public class TestMorfologikAnalyzer extends LuceneTestCase {
public void testMorfologikAnalyzerLoads() {
var analyzer = new MorfologikAnalyzer();
Assert.assertNotNull(analyzer);
}
@Test
public void testUkrainianMorfologikAnalyzerLoads() {
var analyzer = new UkrainianMorfologikAnalyzer();
Assert.assertNotNull(analyzer);
}
@Test
public void testWeAreModule() {
Assert.assertTrue(this.getClass().getModule().isNamed());
}
@Test
public void testLuceneIsAModule() {
Assert.assertTrue(IndexWriter.class.getModule().isNamed());
}

View File

@ -27,5 +27,5 @@ dependencies {
moduleImplementation 'org.carrot2:morfologik-polish'
moduleImplementation 'ua.net.nlp:morfologik-ukrainian-search'
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -23,6 +23,6 @@ dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:analysis:common')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -24,5 +24,5 @@ dependencies {
moduleApi project(':lucene:analysis:common')
moduleApi 'org.apache.opennlp:opennlp-tools'
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -25,6 +25,6 @@ dependencies {
moduleApi 'commons-codec:commons-codec'
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -23,5 +23,5 @@ dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:analysis:common')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -23,5 +23,5 @@ dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:analysis:common')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -22,5 +22,5 @@ description = 'Codecs for older versions of Lucene'
dependencies {
moduleApi project(':lucene:core')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -36,11 +36,18 @@ dependencies {
moduleImplementation "org.locationtech.spatial4j:spatial4j"
moduleImplementation ("net.sourceforge.nekohtml:nekohtml", {
exclude module: "xml-apis"
// LUCENE-10337: Exclude xercesImpl from module path because it has split packages with the JDK (!)
exclude module: "xercesImpl"
})
// LUCENE-10337: Include xercesImpl on regular classpath where it won't cause conflicts.
implementation ("xerces:xercesImpl", {
exclude module: "xml-apis"
})
moduleRuntimeOnly project(':lucene:analysis:icu')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}
// We add 'conf' to resources because we validate *.alg script correctness in one of the tests.

View File

@ -25,7 +25,7 @@ dependencies {
moduleImplementation project(':lucene:queries')
moduleImplementation project(':lucene:grouping')
testImplementation project(':lucene:test-framework')
testImplementation project(':lucene:analysis:common')
testImplementation project(':lucene:codecs')
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:analysis:common')
moduleTestImplementation project(':lucene:codecs')
}

View File

@ -21,5 +21,5 @@ description = 'Lucene codecs and postings formats'
dependencies {
moduleImplementation project(':lucene:core')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -15,13 +15,14 @@
* limitations under the License.
*/
package org.apache.lucene.codecs.lucene90;
package org.apache.lucene.codecs.lucene90.tests;
/** Test utility class to create mock {@link Lucene90PostingsFormat.IntBlockTermState}. */
import org.apache.lucene.codecs.lucene90.Lucene90PostingsFormat.IntBlockTermState;
/** Test utility class to create mock {@link IntBlockTermState}. */
public class MockTermStateFactory {
/** Creates an empty {@link Lucene90PostingsFormat.IntBlockTermState}. */
public static Lucene90PostingsFormat.IntBlockTermState create() {
return new Lucene90PostingsFormat.IntBlockTermState();
/** Creates an empty {@link IntBlockTermState}. */
public static IntBlockTermState create() {
return new IntBlockTermState();
}
}

View File

@ -19,7 +19,7 @@ package org.apache.lucene.codecs.uniformsplit;
import java.io.IOException;
import java.util.Collections;
import org.apache.lucene.codecs.lucene90.MockTermStateFactory;
import org.apache.lucene.codecs.lucene90.tests.MockTermStateFactory;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.IndexOptions;

View File

@ -27,7 +27,7 @@ import java.util.Map;
import java.util.Set;
import org.apache.lucene.codecs.BlockTermState;
import org.apache.lucene.codecs.PostingsReaderBase;
import org.apache.lucene.codecs.lucene90.MockTermStateFactory;
import org.apache.lucene.codecs.lucene90.tests.MockTermStateFactory;
import org.apache.lucene.codecs.uniformsplit.BlockHeader;
import org.apache.lucene.codecs.uniformsplit.BlockLine;
import org.apache.lucene.codecs.uniformsplit.FSTDictionary;

View File

@ -21,8 +21,5 @@ description = 'Module tests for :lucene:core'
dependencies {
moduleTestImplementation project(':lucene:core')
moduleTestImplementation("junit:junit", {
exclude group: "org.hamcrest"
})
moduleTestImplementation "org.hamcrest:hamcrest"
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -0,0 +1,24 @@
/*
* 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.
*/
/**
* A placeholder module descriptor just so that we can check whether referencing it from the actual
* test module works.
*/
module org.apache.lucene.core.tests.main {
exports org.apache.lucene.core.tests.main;
}

View File

@ -0,0 +1,23 @@
/*
* 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.lucene.core.tests.main;
/** Just an empty class to reference from the actual tests. */
public class EmptyReference {
/** No instantiation. */
private EmptyReference() {}
}

View File

@ -0,0 +1,19 @@
/*
* 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.
*/
/** Contains a placeholder class for referencing in tests. */
package org.apache.lucene.core.tests.main;

View File

@ -0,0 +1,26 @@
<!--
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.
-->
<html>
<head>
<title>Apache Lucene Core Tests</title>
</head>
<body>
<p>A placeholder module descriptor just so that we can check whether
referencing it from the actual test module works.</p>
</body>
</html>

View File

@ -19,7 +19,8 @@
@SuppressWarnings({"requires-automatic"})
module org.apache.lucene.core.tests {
requires org.apache.lucene.core;
requires junit;
requires org.apache.lucene.test_framework;
requires org.apache.lucene.core.tests.main;
exports org.apache.lucene.core.tests;

View File

@ -17,12 +17,11 @@
package org.apache.lucene.core.tests;
import org.apache.lucene.store.MMapDirectory;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Assert;
import org.junit.Test;
public class TestMMap {
@Test
public void testUnmapSupported() throws Exception {
public class TestMMap extends LuceneTestCase {
public void testUnmapSupported() {
final Module module = MMapDirectory.class.getModule();
Assert.assertTrue("Lucene Core is not loaded as module", module.isNamed());
Assert.assertTrue(

View File

@ -17,13 +17,12 @@
package org.apache.lucene.core.tests;
import java.io.IOException;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.apache.lucene.util.ModuleResourceLoader;
import org.apache.lucene.util.ResourceLoader;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestModuleResourceLoader extends Assert {
public class TestModuleResourceLoader extends LuceneTestCase {
private static final Module MODULE = TestModuleResourceLoader.class.getModule();
private final ResourceLoader loader = new ModuleResourceLoader(MODULE);
@ -33,7 +32,6 @@ public class TestModuleResourceLoader extends Assert {
assertTrue("Test class must be in a named module", MODULE.isNamed());
}
@Test
public void testModuleResources() throws Exception {
try (var stream = loader.openResource("org/apache/lucene/core/testresources/accessible.txt")) {
stream.available();
@ -51,8 +49,7 @@ public class TestModuleResourceLoader extends Assert {
}
}
@Test
public void testModuleClassloading() throws Exception {
public void testModuleClassloading() {
assertSame(
TestModuleResourceLoader.class,
loader.findClass(TestModuleResourceLoader.class.getName(), Object.class));

View File

@ -0,0 +1,58 @@
/*
* 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.lucene.core.tests;
import org.apache.lucene.core.tests.main.EmptyReference;
import org.apache.lucene.index.IndexWriter;
import org.junit.Test;
/** Intentionally not a subclass of {@code LuceneTestCase}. */
public class TestRuntimeDependenciesSane {
@Test
public void testExternalDependenciesAreModules() {
isModule(org.junit.Test.class);
}
@Test
public void testInterProjectDependenciesAreModules() {
// The core Lucene should be a modular dependency.
isModule(IndexWriter.class);
}
@Test
public void testTestSourceSetIsAModule() {
// The test source set itself should be loaded as a module.
isModule(getClass());
}
@Test
public void testMainSourceSetIsAModule() {
// The test source set itself should be loaded as a module.
isModule(EmptyReference.class);
}
private static void isModule(Class<?> clazz) {
var module = clazz.getModule();
if (!module.isNamed()) {
throw new AssertionError(
"Class should be loaded from a named module: "
+ clazz.getName()
+ " but is instead part of: "
+ module);
}
}
}

View File

@ -20,6 +20,6 @@ apply plugin: 'java-library'
description = 'Lucene core library'
dependencies {
testImplementation project(':lucene:codecs')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:codecs')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -27,5 +27,5 @@ dependencies {
moduleImplementation project(':lucene:queryparser')
moduleImplementation project(':lucene:expressions')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -29,5 +29,5 @@ dependencies {
moduleImplementation 'org.ow2.asm:asm'
moduleImplementation 'org.ow2.asm:asm-commons'
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -22,11 +22,10 @@ description = 'Faceted indexing and search capabilities'
dependencies {
moduleApi project(':lucene:core')
moduleImplementation 'com.carrotsearch:hppc'
testImplementation project(':lucene:test-framework')
testImplementation project(':lucene:queries')
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:queries')
// Required for opening older indexes for backward compatibility tests
testImplementation project(':lucene:backward-codecs')
moduleTestImplementation project(':lucene:backward-codecs')
}

View File

@ -25,5 +25,5 @@ dependencies {
moduleImplementation project(':lucene:queries')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -26,7 +26,7 @@ dependencies {
moduleImplementation project(':lucene:queries')
moduleImplementation project(':lucene:memory')
testImplementation project(':lucene:test-framework')
testImplementation project(':lucene:analysis:common')
testImplementation project(':lucene:queryparser')
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:analysis:common')
moduleTestImplementation project(':lucene:queryparser')
}

View File

@ -21,5 +21,5 @@ description = 'Index-time and Query-time joins for normalized content'
dependencies {
moduleApi project(':lucene:core')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -45,7 +45,7 @@ dependencies {
moduleImplementation project(':lucene:analysis:stempel')
moduleImplementation project(':lucene:suggest')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}
// Configure main class name for all JARs.

View File

@ -23,6 +23,6 @@ description = 'Single-document in-memory index implementation'
dependencies {
moduleApi project(':lucene:core')
testImplementation project(':lucene:test-framework')
testImplementation project(':lucene:queryparser')
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:queryparser')
}

View File

@ -21,7 +21,7 @@ description = 'Index tools and other miscellaneous code'
dependencies {
moduleApi project(':lucene:core')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
nativeDeps project(":lucene:misc:native")
}

View File

@ -25,6 +25,6 @@ dependencies {
moduleImplementation project(':lucene:memory')
moduleImplementation project(':lucene:analysis:common')
testImplementation project(':lucene:queryparser')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:queryparser')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -22,7 +22,7 @@ description = 'Filters and Queries that add to core Lucene'
dependencies {
moduleApi project(':lucene:core')
testImplementation project(':lucene:test-framework')
testImplementation project(':lucene:expressions')
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:expressions')
}

View File

@ -24,5 +24,5 @@ dependencies {
moduleApi project(':lucene:queries')
moduleApi project(':lucene:sandbox')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -30,12 +30,12 @@ dependencies {
moduleImplementation 'javax.servlet:javax.servlet-api'
testImplementation 'org.eclipse.jetty:jetty-server'
testImplementation('org.eclipse.jetty:jetty-servlet', {
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation 'org.eclipse.jetty:jetty-server'
moduleTestImplementation('org.eclipse.jetty:jetty-servlet', {
exclude group: "org.eclipse.jetty", module: "jetty-security"
})
testImplementation 'org.eclipse.jetty:jetty-continuation'
testImplementation project(':lucene:test-framework')
moduleTestImplementation 'org.eclipse.jetty:jetty-continuation'
}

View File

@ -22,5 +22,5 @@ description = 'Various third party contributions and new ideas'
dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:queries')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -19,6 +19,10 @@ apply plugin: 'java-library'
description = 'Geospatial search'
configurations {
spatial4jTestPatch
}
dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:spatial3d')
@ -26,11 +30,17 @@ dependencies {
moduleApi 'org.locationtech.spatial4j:spatial4j'
moduleApi 'io.sgr:s2-geometry-library-java'
testImplementation project(':lucene:test-framework')
testImplementation testFixtures(project(':lucene:spatial3d'))
moduleTestImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:spatial-test-fixtures')
moduleTestImplementation 'org.locationtech.jts:jts-core'
testImplementation 'org.locationtech.spatial4j:spatial4j::tests'
// We add patched modules to this configuration because otherwise IDEs would not see the
// dependency at all, even in classpath mode (they don't see --patch-module commands we
// add to the compiler and test tasks).
moduleTestPatchOnly 'org.locationtech.spatial4j:spatial4j::tests'
spatial4jTestPatch 'org.locationtech.spatial4j:spatial4j::tests'
}
sourceSets.test.extensions.configure("modularPaths", {
it.patchModule("spatial4j", project.providers.provider({ configurations.spatial4jTestPatch.singleFile }))
})

View File

@ -16,7 +16,7 @@
*/
package org.apache.lucene.spatial.spatial4j;
import static org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator.*;
import static org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator.*;
import static org.locationtech.spatial4j.distance.DistanceUtils.DEGREES_TO_RADIANS;
import java.io.IOException;

View File

@ -0,0 +1,25 @@
/*
* 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.
*/
apply plugin: 'java-library'
description = '3D spatial planar geometry test fixtures'
dependencies {
moduleImplementation project(':lucene:test-framework')
moduleImplementation project(':lucene:spatial3d')
}

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
package org.apache.lucene.spatial3d.tests;
import com.carrotsearch.randomizedtesting.RandomizedContext;
import com.carrotsearch.randomizedtesting.RandomizedTest;
@ -25,6 +25,23 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.lucene.spatial3d.geom.GeoArea;
import org.apache.lucene.spatial3d.geom.GeoAreaShape;
import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoCircle;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
import org.apache.lucene.spatial3d.geom.GeoCompositeAreaShape;
import org.apache.lucene.spatial3d.geom.GeoCompositeMembershipShape;
import org.apache.lucene.spatial3d.geom.GeoPath;
import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.GeoPointShape;
import org.apache.lucene.spatial3d.geom.GeoPointShapeFactory;
import org.apache.lucene.spatial3d.geom.GeoPolygon;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoShape;
import org.apache.lucene.spatial3d.geom.PlanetModel;
/**
* Class for generating random Geo3dShapes. They can be generated under given constraints which are
@ -42,22 +59,22 @@ public final class RandomGeo3dShapeGenerator {
private static final int MAX_POINT_ITERATIONS = 1000;
/* Supported shapes */
protected static final int CONVEX_POLYGON = 0;
protected static final int CONVEX_POLYGON_WITH_HOLES = 1;
protected static final int CONCAVE_POLYGON = 2;
protected static final int CONCAVE_POLYGON_WITH_HOLES = 3;
protected static final int COMPLEX_POLYGON = 4;
protected static final int CIRCLE = 5;
protected static final int RECTANGLE = 6;
protected static final int PATH = 7;
protected static final int COLLECTION = 8;
protected static final int POINT = 9;
protected static final int LINE = 10;
protected static final int EXACT_CIRCLE = 11;
public static final int CONVEX_POLYGON = 0;
public static final int CONVEX_POLYGON_WITH_HOLES = 1;
public static final int CONCAVE_POLYGON = 2;
public static final int CONCAVE_POLYGON_WITH_HOLES = 3;
public static final int COMPLEX_POLYGON = 4;
public static final int CIRCLE = 5;
public static final int RECTANGLE = 6;
public static final int PATH = 7;
public static final int COLLECTION = 8;
public static final int POINT = 9;
public static final int LINE = 10;
public static final int EXACT_CIRCLE = 11;
/* Helper shapes for generating constraints whch are just three sided polygons */
protected static final int CONVEX_SIMPLE_POLYGON = 500;
protected static final int CONCAVE_SIMPLE_POLYGON = 501;
public static final int CONVEX_SIMPLE_POLYGON = 500;
public static final int CONCAVE_SIMPLE_POLYGON = 501;
/** Static methods only. */
private RandomGeo3dShapeGenerator() {}
@ -550,7 +567,7 @@ public final class RandomGeo3dShapeGenerator {
collection.addShape(member);
}
}
if (collection.shapes.size() == 0) {
if (collection.size() == 0) {
continue;
}
return collection;
@ -963,7 +980,7 @@ public final class RandomGeo3dShapeGenerator {
* @param points The points to order.
* @return The list of ordered points anti-clockwise.
*/
protected static List<GeoPoint> orderPoints(List<GeoPoint> points) {
public static List<GeoPoint> orderPoints(List<GeoPoint> points) {
double x = 0;
double y = 0;
double z = 0;
@ -1005,8 +1022,7 @@ public final class RandomGeo3dShapeGenerator {
* Class that holds the constraints that are given to build shapes. It consists in a list of
* GeoAreaShapes and relationships the new shape needs to satisfy.
*/
static class Constraints extends HashMap<GeoAreaShape, Integer> {
public static class Constraints extends HashMap<GeoAreaShape, Integer> {
/**
* Check if the shape is valid under the constraints.
*
@ -1052,7 +1068,7 @@ public final class RandomGeo3dShapeGenerator {
// For GeoCompositeMembershipShape we only consider the first shape to help
// converging
if (relationship == GeoArea.WITHIN && shape instanceof GeoCompositeMembershipShape) {
shape = (((GeoCompositeMembershipShape) shape).shapes.get(0));
shape = (((GeoCompositeMembershipShape) shape).getShapes().get(0));
}
switch (relationship) {
case GeoArea.DISJOINT:

View File

@ -0,0 +1,19 @@
/*
* 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.
*/
/** Shared test fixtures for spatial packages. */
package org.apache.lucene.spatial3d.tests;

View File

@ -0,0 +1,24 @@
<!--
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.
-->
<html>
<head>
<title>Apache Lucene Spatial Test Fixtures Module</title>
</head>
<body>
<h1>Internal test fixtures for spatial modules</h1>
</body>
</html>

View File

@ -16,13 +16,17 @@
*/
apply plugin: 'java-library'
apply plugin: 'java-test-fixtures'
description = '3D spatial planar geometry APIs'
dependencies {
moduleApi project(':lucene:core')
moduleTestImplementation project(':lucene:test-framework')
testFixturesApi project(':lucene:test-framework')
testImplementation project(':lucene:test-framework')
// We have to exclude ourselves because spatial-test-fixtures depend
// on the main source set of this project and tests would receive the
// dependency twice - on classpath and in module path.
moduleTestImplementation(project(':lucene:spatial-test-fixtures'), {
exclude module: "spatial3d"
})
}

View File

@ -22,6 +22,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
@ -149,4 +150,9 @@ public abstract class GeoBaseCompositeShape<T extends GeoShape> extends BasePlan
GeoBaseCompositeShape<?> other = (GeoBaseCompositeShape<?>) o;
return super.equals(other) && shapes.equals(other.shapes);
}
/** Returns an unmodifiable list of composed shapes. */
public List<T> getShapes() {
return Collections.unmodifiableList(shapes);
}
}

View File

@ -17,9 +17,10 @@
package org.apache.lucene.spatial3d.geom;
import static org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator.*;
import static org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator.*;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Test;

View File

@ -17,7 +17,7 @@
package org.apache.lucene.spatial3d.geom;
import static org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator.*;
import static org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator.*;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import java.io.ByteArrayInputStream;

View File

@ -16,7 +16,7 @@
*/
package org.apache.lucene.spatial3d.geom;
import static org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator.*;
import static org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator.*;
import com.carrotsearch.randomizedtesting.generators.BiasedNumbers;
import java.util.ArrayList;

View File

@ -17,7 +17,7 @@
package org.apache.lucene.spatial3d.geom;
import static org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator.*;
import static org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator.*;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.junit.Test;

View File

@ -17,7 +17,7 @@
package org.apache.lucene.spatial3d.geom;
import static org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator.*;
import static org.apache.lucene.spatial3d.tests.RandomGeo3dShapeGenerator.*;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import java.util.ArrayList;

View File

@ -23,5 +23,5 @@ dependencies {
moduleApi project(':lucene:core')
moduleApi project(':lucene:analysis:common')
testImplementation project(':lucene:test-framework')
moduleTestImplementation project(':lucene:test-framework')
}

View File

@ -16,12 +16,12 @@
*/
/** Lucene test framework. */
@SuppressWarnings({"module", "requires-automatic"})
@SuppressWarnings({"module", "requires-automatic", "requires-transitive-automatic"})
module org.apache.lucene.test_framework {
requires org.apache.lucene.core;
requires org.apache.lucene.codecs;
requires junit;
requires randomizedtesting.runner;
requires transitive junit;
requires transitive randomizedtesting.runner;
exports org.apache.lucene.tests.analysis.standard;
exports org.apache.lucene.tests.analysis;

View File

@ -63,5 +63,6 @@ include "lucene:replicator"
include "lucene:sandbox"
include "lucene:spatial3d"
include "lucene:spatial-extras"
include "lucene:spatial-test-fixtures"
include "lucene:suggest"
include "lucene:test-framework"

View File

@ -23,7 +23,7 @@ org.ow2.asm:asm-analysis:7.2 (1 constraints: e409d9a5)
org.ow2.asm:asm-commons:7.2 (1 constraints: ad042e2c)
org.ow2.asm:asm-tree:7.2 (2 constraints: 2f14468c)
ua.net.nlp:morfologik-ukrainian-search:4.9.1 (1 constraints: 10051b36)
xerces:xercesImpl:2.12.0 (2 constraints: 1f14b675)
xerces:xercesImpl:2.12.0 (1 constraints: 3705353b)
[Test dependencies]
org.assertj:assertj-core:3.21.0 (1 constraints: 38053c3b)