Search code examples
groovygradlenebula-test

Custom Gradle task running under nebula-test fails to see created directory


I have a working build of a custom Gradle plugin. I have a couple of unit tests, and now I'm trying to get integration tests going, using nebula-test.

I'm getting a confusing test error in the integration test. I must be doing something wrong in how I'm using nebula-test.

I'll show full code, but I'll describe the essentials first.

The plugin defines an extension block which contains a handful of properties, one of which is a file path where particular files can be found. For context, that property name is called "yangFilesRootDir".

The plugin creates an instance of a custom task called "YangGenerate". The task has an instance variable called "yangFilesRootDir" of type File, and this instance variable has a @InputDirectory annotation on it. The task has other instance variables, some with "@Input..." annotations on them.

The task has an "init" method that is called from the plugin after project evaluation. This initializes the task instance variables from the values in the extension block. In the "init" method, if the "yangFilesRootDir" property isn't set in the extension block, I use a default value of "src/main/yang".

Before working on this integration test, I set up a standalone project referencing the plugin. The "build.gradle" for this project specifies the extension block, and I've tested it both with the property setting and without (I'm storing the files in that the default location), and it's working fine.

This is what my spec looks like right now:

class YangPluginIntegSpec extends IntegrationSpec {
def 'simple'() {
    createFile("src/main/yang/dummy")
    writeHelloWorld("com.example")
    buildFile << applyPlugin(YangPlugin)
    buildFile << '''
        yang {
            yangFilesRootDir 'src/main/yang'
        }
    '''.stripIndent()

    when:
    ExecutionResult result = runTasksSuccessfully('build')

    then:
    println result
}

}

I've tried this both with and without the "yang" block, and the test is failing with this stacktrace:

org.gradle.api.GradleException: Build aborted because of an internal error.
    at nebula.test.functional.internal.DefaultExecutionResult.rethrowFailure(DefaultExecutionResult.groovy:95)
    at nebula.test.IntegrationSpec.runTasksSuccessfully(IntegrationSpec.groovy:234)
    at com.att.opnfv.yang.gradle.YangPluginIntegSpec.simple(YangPluginIntegSpec.groovy:18)
Caused by: org.gradle.internal.exceptions.LocationAwareException: A problem was found with the configuration of task ':yangGenerate'.
    at org.gradle.initialization.DefaultExceptionAnalyser.transform(DefaultExceptionAnalyser.java:77)
    at org.gradle.initialization.MultipleBuildFailuresExceptionAnalyser.transform(MultipleBuildFailuresExceptionAnalyser.java:47)
    at org.gradle.initialization.StackTraceSanitizingExceptionAnalyser.transform(StackTraceSanitizingExceptionAnalyser.java:30)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:108)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:80)
    at org.gradle.tooling.internal.provider.BuildModelAction.run(BuildModelAction.java:43)
    at org.gradle.tooling.internal.provider.BuildModelAction.run(BuildModelAction.java:30)
    at org.gradle.tooling.internal.provider.ConfiguringBuildAction.run(ConfiguringBuildAction.java:119)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:36)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
    at org.gradle.tooling.internal.provider.DaemonBuildActionExecuter.execute(DaemonBuildActionExecuter.java:42)
    at org.gradle.tooling.internal.provider.DaemonBuildActionExecuter.execute(DaemonBuildActionExecuter.java:29)
    at org.gradle.tooling.internal.provider.LoggingBridgingBuildActionExecuter.execute(LoggingBridgingBuildActionExecuter.java:62)
    at org.gradle.tooling.internal.provider.LoggingBridgingBuildActionExecuter.execute(LoggingBridgingBuildActionExecuter.java:34)
    at org.gradle.tooling.internal.provider.ProviderConnection.run(ProviderConnection.java:107)
    at org.gradle.tooling.internal.provider.ProviderConnection.run(ProviderConnection.java:94)
    at org.gradle.tooling.internal.provider.DefaultConnection.getModel(DefaultConnection.java:149)
    at org.gradle.tooling.internal.consumer.connection.CancellableModelBuilderBackedModelProducer.produceModel(CancellableModelBuilderBackedModelProducer.java:58)
    at org.gradle.tooling.internal.consumer.connection.AbstractConsumerConnection.run(AbstractConsumerConnection.java:56)
    at org.gradle.tooling.internal.consumer.DefaultBuildLauncher$1.run(DefaultBuildLauncher.java:82)
    at org.gradle.tooling.internal.consumer.DefaultBuildLauncher$1.run(DefaultBuildLauncher.java:76)
    at org.gradle.tooling.internal.consumer.connection.LazyConsumerActionExecutor.run(LazyConsumerActionExecutor.java:83)
    at org.gradle.tooling.internal.consumer.connection.ProgressLoggingConsumerActionExecutor.run(ProgressLoggingConsumerActionExecutor.java:58)
    at org.gradle.tooling.internal.consumer.async.DefaultAsyncConsumerActionExecutor$1$1.run(DefaultAsyncConsumerActionExecutor.java:55)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
Caused by: org.gradle.api.tasks.TaskValidationException: A problem was found with the configuration of task ':yangGenerate'.
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:306)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:88)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:68)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:55)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:149)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
    ... 22 more
Caused by: org.gradle.api.InvalidUserDataException: Directory 'src\main\yang' specified for property 'yangFilesRootDir' does not exist.
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:47)
    ... 41 more

I also ran this with "-i" so I can get more info, and it showed me the path to the temporary build directory for the test, and after the test failed I verified that "src/main/yang/dummy" exists in the test project directory.

Here's the relevant code for the plugin class:

    class YangPlugin implements Plugin<Project> {
        public void apply(Project project) {
            YangExtension   yang  = project.extensions.create(YANG_EXT, YangExtension, project)
            YangGenerateTask  task  = project.task(YANG_GENERATE_TASK, type: YangGenerateTask)
            project.afterEvaluate {
                task.init(project, yang)
                project.compileJava.dependsOn task
                if (task.yangFilesRootDir && task.yangFilesRootDir.exists()) {
                    Jar jarTask = project.jar
                    if (task.yangFilesRootDir.getPath().startsWith(FilenameUtils.normalize(SRC_MAIN_RESOURCES))) {
                        jarTask.exclude(task.yangFilesRootDir.getPath() - FilenameUtils.normalize(SRC_MAIN_RESOURCES))
                    }
                    jarTask.from(task.yangFilesRootDir) { into(META_INF_YANG) }
                }
            }
        }
    }

And the relevant portion of the YangGenerate task:

    class YangGenerateTask extends DefaultTask {
        private static final String DEFAULT_YANG_FILES_ROOT_DIR = "src/main/yang"   

        Project         project

        Collection<CodeGeneratorConfig> generators
        boolean                         inspectDependencies
        Collection<String>              excludeFiles
        String                          yangFilesConfiguration
        String                          generatorsConfiguration

        Pattern         yangFilePattern = Pattern.compile("META-INF/yang/.*\\.yang\$")

        @InputDirectory
        File    yangFilesRootDir

        @Input
        def getGeneratorClasses() {
            ...
        }

        @InputFiles
        def getOptionalYangClasspath() {
         ...
        }

        @OutputDirectories
        def getOutputDirectories() {
         ...
        }

        @TaskAction
        void generate() {
         ...
        }

        public void init(Project project, YangExtension yang) {
            this.yangFilesRootDir           = new File(yang.yangFilesRootDir ?: DEFAULT_YANG_FILES_ROOT_DIR)
            ...
        }
    }

Update:

I finally thought to run this test in the debugger, and I discovered that the reason the path isn't found is that the "current directory" is not the base directory of the test project, but the base directory of the main plugin project, which definitely doesn't have a "src/main/yang" directory.


Solution

  • Someone on #gradle pointed me to how to open files in the plugin code relative to the project basedir (project.file(...)), and after I changed my plugin code to do this, this error went away.