diff --git a/AltCover.Engine/Cobertura.fs b/AltCover.Engine/Cobertura.fs index d5fb4a9d..e4c4280e 100644 --- a/AltCover.Engine/Cobertura.fs +++ b/AltCover.Engine/Cobertura.fs @@ -113,15 +113,18 @@ module internal Cobertura = let cname = m.Attribute("class".X).Value let mname = m.Attribute("name".X).Value + let indexmname = + mname.Split('`') |> Seq.head + let classAt = fn.IndexOf(cname, StringComparison.Ordinal) let returnType = fn.Substring(0, classAt) let methodAt = - fn.IndexOf(mname, classAt + cname.Length, StringComparison.Ordinal) + fn.IndexOf(indexmname, classAt + cname.Length, StringComparison.Ordinal) - let argsAt = methodAt + mname.Length + let argsAt = methodAt + indexmname.Length let args = fn.Substring(argsAt) let signature = returnType + args (mname, signature) diff --git a/AltCover.Engine/Naming.fs b/AltCover.Engine/Naming.fs index 2b8d9f9f..14549ab7 100644 --- a/AltCover.Engine/Naming.fs +++ b/AltCover.Engine/Naming.fs @@ -83,7 +83,12 @@ module internal Naming = String.Empty let return' = - I.fullTypeRefName def.ReturnType + let r = def.ReturnType + + if r.ContainsGenericParameter then + r.FullName + else + I.fullTypeRefName r String.Join( String.Empty, diff --git a/AltCover.Engine/Report.fs b/AltCover.Engine/Report.fs index 9fcc0d1c..5d24f90d 100644 --- a/AltCover.Engine/Report.fs +++ b/AltCover.Engine/Report.fs @@ -92,10 +92,30 @@ module internal Report = (methodDef: MethodDefinition) included = + let vtype = methodDef.DeclaringType + + let vms = + vtype.Methods |> Seq.groupBy _.FullName + + let methodName = + let full = methodDef.FullName + + let group = + vms + |> Seq.find (fun (n, x) -> n.Equals(full, StringComparison.Ordinal)) + |> snd + |> Seq.length + + let tentative = methodDef.Name + + match group with + | 1 -> tentative + | _ -> sprintf "%s`%d" tentative methodDef.GenericParameters.Count + let element = XElement( "method".X, - XAttribute("name".X, methodDef.Name), + XAttribute("name".X, methodName), // /// Mono.Cecil emits names in the form outer/inner rather than outer+inner XAttribute("class".X, Naming.fullTypeName methodDef.DeclaringType), XAttribute("metadataToken".X, methodDef.MetadataToken.ToUInt32().ToString()), diff --git a/AltCover.Tests/AltCover.Tests.fsproj b/AltCover.Tests/AltCover.Tests.fsproj index 9f671f97..b58c4c44 100644 --- a/AltCover.Tests/AltCover.Tests.fsproj +++ b/AltCover.Tests/AltCover.Tests.fsproj @@ -99,8 +99,12 @@ + + + + AltCover.Recorder.net20.dll diff --git a/AltCover.Tests/Expecto.fs b/AltCover.Tests/Expecto.fs index dcf92bef..b5a5191c 100644 --- a/AltCover.Tests/Expecto.fs +++ b/AltCover.Tests/Expecto.fs @@ -243,8 +243,12 @@ module ExpectoTestManifest = "Runner.JsonShouldGeneratePlausibleLcov" Tests.AltCoverRunnerTests.JsonWithPartialsShouldGeneratePlausibleLcov, "Runner.JsonWithPartialsShouldGeneratePlausibleLcov" + Tests.AltCoverRunnerTests.JsonWithOverloadsShouldGeneratePlausibleLcov, + "Runner.JsonWithOverloadsShouldGeneratePlausibleLcov" Tests.AltCoverRunnerTests.NCoverShouldGeneratePlausibleLcov, "Runner.NCoverShouldGeneratePlausibleLcov" + Tests.AltCoverRunnerTests.NCoverWithOverloadsShouldGeneratePlausibleLcov, + "Runner.NCoverWithOverloadsShouldGeneratePlausibleLcov" Tests.AltCoverRunnerTests.NCoverWithPartialsShouldGeneratePlausibleLcov, "Runner.NCoverWithPartialsShouldGeneratePlausibleLcov" Tests.AltCoverRunnerTests.NCoverShouldGenerateMorePlausibleLcov, @@ -266,6 +270,8 @@ module ExpectoTestManifest = "Runner.NCoverShouldGeneratePlausibleCobertura" Tests.AltCoverRunnerTests.NCoverWithPartialsShouldGeneratePlausibleCobertura, "Runner.NCoverWithPartialsShouldGeneratePlausibleCobertura" + Tests.AltCoverRunnerTests.NCoverWithOverloadsShouldGeneratePlausibleCobertura, + "Runner.NCoverWithOverloadsShouldGeneratePlausibleCobertura" Tests.AltCoverRunnerTests.NCoverShouldGenerateMorePlausibleCobertura, "Runner.NCoverShouldGenerateMorePlausibleCobertura" Tests.AltCoverRunnerTests.DegenerateCasesShouldNotGenerateCobertura, @@ -274,6 +280,8 @@ module ExpectoTestManifest = "Runner.NCoverShouldGeneratePlausibleCoberturaWithMissingFullName" Tests.AltCoverRunnerTests.OpenCoverShouldGeneratePlausibleCobertura, "Runner.OpenCoverShouldGeneratePlausibleCobertura" + Tests.AltCoverRunnerTests.OpenCoverWithOverloadsShouldGeneratePlausibleCobertura, + "Runner.OpenCoverWithOverloadsShouldGeneratePlausibleCobertura" Tests.AltCoverRunnerTests.OpenCoverWithPartialsShouldGeneratePlausibleCobertura, "Runner.OpenCoverWithPartialsShouldGeneratePlausibleCobertura" Tests.AltCoverRunnerTests.ThresholdViolationShouldBeReported, @@ -426,6 +434,8 @@ module ExpectoTestManifest = Tests.AltCoverTests.MethodNamesAreExtracted, "Tests.MethodNamesAreExtracted" Tests.AltCoverTests.FullMethodNamesAreExtracted, "Tests.FullMethodNamesAreExtracted" + Tests.AltCoverTests.ShouldGenerateExpectedNCoverReportWithOverloads, + "Tests.ShouldGenerateExpectedNCoverReportWithOverloads" Tests.AltCoverTests.ShouldGenerateExpectedXmlReportFromDotNet, "Tests.ShouldGenerateExpectedXmlReportFromDotNet" Tests.AltCoverTests.ShouldGenerateExpectedXmlReportWithoutTriviaFromDotNet, diff --git a/AltCover.Tests/Issue222.NCover.cobertura b/AltCover.Tests/Issue222.NCover.cobertura new file mode 100644 index 00000000..db09c6c7 --- /dev/null +++ b/AltCover.Tests/Issue222.NCover.cobertura @@ -0,0 +1,101 @@ + + + + + C:/Users/email/Documents/Github/altcover/Samples/Sample32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AltCover.Tests/Issue222.NCover.lcov b/AltCover.Tests/Issue222.NCover.lcov new file mode 100644 index 00000000..d62f3046 --- /dev/null +++ b/AltCover.Tests/Issue222.NCover.lcov @@ -0,0 +1,47 @@ +TN: Sample32, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +SF:C:\Users\email\Documents\Github\altcover\Samples\Sample32\Class1.cs +FN:13,T issue222.Class1.GetService(System.IServiceProvider) +FN:19,System.Void issue222.Class1.AddSingleton(issue222.IServiceCollection,System.Func`2) +FN:21,T issue222.Class1+<>c__1`3.b__1_0(System.IServiceProvider) +FN:26,System.Void issue222.Class1.AddSingleton(issue222.IServiceCollection,System.Func`2) +FN:28,T issue222.Class1+<>c__2`4.b__2_0(System.IServiceProvider) +FN:29,T issue222.Class1+<>c__2`4.b__2_1(System.IServiceProvider) +FNDA:0,T issue222.Class1.GetService(System.IServiceProvider) +FNDA:0,System.Void issue222.Class1.AddSingleton(issue222.IServiceCollection,System.Func`2) +FNDA:0,T issue222.Class1+<>c__1`3.b__1_0(System.IServiceProvider) +FNDA:0,System.Void issue222.Class1.AddSingleton(issue222.IServiceCollection,System.Func`2) +FNDA:0,T issue222.Class1+<>c__2`4.b__2_0(System.IServiceProvider) +FNDA:0,T issue222.Class1+<>c__2`4.b__2_1(System.IServiceProvider) +FNF:6 +FNH:0 +BRF:0 +BRH:0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +LH:0 +LF:12 +end_of_record +TN: Sample32, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +SF:C:\Users\email\Documents\Github\altcover\Samples\Sample32\Program.cs +FN:8,System.Void Sample32.Program.Main(System.String[]) +FNDA:0,System.Void Sample32.Program.Main(System.String[]) +FNF:1 +FNH:0 +BRF:0 +BRH:0 +DA:8,0 +DA:9,0 +DA:10,0 +LH:0 +LF:3 +end_of_record diff --git a/AltCover.Tests/Issue222.NCover.xml b/AltCover.Tests/Issue222.NCover.xml index 3d6abb59..9713166a 100644 --- a/AltCover.Tests/Issue222.NCover.xml +++ b/AltCover.Tests/Issue222.NCover.xml @@ -1,7 +1,7 @@  - - + + @@ -12,26 +12,26 @@ - + - + - + - + - + diff --git a/AltCover.Tests/Issue222.OpenCover.cobertura b/AltCover.Tests/Issue222.OpenCover.cobertura new file mode 100644 index 00000000..20b1fd1e --- /dev/null +++ b/AltCover.Tests/Issue222.OpenCover.cobertura @@ -0,0 +1,101 @@ + + + + + C:\Users\email\Documents\Github\altcover\Samples\Sample32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AltCover.Tests/Issue222.json.lcov b/AltCover.Tests/Issue222.json.lcov new file mode 100644 index 00000000..76330e49 --- /dev/null +++ b/AltCover.Tests/Issue222.json.lcov @@ -0,0 +1,41 @@ +TN: Sample32 +SF:C:\Users\email\Documents\Github\altcover\Samples\Sample32\Class1.cs +FN:13,T issue222.Class1::GetService(System.IServiceProvider) +FN:19,System.Void issue222.Class1::AddSingleton`3(issue222.IServiceCollection,System.Func`2) +FN:26,System.Void issue222.Class1::AddSingleton`4(issue222.IServiceCollection,System.Func`2) +FNDA:0,T issue222.Class1::GetService(System.IServiceProvider) +FNDA:0,System.Void issue222.Class1::AddSingleton`3(issue222.IServiceCollection,System.Func`2) +FNDA:0,System.Void issue222.Class1::AddSingleton`4(issue222.IServiceCollection,System.Func`2) +FNF:3 +FNH:0 +BRF:0 +BRH:0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:30,0 +LH:0 +LF:12 +end_of_record +TN: Sample32 +SF:C:\Users\email\Documents\Github\altcover\Samples\Sample32\Program.cs +FN:8,System.Void Sample32.Program::Main(System.String[]) +FNDA:0,System.Void Sample32.Program::Main(System.String[]) +FNF:1 +FNH:0 +BRF:0 +BRH:0 +DA:8,0 +DA:9,0 +DA:10,0 +LH:0 +LF:3 +end_of_record diff --git a/AltCover.Tests/Runner.Tests.fs b/AltCover.Tests/Runner.Tests.fs index b31d616c..59e47969 100644 --- a/AltCover.Tests/Runner.Tests.fs +++ b/AltCover.Tests/Runner.Tests.fs @@ -5658,6 +5658,73 @@ module AltCoverRunnerTests = finally LCov.path.Value <- None + [] + let NCoverWithOverloadsShouldGeneratePlausibleLcov () = + Runner.init () + + let resource = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.NCover.xml", StringComparison.Ordinal) + + use stream = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource) + + let baseline = XDocument.Load(stream) + + let unique = + Path.Combine( + Assembly.GetExecutingAssembly().Location + |> Path.GetDirectoryName, + Guid.NewGuid().ToString() + "/NCover.lcov" + ) + + LCov.path.Value <- Some unique + + unique + |> Path.GetDirectoryName + |> Directory.CreateDirectory + |> ignore + + try + let r = + LCov.summary (XML baseline) ReportFormat.NCover 0 + + Assert.That(r, Is.EqualTo(0, 0, String.Empty)) + let result = File.ReadAllText unique + printfn "%s" result + + let resource2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.NCover.lcov", StringComparison.Ordinal) + + use stream2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource2) + + use reader = new StreamReader(stream2) + + let expected = + reader + .ReadToEnd() + .Replace("\r", String.Empty) + .Replace("\\", "/") + + Assert.That( + result + .Replace("\r", String.Empty) + .Replace("\\", "/"), + Is.EqualTo expected + ) + finally + LCov.path.Value <- None + [] let NCoverShouldGenerateMorePlausibleLcov () = Runner.init () @@ -5793,6 +5860,76 @@ module AltCoverRunnerTests = finally LCov.path.Value <- None + [] + let JsonWithOverloadsShouldGeneratePlausibleLcov () = + Runner.init () + + let resource = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.json", StringComparison.Ordinal) + + use stream = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource) + + let baseline = + use r = new StreamReader(stream) + r.ReadToEnd() |> NativeJson.fromJsonText + + let unique = + Path.Combine( + Assembly.GetExecutingAssembly().Location + |> Path.GetDirectoryName, + Guid.NewGuid().ToString() + + "/JsonWithOverloads.lcov" + ) + + LCov.path.Value <- Some unique + + unique + |> Path.GetDirectoryName + |> Directory.CreateDirectory + |> ignore + + try + let r = + LCov.summary (JSON baseline) ReportFormat.NativeJson 0 + + Assert.That(r, Is.EqualTo(0, 0, String.Empty)) + let result = File.ReadAllText unique + printfn "%s" result + + let resource2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.json.lcov", StringComparison.Ordinal) + + use stream2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource2) + + use reader = new StreamReader(stream2) + + let expected = + reader + .ReadToEnd() + .Replace("\r", String.Empty) + .Replace("\\", "/") + + Assert.That( + result + .Replace("\r", String.Empty) + .Replace("\\", "/"), + Is.EqualTo expected + ) + finally + LCov.path.Value <- None + [] let JsonWithPartialsShouldGeneratePlausibleLcov () = Runner.init () @@ -6127,6 +6264,89 @@ module AltCoverRunnerTests = finally Cobertura.path.Value <- None + [] + let NCoverWithOverloadsShouldGeneratePlausibleCobertura () = + Runner.init () + + let resource = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.NCover.xml", StringComparison.Ordinal) + + use stream = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource) + + let baseline = XDocument.Load(stream) + + let unique = + Path.Combine( + Assembly.GetExecutingAssembly().Location + |> Path.GetDirectoryName, + Guid.NewGuid().ToString() + + "/Issue222.NCover.cobertura" + ) + + Cobertura.path.Value <- Some unique + + unique + |> Path.GetDirectoryName + |> Directory.CreateDirectory + |> ignore + + try + Runner.I.addCoberturaSummary () + + let summarize = + Runner.I.summaries |> Seq.head + + let r = + summarize (XML baseline) ReportFormat.NCover 0 + + Assert.That(r, Is.EqualTo(0, 0, String.Empty)) + + let result = + Regex + .Replace( + File.ReadAllText unique, + """timestamp=\"\d*\">""", + """timestamp="xx">""" + ) + .Replace("\\", "/") + //printfn "%s" result + let resource2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.NCover.cobertura", StringComparison.Ordinal) + + use stream2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource2) + + use reader = new StreamReader(stream2) + + let expected = + reader + .ReadToEnd() + .Replace("\r", String.Empty) + .Replace("\\", "/") + .Replace( + """version="8.8.0.0""", + "version=\"" + + typeof.Assembly + .GetName() + .Version.ToString() + ) + + Assert.That(result.Replace("\r", String.Empty), Is.EqualTo expected, result) + validate result + finally + Cobertura.path.Value <- None + [] let NCoverWithPartialsShouldGeneratePlausibleCobertura () = Runner.init () @@ -6875,6 +7095,89 @@ module AltCoverRunnerTests = finally Cobertura.path.Value <- None + [] + let OpenCoverWithOverloadsShouldGeneratePlausibleCobertura () = + Runner.init () + + let resource = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.OpenCover.xml", StringComparison.Ordinal) + + use stream = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource) + + let baseline = XDocument.Load(stream) + + let unique = + Path.Combine( + Assembly.GetExecutingAssembly().Location + |> Path.GetDirectoryName, + Guid.NewGuid().ToString() + + "/OpenCoverWithOverloads.cobertura" + ) + + Cobertura.path.Value <- Some unique + + unique + |> Path.GetDirectoryName + |> Directory.CreateDirectory + |> ignore + + try + let r = + Cobertura.summary (XML baseline) ReportFormat.OpenCover 0 + + Assert.That(r, Is.EqualTo(0, 0, String.Empty)) + + let result = + Regex.Replace( + File.ReadAllText unique, + """timestamp=\"\d*\">""", + """timestamp="xx">""" + ) + //printfn "%s" result + let resource2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceNames() + |> Seq.find _.EndsWith("Issue222.OpenCover.cobertura", StringComparison.Ordinal) + + use stream2 = + Assembly + .GetExecutingAssembly() + .GetManifestResourceStream(resource2) + + use reader = new StreamReader(stream2) + + let expected = + reader + .ReadToEnd() + .Replace("\r", String.Empty) + .Replace("\\", "/") + .Replace( + """version="3.0.0.0""", + "version=\"" + + typeof.Assembly + .GetName() + .Version.ToString() + ) + + Assert.That( + result + .Replace("\r", String.Empty) + .Replace("\\", "/"), + Is.EqualTo expected, + result + ) + + validate result + finally + Cobertura.path.Value <- None + [] let OpenCoverWithPartialsShouldGeneratePlausibleCobertura () = Runner.init () diff --git a/AltCover.Tests/Tests.fs b/AltCover.Tests/Tests.fs index 4df3e7e5..ed73bf73 100644 --- a/AltCover.Tests/Tests.fs +++ b/AltCover.Tests/Tests.fs @@ -3403,7 +3403,7 @@ module AltCoverTests = "System.Void AltCover.Sample3.Class3+Class4.set_Sample(System.Int32)" "System.String[] AltCover.Sample3.Class3+Class4.get_modules()" "System.Void AltCover.Sample3.Class3+Class4.set_modules(System.String[])" - "System.Collections.Generic.List`1 AltCover.Sample3.Class3+Class4.ToList(T)" + "System.Collections.Generic.List`1 AltCover.Sample3.Class3+Class4.ToList(T)" "System.Void AltCover.Sample3.Class3+Class4.#ctor()" "System.String AltCover.Recorder.InstrumentationAttribute.get_Assembly()" "System.String AltCover.Recorder.InstrumentationAttribute.get_Configuration()" @@ -3644,6 +3644,71 @@ module AltCoverTests = CoverageParameters.nameFilters.Clear() CoverageParameters.theReportFormat <- None + [] + let ShouldGenerateExpectedNCoverReportWithOverloads () = + let visitor, document = + Report.reportGenerator () + + let path = sample32path + + maybeIgnore (fun () -> path |> File.Exists |> not) + + try + CoverageParameters.nameFilters.Clear() + CoverageParameters.theReportFormat <- Some ReportFormat.NCover + + Visitor.visit + [ visitor ] + (Visitor.I.toSeq + { AssemblyPath = path + Identity = Hallmark.Build() + Destinations = [] }) + + // printfn "%A" (makeDocument document) + + let doc = makeDocument document + + let results = + doc.Descendants("method".X) + |> Seq.map _.Attribute("name".X).Value + |> Seq.toList + + //printfn "%A" results + + Assert.That(results |> List.length, Is.EqualTo 7) + Assert.That(results |> List.distinct |> List.length, Is.EqualTo 7) + + let expected = + [ "Main" + "GetService" + "AddSingleton`3" + "AddSingleton`4" + "b__1_0" + "b__2_0" + "b__2_1" ] + + Assert.That(results, Is.EquivalentTo expected) + + let results = + doc.Descendants("method".X) + |> Seq.map _.Attribute("fullname".X).Value + |> Seq.toList + + let expected = + [ "T issue222.Class1+<>c__1`3.b__1_0(System.IServiceProvider)" + "T issue222.Class1+<>c__2`4.b__2_0(System.IServiceProvider)" + "T issue222.Class1+<>c__2`4.b__2_1(System.IServiceProvider)" + "System.Void issue222.Class1.AddSingleton(issue222.IServiceCollection,System.Func`2)" + "System.Void issue222.Class1.AddSingleton(issue222.IServiceCollection,System.Func`2)" + "System.Void Sample32.Program.Main(System.String[])" + "T issue222.Class1.GetService(System.IServiceProvider)" ] + + Assert.That(results, Is.EquivalentTo expected) + + finally + CoverageParameters.nameFilters.Clear() + CoverageParameters.theReportFormat <- None + [] let ShouldGenerateExpectedXmlReportWithOverloads () = let visitor, document = diff --git a/ReleaseNotes.md b/ReleaseNotes.md index c45116a3..bf7eaed5 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -4,6 +4,7 @@ A. Start with the Quick Start guide : https://github.com/SteveGilham/altcover/wi read the FAQ : https://github.com/SteveGilham/altcover/wiki/FAQ # (Habu series release 30) +* [BUGFIX] Issue #222 - distinguish methods differeing only in number of generic parameters (JSON and cobertura in particular, but with small changes for all all output formats) * [BUGFIX] Issue #223 - degenerate source paths with Cobertura output # 8.8.53 (Habu series release 29)