为什么这个dotCover rake任务使用这些相对文件路径失败?

时间:2013-03-18 15:40:17

标签: rake teamcity teamcity-7.0 dotcover albacore

我在使用相对路径让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帮助保护,但没有提供很多关于出错的线索。

我已关注this post about rake and dotcoverthis question。也许我在这里错过了相关的文档,但能够让这个工作真的很有帮助。


编辑:我刚发现这与relative and absolute paths有关,也许是因为我使用的是绝对路径,我需要以下内容。我们明天会发现

/AnalyseTargetArguments=false

2 个答案:

答案 0 :(得分:3)

我要从你自己的答案中重新混合rakefile / tasks。您应该遵循一些Ruby / Rake惯例以吸引更广泛的受众。我对如何编写令人敬畏的rakefiles有一些看法。特别是......

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']

coverage运行器支持具有单个结果文件的多个程序集。这样我们就没有另一个复杂的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

The Old Remix

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

之后构建任务之前,测试程序集将不存在。这通常不是问题,因为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

答案 1 :(得分:1)

对于任何对此感兴趣的人来说,我的最终佣金工作是

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,因为他向我展示了一些非常好的小红宝石技巧,以及我应该如何正确构建我的rake文件。