Skip to content
This repository has been archived by the owner on Nov 15, 2021. It is now read-only.

Feature issue 615 f# nonpublic types #770

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 9 additions & 6 deletions main/OpenCover.Framework/Filter.cs
Expand Up @@ -379,26 +379,29 @@ public bool IsFSharpInternal(MethodDefinition method)
{
// expect 01, 00, 01, 00, 00, 00, 00, 00 or 01, 00, 02, 00, 00, 00, 00, 00
var y = x.GetBlob();
if(y.Length != 8 || y[0] != 1 || y[1] != 0 || y.Skip(3).Any(z => z!=0))
if (y.Length != 8 || y[0] != 1 || y[1] != 0 || y.Skip(3).Any(z => z != 0))
{
return false;
}

return y[2].Equals(1) // sum
|| y[2].Equals(2); // record
// Mask out the class kind from its public/non-public state
// SourceConstructFlags.NonPublicRepresentation = 32
// SourceConstructFlags.KindMask = 31
return (y[2] & 31).Equals(1) // SourceConstructFlags.SumType = 1
|| (y[2] & 31).Equals(2); // SourceConstructFlags.RecordType = 2
}).ToList();

var fieldGetter = false;
if(method.IsGetter)
if (method.IsGetter)
{
// record type has getters marked as field
var owner = method.DeclaringType.Properties
.Where(x => x.GetMethod == method)
.First();
if(owner.HasCustomAttributes)
if (owner.HasCustomAttributes)
{
fieldGetter = owner.CustomAttributes.Where(x => x.AttributeType.FullName == "Microsoft.FSharp.Core.CompilationMappingAttribute")
.Any(x => x.GetBlob()[2] == 4); // Field
.Any(x => (x.GetBlob()[2] & 31) == 4); // SourceConstructFlags.Field = 4
}
}

Expand Down
61 changes: 38 additions & 23 deletions main/OpenCover.Test/Framework/FilterTests.cs
Expand Up @@ -569,34 +569,49 @@ public void Can_Identify_Excluded_Properties()
}
}

[Test]
public void Can_Identify_Excluded_FSharp_Methods()
{
var location = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location),
@"Samples\Library1.dll");
[Test]
public void Can_Identify_Excluded_FSharp_Methods()
{
var location = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location),
@"Samples\Library1.dll");
var sourceAssembly = AssemblyDefinition.ReadAssembly(location);

var direct = sourceAssembly.MainModule.Types.ToList();
var indirect = direct
.Where(t => t.HasNestedTypes).SelectMany(t => t.NestedTypes).ToList(); // MyUnion, MyThing
var indirect2 = indirect.Where(t => t.HasNestedTypes).SelectMany(t => t.NestedTypes).ToList(); // Foo, Bar, ...
Assert.That(
indirect2.Where(t => t.HasNestedTypes).SelectMany(t => t.NestedTypes).ToList(),
Is.Empty);

var filter = new Filter(false);
var pass = direct.Concat(indirect).Concat(indirect2).SelectMany(x => x.Methods)
.Where(x => !filter.IsFSharpInternal(x))
.Select(x => x.Name)
.OrderBy(x => x)
.ToList();

var expected = new[] { "as_bar", "bytes", "makeThing", "returnBar", "returnFoo", "testMakeThing", "testMakeUnion" };

Assert.That(pass, Is.EquivalentTo(expected));
Identify_FSharp_Methods(sourceAssembly, expected);
}

[Test]
public void Can_Identify_Excluded_FSharp_NonPublic_Methods()
{
var location = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location),
@"Samples\Library2.dll");
var sourceAssembly = AssemblyDefinition.ReadAssembly(location);
var expected = new[] { "as_bar", "bytes", "makeThing", "returnBar", "returnFoo", "testMakeThing2", "testMakeUnion2" };

Identify_FSharp_Methods(sourceAssembly, expected);
}

private static void Identify_FSharp_Methods(AssemblyDefinition sourceAssembly, string[] expected)
{
var direct = sourceAssembly.MainModule.Types.ToList();
var indirect = direct
.Where(t => t.HasNestedTypes).SelectMany(t => t.NestedTypes).ToList(); // MyUnion, MyThing
var indirect2 = indirect.Where(t => t.HasNestedTypes).SelectMany(t => t.NestedTypes).ToList(); // Foo, Bar, ...
Assert.That(
indirect2.Where(t => t.HasNestedTypes).SelectMany(t => t.NestedTypes).ToList(),
Is.Empty);

var filter = new Filter(false);
var pass = direct.Concat(indirect).Concat(indirect2).SelectMany(x => x.Methods)
.Where(x => !filter.IsFSharpInternal(x))
.Select(x => x.Name)
.OrderBy(x => x)
.ToList();

Assert.That(pass, Is.EquivalentTo(expected));
}

[Test]
public void Can_Identify_Excluded_Anonymous_Issue99()
Expand Down
7 changes: 7 additions & 0 deletions main/OpenCover.Test/OpenCover.Test.csproj
Expand Up @@ -212,6 +212,13 @@
<None Include="Samples\Library1.pdb">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Samples\Library2.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Samples\Library2.fs" />
<None Include="Samples\Library2.pdb">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="Samples\ReadMe.txt" />
</ItemGroup>
<ItemGroup>
Expand Down
Binary file added main/OpenCover.Test/Samples/Library2.dll
Binary file not shown.
29 changes: 29 additions & 0 deletions main/OpenCover.Test/Samples/Library2.fs
@@ -0,0 +1,29 @@
namespace N
open NUnit.Framework
module M =
type private Thing = { Thing: string } with
member this.bytes () = System.Text.Encoding.UTF8.GetBytes(this.Thing)
let private makeThing s = { Thing = s }

[<Test>]
let testMakeThing2() =
Assert.AreEqual("s", (makeThing "s").Thing)
Assert.AreEqual(5, (makeThing "aeiou").bytes().Length)

module DU =
type private MyUnion =
| Foo of int
| Bar of string
with member this.as_bar() = match this with
| Foo n -> Bar (string n)
| bar -> bar

let private returnFoo v = Foo v

let private returnBar v = Bar v

[<Test>]
let testMakeUnion2() =
Assert.AreEqual(returnFoo 10, Foo 10)
Assert.AreEqual(returnBar "s", Bar "s")
Assert.AreEqual(Bar "10", (Foo 10).as_bar())
Binary file added main/OpenCover.Test/Samples/Library2.pdb
Binary file not shown.