nebula-test 下的自定义 Gradle 任务 运行 无法查看创建的目录

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

我有一个自定义 Gradle 插件的工作版本。我有几个单元测试,现在我正在尝试使用 nebula-test 进行集成测试。

我在集成测试中遇到了一个令人困惑的测试错误。我一定是在使用 nebula-test 时做错了什么。

我会展示完整的代码,但我会先描述要点。

该插件定义了一个扩展块,其中包含一些属性,其中之一是可以找到特定文件的文件路径。对于上下文,属性 名称称为 "yangFilesRootDir"。

该插件创建了一个名为 "YangGenerate" 的自定义任务实例。该任务有一个名为 "yangFilesRootDir" 的实例变量,类型为 File,该实例变量上有一个 @InputDirectory 注释。该任务还有其他实例变量,其中一些带有“@Input...”注释。

任务有一个 "init" 方法,在项目评估后从插件调用。这将从扩展块中的值初始化任务实例变量。在 "init" 方法中,如果 "yangFilesRootDir" 属性 没有在扩展块中设置,我使用默认值 "src/main/yang".

在进行此集成测试之前,我设置了一个引用该插件的独立项目。该项目的 "build.gradle" 指定了扩展块,我已经使用 属性 设置和不使用(我将文件存储在默认位置)对其进行了测试,并且工作正常。

这是我的规范现在的样子:

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
}

}

我在有和没有 "yang" 块的情况下都尝试过这个,并且测试失败了这个堆栈跟踪:

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.run(DefaultBuildLauncher.java:82)
    at org.gradle.tooling.internal.consumer.DefaultBuildLauncher.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.run(DefaultAsyncConsumerActionExecutor.java:55)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl.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.access0(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter.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”来获取更多信息,它向我显示了用于测试的临时构建目录的路径,在测试失败后我验证了 "src/main/yang/dummy"存在于测试项目目录中。

这里是插件的相关代码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) }
                }
            }
        }
    }

以及 YangGenerate 任务的相关部分:

    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)
            ...
        }
    }

更新:

我终于想到运行在调试器中测试这个,发现找不到路径的原因是"current directory"不是测试项目的基目录,但是主插件项目的基本目录,肯定没有 "src/main/yang" 目录。

#gradle 上有人向我指出了如何在与项目 basedir (project.file(...)) 相关的插件代码中打开文件,在我将插件代码更改为这样做,这个错误就消失了。