Skip to content

[Bug fix]Fix instrumentation serialization bug #458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions Documentation/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,55 @@ Hits file:'C:\Users\Marco\AppData\Local\Temp\coverlet.core_703263e9-21f0-4d1c-9c
| Average | 4,44% | 3,455% | 5,66% |
+---------+--------+--------+--------+
```

## Use local build

Sometimes is useful test local updated source to fix issue.
You can "load" your local build using simple switch:

* build repo

```
D:\git\coverlet (fixjsonserializerbug -> origin)
λ dotnet build
Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

Restore completed in 52.23 ms for D:\git\coverlet\test\coverlet.testsubject\coverlet.testsubject.csproj.
Restore completed in 58.97 ms for D:\git\coverlet\src\coverlet.console\coverlet.console.csproj.
Restore completed in 59 ms for D:\git\coverlet\src\coverlet.core\coverlet.core.csproj.
Restore completed in 59.17 ms for D:\git\coverlet\src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj.
Restore completed in 59.26 ms for D:\git\coverlet\src\coverlet.collector\coverlet.collector.csproj.
Restore completed in 60.1 ms for D:\git\coverlet\test\coverlet.collector.tests\coverlet.collector.tests.csproj.
Restore completed in 60.42 ms for D:\git\coverlet\test\coverlet.core.performancetest\coverlet.core.performancetest.csproj.
Restore completed in 60.47 ms for D:\git\coverlet\test\coverlet.core.tests\coverlet.core.tests.csproj.
Restore completed in 22.85 ms for D:\git\coverlet\test\coverlet.core.tests\coverlet.core.tests.csproj.
coverlet.testsubject -> D:\git\coverlet\test\coverlet.testsubject\bin\Debug\netcoreapp2.0\coverlet.testsubject.dll
coverlet.core -> D:\git\coverlet\src\coverlet.core\bin\Debug\netstandard2.0\coverlet.core.dll
coverlet.msbuild.tasks -> D:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\coverlet.msbuild.tasks.dll
coverlet.collector -> D:\git\coverlet\src\coverlet.collector\bin\Debug\netcoreapp2.0\coverlet.collector.dll
coverlet.console -> D:\git\coverlet\src\coverlet.console\bin\Debug\netcoreapp2.2\coverlet.console.dll
coverlet.core.performancetest -> D:\git\coverlet\test\coverlet.core.performancetest\bin\Debug\netcoreapp2.0\coverlet.core.performancetest.dll
coverlet.core.tests -> D:\git\coverlet\test\coverlet.core.tests\bin\Debug\netcoreapp2.0\coverlet.core.tests.dll
coverlet.collector.tests -> D:\git\coverlet\test\coverlet.collector.tests\bin\Debug\netcoreapp2.2\coverlet.collector.tests.dll

Build succeeded.
0 Warning(s)
0 Error(s)

Time Elapsed 00:00:07.42

D:\git\coverlet (fixjsonserializerbug -> origin)
λ

```

* Go to repro project and run
```
D:\git\Cake.Codecov\Source\Cake.Codecov.Tests (develop -> origin)
λ dotnet test /p:CollectCoverage=true /p:Exclude="[xunit.*]*" /p:CoverletToolsPath=D:\git\coverlet\src\coverlet.msbuild.tasks\bin\Debug\netstandard2.0\
Test run for D:\git\Cake.Codecov\Source\Cake.Codecov.Tests\bin\Debug\netcoreapp2.0\Cake.Codecov.Tests.dll(.NETCoreApp,Version=v2.0)
...
```

In this way you can add `Debug.Launch()` inside coverlet source and debug.
35 changes: 17 additions & 18 deletions src/coverlet.core/CoveragePrepareResult.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
using System.IO;
using System.Text;

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Coverlet.Core.Instrumentation;
using Newtonsoft.Json;

namespace Coverlet.Core
{
// Followed safe serializer guide, will emit xml format
// https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2300-do-not-use-insecure-deserializer-binaryformatter?view=vs-2019
// https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2301-do-not-call-binaryformatter-deserialize-without-first-setting-binaryformatter-binder?view=vs-2019
[DataContract]
public class CoveragePrepareResult
{
[DataMember]
public string Identifier { get; set; }
[DataMember]
public string Module { get; set; }
[DataMember]
public string MergeWith { get; set; }
[DataMember]
public bool UseSourceLink { get; set; }
[DataMember]
public InstrumenterResult[] Results { get; set; }

public static CoveragePrepareResult Deserialize(Stream serializedInstrumentState)
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(serializedInstrumentState))
using (var jsonTextReader = new JsonTextReader(sr))
{
return serializer.Deserialize<CoveragePrepareResult>(jsonTextReader);
}
return (CoveragePrepareResult)new DataContractSerializer(typeof(CoveragePrepareResult)).ReadObject(serializedInstrumentState);
}

public static Stream Serialize(CoveragePrepareResult instrumentState)
{
var serializer = new JsonSerializer();
MemoryStream ms = new MemoryStream();
using (var sw = new StreamWriter(ms, Encoding.UTF8, 1024, true))
{
serializer.Serialize(sw, instrumentState);
sw.Flush();
ms.Position = 0;
return ms;
}
new DataContractSerializer(typeof(CoveragePrepareResult)).WriteObject(ms, instrumentState);
ms.Position = 0;
return ms;
}
}
}
61 changes: 33 additions & 28 deletions src/coverlet.core/Instrumentation/InstrumenterResult.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,44 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using Newtonsoft.Json;
using System.Runtime.Serialization;

namespace Coverlet.Core.Instrumentation
{
[DataContract]
public class Line
{
[DataMember]
public int Number;
[DataMember]
public string Class;
[DataMember]
public string Method;
[DataMember]
public int Hits;
}

[DataContract]
public class Branch : Line
{
[DataMember]
public int Offset;
[DataMember]
public int EndOffset;
[DataMember]
public int Path;
[DataMember]
public uint Ordinal;
}

public class BranchKeyConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return JsonConvert.DeserializeObject<BranchKey>(value.ToString());
}

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(BranchKey);
}
}

[TypeConverter(typeof(BranchKeyConverter))]
// Implements IEquatable because is used by dictionary key https://docs.microsoft.com/en-us/dotnet/api/system.iequatable-1?view=netcore-2.2#remarks
[DataContract]
public class BranchKey : IEquatable<BranchKey>
{
public BranchKey(int line, int ordinal) => (Line, Ordinal) = (line, ordinal);

[DataMember]
public int Line { get; set; }
[DataMember]
public int Ordinal { get; set; }

public override bool Equals(object obj) => Equals(obj);
Expand All @@ -57,13 +49,9 @@ public override int GetHashCode()
{
return (this.Line, this.Ordinal).GetHashCode();
}

public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}

[DataContract]
public class Document
{
public Document()
Expand All @@ -72,22 +60,32 @@ public Document()
Branches = new Dictionary<BranchKey, Branch>();
}

[DataMember]
public string Path;
[DataMember]
public int Index;
[DataMember]
public Dictionary<int, Line> Lines { get; private set; }
[DataMember]
public Dictionary<BranchKey, Branch> Branches { get; private set; }
}

[DataContract]
public class HitCandidate
{
public HitCandidate(bool isBranch, int docIndex, int start, int end) => (this.isBranch, this.docIndex, this.start, this.end) = (isBranch, docIndex, start, end);

[DataMember]
public bool isBranch { get; set; }
[DataMember]
public int docIndex { get; set; }
[DataMember]
public int start { get; set; }
[DataMember]
public int end { get; set; }
}

[DataContract]
public class InstrumenterResult
{
public InstrumenterResult()
Expand All @@ -96,12 +94,19 @@ public InstrumenterResult()
HitCandidates = new List<HitCandidate>();
}

[DataMember]
public string Module;
[DataMember]
public string[] AsyncMachineStateMethod;
[DataMember]
public string HitsFilePath;
[DataMember]
public string ModulePath;
[DataMember]
public string SourceLink;
[DataMember]
public Dictionary<string, Document> Documents { get; private set; }
[DataMember]
public List<HitCandidate> HitCandidates { get; private set; }
}
}
3 changes: 2 additions & 1 deletion src/coverlet.core/coverlet.core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<!-- Do not upgrade this version or we won't support old SDK -->
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<!--
Do not change System.Reflection.Metadata version since we need to support VSTest DataCollectors. Goto https://www.nuget.org/packages/System.Reflection.Metadata to check versions.
We need to load assembly version 1.4.2.0 to properly work
Expand Down
Loading