【问题标题】:Why does this dotCover rake task fail using these relative file paths?为什么这个 dotCover rake 任务使用这些相对文件路径失败?
【发布时间】:2013-03-07 00:38:37
【问题描述】:

我在让 dotCover 使用相对路径在 Albacore exec 任务中工作时遇到了一些问题。

@xUnitRunnerPath = Pathname.new('../../Tools/xUnit/xunitcontrib-dotcover.2.0/xunit.runner.utility.dll').realpath
@myTestDll = 'C:\PathToProj\My.Project.Tests\bin\Release\My.project.Tests.dll'
@outputDir = 'C:\PathToTestResults\'

exec :testCoverage do |cmd|
    cmd.command = "C:/BuildAgent/tools/dotCover/dotCover.exe"
    cmd.parameters = [
        "cover",
        "/targetexecutable=$$#{@xUnitRunnerPath}$$",
        "/targetarguments=$$#{@myTestDll}$$",
        "/output=#{@outputDir}/My.Project.Tests.dll.dcvr"
    ]
end

dotCover 错误只是告诉我路径错误是没有帮助的

Failed to convert relative paths to absolute in parameters for the 'cover' 
command. The given path's format is not supported. 

这并没有提供太多帮助,我也尝试过 dotcover help cover 以获得帮助,但没有提供很多关于问题所在的线索。

我关注了this post about rake and dotcoverthis question。也许我在这里遗漏了相关文档,但能够使其正常工作会非常有帮助。


编辑:我刚刚发现这与relative and absolute paths 有关,可能是因为我使用的是绝对路径,所以我需要以下内容。明天就知道了

/AnalyseTargetArguments=false

【问题讨论】:

    标签: rake teamcity teamcity-7.0 dotcover albacore


    【解决方案1】:

    我将从您自己的答案中重新混合 rakefile/tasks。您应该遵循一些 Ruby/Rake 约定以吸引更广泛的受众。我对如何编写很棒的 rakefile 有一些看法。特别是……

    1。不要直接调用/执行 Rake 任务

    Rake::Task[:unitTestWithCoverage].execute( testAssembly )
    

    您不想与直接 Rake invokeexecute 混淆的原因有很多。其中一个不调用依赖任务,一个只运行一次依赖任务......它变得愚蠢。应该总是有一种方法来构建正确定义和依赖的任务。

    2。不要参数化“内部”任务

    exec :unitTestWithCoverage, [:testAssembly] do |cmd, testAssembly|
    

    您可能有一个静态列表或通配符匹配的测试程序集列表。您应该能够在不使用参数的情况下构建具体任务。只有当用户可以通过命令行自定义输入调用参数化任务时,我才会使用它们。

    3。无需在每个任务中创建路径

    testAssemblyRealPath = Pathname.new(testAssembly).realpath
    testAssemblyName = File.basename(testAssemblyRealPath)
    

    我们将探索 Rake FileList 以了解如何创建自定义、惰性、mapped 文件名/路径/任意字符串列表!

    混音(更新)

    我在第一个答案中犯了一个严重错误(我将其保留在底部,以供参考)。我将解释您/我的教育在该部分中出了什么问题!

    接下来是我的新建议。这对我来说应该很明显,因为我在自己的构建中使用 mspec 测试运行器任务犯了同样的错误。

    dotcover_path = 'path/to/dotcover.exe'
    xunit_runner_path = 'path/to/xunitrunner.exe'
    
    test_assemblies = FileList['path/to/output/**/*.test.dll']
    coverage_results = "#{test_results_path}/coverage_results.dcvr"
    
    task :cover_all => [ :tests_with_coverage, :publish_coverage_results ]
    
    exec :tests_with_coverage do |cmd|
      cmd.comand = dotcover_path
      cmd.parameters = [ 
        "cover",
        "/AnalyseTargetArguments=False",
        "/TargetExecutable=\"#{xunit_runner_path}\"",
        "/TargetArguments=\"#{test_assemblies.join ','}\"",
        "/Output=\"#{coverage_results}\""
      ]
    end
    
    task :publish_coverage_results => [ :tests_with_coverage ] do 
      import_data 'dotNetCoverage', 'dotCover', coverage_results
    end
    
    def import_data(type, tool, file)
      puts "##teamcity[importData type='#{type}' tool='#{tool}' path='#{file}']"
    end
    

    解释

    我默认使用绝对路径(通常使用File.expand_path__FILE__ 常量)。有些工具/任务需要相对路径,但您始终可以使用 File.basename 之类的方法。

    dotcover_path = 'path/to/dotcover.exe'
    xunit_runner_path = 'path/to/xunitrunner.exe'
    

    我们仍然可以使用 FileList 的已构建程序集来定义目标程序集。在测试任务的主体执行之前,我们不会对其进行评估。

    test_assemblies = FileList['path/to/output/**/*.test.dll']
    

    覆盖运行程序支持具有单个结果文件的多个程序集。这样我们就没有另一个复杂的pathmap

    coverage_results = "#{test_results_path}/coverage_results.dcvr"
    

    从您的 CI 服务器调用它来运行测试并发布覆盖结果。

    task :cover_all => [ :tests_with_coverage, :publish_coverage_results ]
    

    这个任务现在简单明了。一些注意事项: 1. 使用join 将目标列表转换为格式正确的字符串。 2.我倾向于引用有文件路径的exec任务参数(需要转义,\")。

    exec :tests_with_coverage do |cmd|
      cmd.command = dotcover_path
      cmd.parameters = [ 
        "cover",
        "/AnalyseTargetArguments=False",
        "/TargetExecutable=\"#{xunit_runner_path}\"",
        "/TargetArguments=\"#{test_assemblies.join ','}\"",
        "/Output=\"#{coverage_results}\""
      ]
    end
    

    相同的旧发布任务/方法。

    task publish_coverage_results => [ :tests_with_coverage ] do 
      import_data 'dotNetCoverage', 'dotCover', coverage_results
    end
    
    def import_data(type, tool, file)
      puts "##teamcity[importData type='#{type}' tool='#{tool}' path='#{file}']"
    end
    

    旧混音

    截图显示问题区域,假设其余部分无趣或存在于新解决方案中。

    在构建任务之后,测试程序集才会存在。这通常不是问题,因为FileList 是懒惰的。在您对其进行枚举之前,它不会进行评估(例如,通过使用 eachmapzip)。

    但是,我们立即通过它each 来生成测试任务……所以这不起作用。列表中没有任何内容,也不会生成任何任务。或者,更糟糕的是,它会拾取先前构建的输出并可能做坏事(如果您没有完全清理输出目录)。

    test_assemblies = FileList['path/to/output/**/*.test.dll']
    coverage_results = test_assemblies.pathmap "#{test_results_path}/%n.dcvr"
    cover_task_names = test_assemblies.pathmap "cover_%n"
    
    test_assemblies.zip(coverage_results, cover_task_names) do |assembly, results, task_name|
      exec task_name do |cmd|
        cmd.command = dotcover_path
        cmd.parameters = [ 
          "cover",
          "/AnalyseTargetArguments=False",
          "/TargetExecutable=#{xunit_path}",
          "/TargetArguments=#{assembly}",
          "/Output=#{results}"
        ]
      end
    end
    

    【讨论】:

    • 感谢您的回答,非常感谢您花时间深入了解我在脚本中犯的错误。但是我发现你的代码比我的更难理解,但也许是因为我是 ruby​​ / rake 的新手。一些指向 .zip 和 .pathmap 等聪明位文档的链接会有所帮助,因为它对我来说有点太像魔术了。
    • 我在第一个答案中犯了一个非常严重的错误,我对其进行了调整,结果变得更加简单。我会读一些类似Eloquent Ruby 的东西作为Ruby 语言习语的介绍。这对我帮助很大!
    • 我更喜欢新的方式,也感谢书上的提示,我一定会捡起来
    【解决方案2】:

    对于任何对此感兴趣的人,这是我最后的 rake 任务工作

    task :unitTestsWithCoverageReport => [ :unitTestsWithCoverage, :coverageServiceMessage ]
    
    exec :unitTestsWithCoverage do |cmd|
        fullPathAssemblies = []
    
        @unitTestAssemblies.each do |testAssembly|
            testAssemblyRealPath = Pathname.new(testAssembly).realpath
            fullPathAssemblies << testAssemblyRealPath
        end
    
        cmd.command = @dotCoverRealPath
        cmd.parameters = [
            "cover",
            "/AnalyseTargetArguments=False",
            "/TargetExecutable=#{@xUnitRunnerRealPath}",
            "/TargetArguments=\"#{fullPathAssemblies.join ';'}\"",
            "/Output=#{@testResultsRealPath}/coverage.dcvr"
            ]
    end
    
    task :coverageServiceMessage do |t|
        puts "##teamcity[importData type='dotNetCoverage' tool='dotcover' path='#{@testResultsRealPath}/coverage.dcvr']"
    end
    

    非常感谢@AnthonyMastrean,他向我展示了一些非常棒的 ruby​​ 小技巧,以及我应该如何正确构建我的 rake 文件。

    【讨论】:

    • 你能提供一些关于为什么/如何与以前的 rakefile 相比的评论吗?
    • dotCover 命令行似乎接受 多个 目标参数。您是否需要每个程序集的覆盖率报告文件?如果没有,您可以将所有程序集路径推送到该目标参数中,而不是为每个程序集创建一个任务。
    • 我试试看,谢谢。另外,我不喜欢您将任务命名为import_data,而实际上您正在创建一个service message 以供teamcity 进行解释。这可能涉及 teamcity 导入数据,但这不是任务的作用。
    • import_data 只是我定义的用于结束打印服务消息的方法。我制作了一个小型TeamCity service message gem 的原型。我的好友 Henrik Feldt 正在使用 the same features 重新设计 Albacore 的内部结构。
    • 喜欢 TC 集成到长鳍金枪鱼的外观,也很漂亮
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    • 2012-03-10
    • 2021-02-20
    • 1970-01-01
    • 2011-03-12
    • 2023-03-08
    • 2013-07-14
    相关资源
    最近更新 更多