|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
using static Microsoft.CodeAnalysis.Test.Utilities.CSharpInstrumentationChecker;
namespace Microsoft.CodeAnalysis.CSharp.DynamicAnalysis.UnitTests
{
public class DynamicInstrumentationTests : CSharpTestBase
{
[Fact]
public void HelpersInstrumentation()
{
string source = @"
public class Program
{
public static void Main(string[] args)
{
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
Method 6
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
string expectedCreatePayloadForMethodsSpanningSingleFileIL = @"{
// Code size 21 (0x15)
.maxstack 6
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ldc.i4.1
IL_0003: newarr ""int""
IL_0008: dup
IL_0009: ldc.i4.0
IL_000a: ldarg.2
IL_000b: stelem.i4
IL_000c: ldarg.3
IL_000d: ldarg.s V_4
IL_000f: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int[], ref bool[], int)""
IL_0014: ret
}";
string expectedCreatePayloadForMethodsSpanningMultipleFilesIL = @"{
// Code size 87 (0x57)
.maxstack 3
IL_0000: ldsfld ""System.Guid Microsoft.CodeAnalysis.Runtime.Instrumentation._mvid""
IL_0005: ldarg.0
IL_0006: call ""bool System.Guid.op_Inequality(System.Guid, System.Guid)""
IL_000b: brfalse.s IL_002b
IL_000d: ldc.i4.s 100
IL_000f: newarr ""bool[]""
IL_0014: stsfld ""bool[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._payloads""
IL_0019: ldc.i4.s 100
IL_001b: newarr ""int[]""
IL_0020: stsfld ""int[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._fileIndices""
IL_0025: ldarg.0
IL_0026: stsfld ""System.Guid Microsoft.CodeAnalysis.Runtime.Instrumentation._mvid""
IL_002b: ldarg.3
IL_002c: ldarg.s V_4
IL_002e: newarr ""bool""
IL_0033: ldnull
IL_0034: call ""bool[] System.Threading.Interlocked.CompareExchange<bool[]>(ref bool[], bool[], bool[])""
IL_0039: brtrue.s IL_004f
IL_003b: ldsfld ""bool[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._payloads""
IL_0040: ldarg.1
IL_0041: ldarg.3
IL_0042: ldind.ref
IL_0043: stelem.ref
IL_0044: ldsfld ""int[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._fileIndices""
IL_0049: ldarg.1
IL_004a: ldarg.2
IL_004b: stelem.ref
IL_004c: ldarg.3
IL_004d: ldind.ref
IL_004e: ret
IL_004f: ldsfld ""bool[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._payloads""
IL_0054: ldarg.1
IL_0055: ldelem.ref
IL_0056: ret
}";
string expectedFlushPayloadIL = @"{
// Code size 288 (0x120)
.maxstack 5
.locals init (bool[] V_0,
int V_1, //i
bool[] V_2, //payload
int V_3, //j
int V_4) //j
IL_0000: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0005: ldtoken ""void Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()""
IL_000a: ldelem.ref
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0035
IL_000f: ldsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0014: ldtoken ""void Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()""
IL_0019: ldtoken Source Document 0
IL_001e: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0023: ldtoken ""void Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()""
IL_0028: ldelema ""bool[]""
IL_002d: ldc.i4.s 16
IL_002f: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)""
IL_0034: stloc.0
IL_0035: ldloc.0
IL_0036: ldc.i4.0
IL_0037: ldc.i4.1
IL_0038: stelem.i1
IL_0039: ldloc.0
IL_003a: ldc.i4.1
IL_003b: ldc.i4.1
IL_003c: stelem.i1
IL_003d: ldstr ""Flushing""
IL_0042: call ""void System.Console.WriteLine(string)""
IL_0047: ldloc.0
IL_0048: ldc.i4.3
IL_0049: ldc.i4.1
IL_004a: stelem.i1
IL_004b: ldsfld ""bool[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._payloads""
IL_0050: brtrue.s IL_0057
IL_0052: ldloc.0
IL_0053: ldc.i4.2
IL_0054: ldc.i4.1
IL_0055: stelem.i1
IL_0056: ret
IL_0057: ldloc.0
IL_0058: ldc.i4.4
IL_0059: ldc.i4.1
IL_005a: stelem.i1
IL_005b: ldc.i4.0
IL_005c: stloc.1
IL_005d: br IL_0112
IL_0062: ldloc.0
IL_0063: ldc.i4.6
IL_0064: ldc.i4.1
IL_0065: stelem.i1
IL_0066: ldsfld ""bool[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._payloads""
IL_006b: ldloc.1
IL_006c: ldelem.ref
IL_006d: stloc.2
IL_006e: ldloc.0
IL_006f: ldc.i4.s 15
IL_0071: ldc.i4.1
IL_0072: stelem.i1
IL_0073: ldloc.2
IL_0074: brfalse IL_010a
IL_0079: ldloc.0
IL_007a: ldc.i4.7
IL_007b: ldc.i4.1
IL_007c: stelem.i1
IL_007d: ldstr ""Method ""
IL_0082: ldloca.s V_1
IL_0084: call ""string int.ToString()""
IL_0089: call ""string string.Concat(string, string)""
IL_008e: call ""void System.Console.WriteLine(string)""
IL_0093: ldloc.0
IL_0094: ldc.i4.8
IL_0095: ldc.i4.1
IL_0096: stelem.i1
IL_0097: ldc.i4.0
IL_0098: stloc.3
IL_0099: br.s IL_00ca
IL_009b: ldloc.0
IL_009c: ldc.i4.s 10
IL_009e: ldc.i4.1
IL_009f: stelem.i1
IL_00a0: ldstr ""File ""
IL_00a5: ldsfld ""int[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._fileIndices""
IL_00aa: ldloc.1
IL_00ab: ldelem.ref
IL_00ac: ldloc.3
IL_00ad: ldelema ""int""
IL_00b2: call ""string int.ToString()""
IL_00b7: call ""string string.Concat(string, string)""
IL_00bc: call ""void System.Console.WriteLine(string)""
IL_00c1: ldloc.0
IL_00c2: ldc.i4.s 9
IL_00c4: ldc.i4.1
IL_00c5: stelem.i1
IL_00c6: ldloc.3
IL_00c7: ldc.i4.1
IL_00c8: add
IL_00c9: stloc.3
IL_00ca: ldloc.3
IL_00cb: ldsfld ""int[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._fileIndices""
IL_00d0: ldloc.1
IL_00d1: ldelem.ref
IL_00d2: ldlen
IL_00d3: conv.i4
IL_00d4: blt.s IL_009b
IL_00d6: ldloc.0
IL_00d7: ldc.i4.s 11
IL_00d9: ldc.i4.1
IL_00da: stelem.i1
IL_00db: ldc.i4.0
IL_00dc: stloc.s V_4
IL_00de: br.s IL_0103
IL_00e0: ldloc.0
IL_00e1: ldc.i4.s 13
IL_00e3: ldc.i4.1
IL_00e4: stelem.i1
IL_00e5: ldloc.2
IL_00e6: ldloc.s V_4
IL_00e8: ldelem.u1
IL_00e9: call ""void System.Console.WriteLine(bool)""
IL_00ee: ldloc.0
IL_00ef: ldc.i4.s 14
IL_00f1: ldc.i4.1
IL_00f2: stelem.i1
IL_00f3: ldloc.2
IL_00f4: ldloc.s V_4
IL_00f6: ldc.i4.0
IL_00f7: stelem.i1
IL_00f8: ldloc.0
IL_00f9: ldc.i4.s 12
IL_00fb: ldc.i4.1
IL_00fc: stelem.i1
IL_00fd: ldloc.s V_4
IL_00ff: ldc.i4.1
IL_0100: add
IL_0101: stloc.s V_4
IL_0103: ldloc.s V_4
IL_0105: ldloc.2
IL_0106: ldlen
IL_0107: conv.i4
IL_0108: blt.s IL_00e0
IL_010a: ldloc.0
IL_010b: ldc.i4.5
IL_010c: ldc.i4.1
IL_010d: stelem.i1
IL_010e: ldloc.1
IL_010f: ldc.i4.1
IL_0110: add
IL_0111: stloc.1
IL_0112: ldloc.1
IL_0113: ldsfld ""bool[][] Microsoft.CodeAnalysis.Runtime.Instrumentation._payloads""
IL_0118: ldlen
IL_0119: conv.i4
IL_011a: blt IL_0062
IL_011f: ret
}";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyIL("Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)", expectedCreatePayloadForMethodsSpanningSingleFileIL);
verifier.VerifyIL("Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int[], ref bool[], int)", expectedCreatePayloadForMethodsSpanningMultipleFilesIL);
verifier.VerifyIL("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload", expectedFlushPayloadIL);
verifier.VerifyDiagnostics();
}
[Fact]
public void GotoCoverage()
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args)
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain()
{
Console.WriteLine(""goo"");
goto bar;
Console.Write(""you won't see me"");
bar: Console.WriteLine(""bar"");
Fred();
return;
}
static void Wilma()
{
Betty(true);
Barney(true);
Barney(false);
Betty(true);
}
static int Barney(bool b)
{
if (b)
return 10;
if (b)
return 100;
return 20;
}
static int Betty(bool b)
{
if (b)
return 30;
if (b)
return 100;
return 40;
}
static void Fred()
{
Wilma();
}
}
";
string expectedOutput = @"goo
bar
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
False
True
True
True
Method 5
File 1
True
True
True
True
True
Method 6
File 1
True
True
True
False
True
True
Method 7
File 1
True
True
True
False
False
False
Method 8
File 1
True
True
Method 11
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
string expectedBarneyIL = @"{
// Code size 91 (0x5b)
.maxstack 5
.locals init (bool[] V_0)
IL_0000: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0005: ldtoken ""int Program.Barney(bool)""
IL_000a: ldelem.ref
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0034
IL_000f: ldsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0014: ldtoken ""int Program.Barney(bool)""
IL_0019: ldtoken Source Document 0
IL_001e: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0023: ldtoken ""int Program.Barney(bool)""
IL_0028: ldelema ""bool[]""
IL_002d: ldc.i4.6
IL_002e: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)""
IL_0033: stloc.0
IL_0034: ldloc.0
IL_0035: ldc.i4.0
IL_0036: ldc.i4.1
IL_0037: stelem.i1
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: ldc.i4.1
IL_003b: stelem.i1
IL_003c: ldarg.0
IL_003d: brfalse.s IL_0046
IL_003f: ldloc.0
IL_0040: ldc.i4.1
IL_0041: ldc.i4.1
IL_0042: stelem.i1
IL_0043: ldc.i4.s 10
IL_0045: ret
IL_0046: ldloc.0
IL_0047: ldc.i4.4
IL_0048: ldc.i4.1
IL_0049: stelem.i1
IL_004a: ldarg.0
IL_004b: brfalse.s IL_0054
IL_004d: ldloc.0
IL_004e: ldc.i4.3
IL_004f: ldc.i4.1
IL_0050: stelem.i1
IL_0051: ldc.i4.s 100
IL_0053: ret
IL_0054: ldloc.0
IL_0055: ldc.i4.5
IL_0056: ldc.i4.1
IL_0057: stelem.i1
IL_0058: ldc.i4.s 20
IL_005a: ret
}";
string expectedPIDStaticConstructorIL = @"{
// Code size 33 (0x21)
.maxstack 2
IL_0000: ldtoken Max Method Token Index
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: newarr ""bool[]""
IL_000c: stsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0011: ldstr ##MVID##
IL_0016: newobj ""System.Guid..ctor(string)""
IL_001b: stsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0020: ret
}";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyIL("Program.Barney", expectedBarneyIL);
verifier.VerifyIL("<PrivateImplementationDetails>..cctor", expectedPIDStaticConstructorIL);
verifier.VerifyDiagnostics(Diagnostic(ErrorCode.WRN_UnreachableCode, "Console").WithLocation(16, 9));
}
[Fact]
public void MethodsOfGenericTypesCoverage()
{
string source = @"
using System;
class MyBox<T> where T : class
{
readonly T _value;
public MyBox(T value)
{
_value = value;
}
public T GetValue()
{
if (_value == null)
{
return null;
}
return _value;
}
}
public class Program
{
public static void Main(string[] args)
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain()
{
MyBox<object> x = new MyBox<object>(null);
Console.WriteLine(x.GetValue() == null ? ""null"" : x.GetValue().ToString());
MyBox<string> s = new MyBox<string>(""Hello"");
Console.WriteLine(s.GetValue() == null ? ""null"" : s.GetValue());
}
}
";
// All instrumentation points in method 4 are True because they are covered by at least one specialization.
//
// This test verifies that the payloads of methods of generic types are in terms of method definitions and
// not method references -- the indices for the methods would be different for references.
string expectedOutput = @"null
Hello
Flushing
Method 3
File 1
True
True
Method 4
File 1
True
True
True
True
Method 5
File 1
True
True
True
Method 6
File 1
True
True
True
True
True
Method 9
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
string expectedReleaseGetValueIL = @"{
// Code size 98 (0x62)
.maxstack 5
.locals init (bool[] V_0,
T V_1)
IL_0000: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0005: ldtoken ""T MyBox<T>.GetValue()""
IL_000a: ldelem.ref
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0034
IL_000f: ldsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0014: ldtoken ""T MyBox<T>.GetValue()""
IL_0019: ldtoken Source Document 0
IL_001e: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0023: ldtoken ""T MyBox<T>.GetValue()""
IL_0028: ldelema ""bool[]""
IL_002d: ldc.i4.4
IL_002e: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)""
IL_0033: stloc.0
IL_0034: ldloc.0
IL_0035: ldc.i4.0
IL_0036: ldc.i4.1
IL_0037: stelem.i1
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: ldc.i4.1
IL_003b: stelem.i1
IL_003c: ldarg.0
IL_003d: ldfld ""T MyBox<T>._value""
IL_0042: box ""T""
IL_0047: brtrue.s IL_0057
IL_0049: ldloc.0
IL_004a: ldc.i4.1
IL_004b: ldc.i4.1
IL_004c: stelem.i1
IL_004d: ldloca.s V_1
IL_004f: initobj ""T""
IL_0055: ldloc.1
IL_0056: ret
IL_0057: ldloc.0
IL_0058: ldc.i4.3
IL_0059: ldc.i4.1
IL_005a: stelem.i1
IL_005b: ldarg.0
IL_005c: ldfld ""T MyBox<T>._value""
IL_0061: ret
}";
string expectedDebugGetValueIL = @"{
// Code size 110 (0x6e)
.maxstack 5
.locals init (bool[] V_0,
bool V_1,
T V_2,
T V_3)
IL_0000: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0005: ldtoken ""T MyBox<T>.GetValue()""
IL_000a: ldelem.ref
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0034
IL_000f: ldsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0014: ldtoken ""T MyBox<T>.GetValue()""
IL_0019: ldtoken Source Document 0
IL_001e: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0023: ldtoken ""T MyBox<T>.GetValue()""
IL_0028: ldelema ""bool[]""
IL_002d: ldc.i4.4
IL_002e: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)""
IL_0033: stloc.0
IL_0034: ldloc.0
IL_0035: ldc.i4.0
IL_0036: ldc.i4.1
IL_0037: stelem.i1
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: ldc.i4.1
IL_003b: stelem.i1
IL_003c: ldarg.0
IL_003d: ldfld ""T MyBox<T>._value""
IL_0042: box ""T""
IL_0047: ldnull
IL_0048: ceq
IL_004a: stloc.1
IL_004b: ldloc.1
IL_004c: brfalse.s IL_005f
IL_004e: nop
IL_004f: ldloc.0
IL_0050: ldc.i4.1
IL_0051: ldc.i4.1
IL_0052: stelem.i1
IL_0053: ldloca.s V_2
IL_0055: initobj ""T""
IL_005b: ldloc.2
IL_005c: stloc.3
IL_005d: br.s IL_006c
IL_005f: ldloc.0
IL_0060: ldc.i4.3
IL_0061: ldc.i4.1
IL_0062: stelem.i1
IL_0063: ldarg.0
IL_0064: ldfld ""T MyBox<T>._value""
IL_0069: stloc.3
IL_006a: br.s IL_006c
IL_006c: ldloc.3
IL_006d: ret
}";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyIL("MyBox<T>.GetValue", expectedReleaseGetValueIL);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput, options: TestOptions.DebugExe);
verifier.VerifyIL("MyBox<T>.GetValue", expectedDebugGetValueIL);
verifier.VerifyDiagnostics();
}
[Fact]
public void NonStaticImplicitBlockMethodsCoverage()
{
string source = @"
public class Program
{
public int Prop { get; }
public int Prop2 { get; } = 25;
public int Prop3 { get; set; } // Methods 3 and 4
public Program() // Method 5
{
Prop = 12;
Prop3 = 12;
Prop2 = Prop3;
}
public static void Main(string[] args) // Method 6
{
new Program();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(5, 1, "public int Prop3")
.True("get");
checker.Method(6, 1, "public int Prop3")
.True("set");
checker.Method(7, 1, "public Program()")
.True("25")
.True("Prop = 12;")
.True("Prop3 = 12;")
.True("Prop2 = Prop3;");
checker.Method(8, 1, "public static void Main")
.True("new Program();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();");
checker.Method(10, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
CompilationVerifier verifier = CompileAndVerify(source, expectedOutput: checker.ExpectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
checker.CompleteCheck(verifier.Compilation, source);
verifier = CompileAndVerify(source, expectedOutput: checker.ExpectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
checker.CompleteCheck(verifier.Compilation, source);
}
[Fact]
public void ImplicitBlockMethodsCoverage()
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
int x = Count;
x += Prop;
Prop = x;
x += Prop2;
Lambda(x, (y) => y + 1);
}
static int Function(int x) => x;
static int Count => Function(44);
static int Prop { get; set; }
static int Prop2 { get; set; } = 12;
static int Lambda(int x, Func<int, int> l)
{
return l(x);
}
// Method 11 is a synthesized static constructor.
}
";
// There is no entry for method '10' since it's a Prop2_set which is never called.
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
True
True
True
True
Method 5
File 1
True
True
Method 6
File 1
True
True
Method 7
File 1
True
True
Method 8
File 1
True
True
Method 9
File 1
True
True
Method 11
File 1
True
True
Method 13
File 1
True
Method 15
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
}
[Fact]
public void LocalFunctionWithLambdaCoverage()
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
new D().M1();
}
}
public class D
{
public void M1() // Method 4
{
L1();
void L1()
{
var f = new Func<int>(
() => 1
);
var f1 = new Func<int>(
() => 2
);
var f2 = new Func<int, int>(
(x) => x + 3
);
var f3 = new Func<int, int>(
x => x + 4
);
f();
f3(2);
}
}
// Method 5 is the synthesized instance constructor for D.
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(3, 1, "public static void Main")
.True("TestMain();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();");
checker.Method(4, 1, "static void TestMain")
.True("new D().M1();");
checker.Method(6, 1, "public void M1()")
.True("L1();")
.True("1")
.True("var f = new Func<int>")
.False("2")
.True("var f1 = new Func<int>")
.False("x + 3")
.True("var f2 = new Func<int, int>")
.True("x + 4")
.True("var f3 = new Func<int, int>")
.True("f();")
.True("f3(2);");
checker.Method(7, 1, snippet: null, expectBodySpan: false);
checker.Method(9, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
CompilationVerifier verifier = CompileAndVerify(source, expectedOutput: checker.ExpectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
checker.CompleteCheck(verifier.Compilation, source);
verifier = CompileAndVerify(source, expectedOutput: checker.ExpectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
checker.CompleteCheck(verifier.Compilation, source);
}
[Fact]
public void MultipleFilesCoverage()
{
string source = @"
public class Program
{
#line 10 ""File1.cs""
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
#line 20 ""File2.cs""
static void TestMain() // Method 2
{
Fred();
Program p = new Program();
return;
}
#line 30 ""File3.cs""
static void Fred() // Method 3
{
return;
}
#line 40 ""File5.cs""
// The synthesized instance constructor is method 4 and
// appears in the original source file, which gets file index 4.
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 2
True
True
True
True
Method 5
File 3
True
True
Method 6
File 4
Method 8
File 5
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
}
[Fact]
public void MultipleDeclarationsCoverage()
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
int x;
int a, b;
DoubleDeclaration(5);
DoubleForDeclaration(5);
}
static int DoubleDeclaration(int x) // Method 3
{
int c = x;
int a, b;
int f;
a = b = f = c;
int d = a, e = b;
return d + e + f;
}
static int DoubleForDeclaration(int x) // Method 4
{
for(int a = x, b = x; a + b < 10; a++)
{
Console.WriteLine(""Cannot get here."");
x++;
}
return x;
}
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
Method 5
File 1
True
True
True
True
True
True
Method 6
File 1
True
True
True
False
False
False
True
Method 9
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics(
Diagnostic(ErrorCode.WRN_UnreferencedVar, "x").WithArguments("x").WithLocation(14, 13),
Diagnostic(ErrorCode.WRN_UnreferencedVar, "a").WithArguments("a").WithLocation(15, 13),
Diagnostic(ErrorCode.WRN_UnreferencedVar, "b").WithArguments("b").WithLocation(15, 16));
}
[Fact]
public void UsingAndFixedCoverage()
{
string source = @"
using System.IO;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
using (var memoryStream = new MemoryStream())
{
;
}
using (MemoryStream s1 = new MemoryStream(), s2 = new MemoryStream())
{
;
}
var otherStream = new MemoryStream();
using (otherStream)
{
;
}
unsafe
{
double[] a = { 1, 2, 3 };
fixed(double* p = a)
{
;
}
fixed(double* q = a, r = a)
{
;
}
}
}
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
Method 7
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.UnsafeDebugExe, expectedOutput: expectedOutput, verify: Verification.Fails);
verifier.VerifyDiagnostics();
}
[Fact]
public void ManyStatementsCoverage() // Method 3
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args)
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain()
{
VariousStatements(2);
Empty();
}
static void VariousStatements(int z)
{
int x = z + 10;
switch (z)
{
case 1:
break;
case 2:
break;
case 3:
break;
default:
break;
}
if (x > 10)
{
x++;
}
else
{
x--;
}
for (int y = 0; y < 50; y++)
{
if (y < 30)
{
x++;
continue;
}
else
break;
}
int[] a = new int[] { 1, 2, 3, 4 };
foreach (int i in a)
{
x++;
}
while (x < 100)
{
x++;
}
try
{
x++;
if (x > 10)
{
throw new System.Exception();
}
x++;
}
catch (System.Exception)
{
x++;
}
finally
{
x++;
}
lock (new object())
{
;
}
Console.WriteLine(x);
try
{
using ((System.IDisposable)new object())
{
;
}
}
catch (System.Exception)
{
}
// Include an infinite loop to make sure that a compiler optimization doesn't eliminate the instrumentation.
while (true)
{
return;
}
}
static void Empty() // Method 4
{
}
}
";
string expectedOutput = @"103
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
Method 5
File 1
True
True
False
True
False
False
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
True
True
False
True
True
True
True
True
False
True
True
True
Method 6
File 1
True
Method 9
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
[Fact]
public void PatternsCoverage()
{
string source = @"
public class C
{
public static void Main()
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
Student s = new Student();
s.Name = ""Bozo"";
s.GPA = s.Name switch { _ => 2.3 }; // switch expression is not instrumented
Operate(s);
}
static string Operate(Person p) // Method 3
{
switch (p)
{
case Student s when s.GPA > 3.5:
return $""Student {s.Name} ({s.GPA:N1})"";
case Student s:
return $""Student {s.Name} ({s.GPA:N1})"";
case Teacher t:
return $""Teacher {t.Name} of {t.Subject}"";
default:
return $""Person {p.Name}"";
}
}
}
class Person { public string Name; }
class Teacher : Person { public string Subject = string.Empty; }
class Student : Person { public double GPA; }
// Methods 7 and 9 are implicit constructors.
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
True
True
Method 5
File 1
True
True
False
True
False
False
True
Method 7
File 1
Method 9
File 1
Method 11
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
/// <see cref="DynamicAnalysisResourceTests.TestPatternSpans_WithSharedWhenExpression"/>
/// for meaning of the spans
[Fact]
public void PatternsCoverage_WithSharedWhenExpression()
{
string source = @"
using System;
public class C
{
public static void Main()
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
public static void TestMain()
{
Method3(1, b1: false, b2: false);
}
static string Method3(int i, bool b1, bool b2) // Method 3
{
switch (i)
{
case not 1 when b1:
return ""b1"";
case var _ when b2:
return ""b2"";
case 1:
return ""1"";
default:
return ""default"";
}
}
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
Method 5
File 1
True
False
True
False
False
True
False
True
Method 8
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
}
[Fact]
public void DeconstructionStatementCoverage()
{
string source = @"
using System;
public class C
{
public static void Main() // Method 1
{
TestMain2();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain2() // Method 2
{
var (x, y) = new C();
}
static void TestMain3() // Method 3
{
var (x, y) = new C();
}
public C() // Method 4
{
}
public void Deconstruct(out int x, out int y) // Method 5
{
x = 1;
y = 1 switch { 1 => 2, 3 => 4, _ => 5 }; // switch expression is not instrumented
}
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
Method 6
File 1
True
Method 7
File 1
True
True
True
Method 9
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
}
[Fact]
public void DeconstructionForeachStatementCoverage()
{
string source = @"
using System;
public class C
{
public static void Main() // Method 1
{
TestMain2(new C[] { new C() });
TestMain3(new C[] { });
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain2(C[] a) // Method 2
{
foreach (
var (x, y)
in a)
;
}
static void TestMain3(C[] a) // Method 3
{
foreach (
var (x, y)
in a)
;
}
public C() // Method 4
{
}
public void Deconstruct(out int x, out int y) // Method 5
{
x = 1;
y = 2;
}
}
";
string expectedOutput = @"Flushing
Method 3
File 1
True
True
True
True
Method 4
File 1
True
True
True
Method 5
File 1
True
False
False
Method 6
File 1
True
Method 7
File 1
True
True
True
Method 9
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
}
[Fact]
public void LambdaCoverage()
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); // Method 2
}
static void TestMain()
{
int y = 5;
Func<int, int> tester = (x) =>
{
while (x > 10)
{
return y;
}
return x;
};
y = 75;
if (tester(20) > 50)
Console.WriteLine(""OK"");
else
Console.WriteLine(""Bad"");
}
}
";
string expectedOutput = @"OK
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
True
False
True
True
True
False
True
Method 7
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
[Fact]
public void AsyncCoverage()
{
string source = @"
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
Console.WriteLine(Outer(""Goo"").Result);
}
async static Task<string> Outer(string s) // Method 3
{
string s1 = await First(s);
string s2 = await Second(s);
return s1 + s2;
}
async static Task<string> First(string s) // Method 4
{
string result = await Second(s) + ""Glue"";
if (result.Length > 2)
return result;
else
return ""Too short"";
}
async static Task<string> Second(string s) // Method 5
{
string doubled = """";
if (s.Length > 2)
doubled = s + s;
else
doubled = ""HuhHuh"";
return await Task.Factory.StartNew(() => doubled);
}
}
";
string expectedOutput = @"GooGooGlueGooGoo
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
Method 5
File 1
True
True
True
True
Method 6
File 1
True
True
True
False
True
Method 7
File 1
True
True
True
False
True
True
True
Method 10
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
[Fact]
public void IteratorCoverage()
{
string source = @"
using System;
public class Program
{
public static void Main(string[] args) // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
foreach (var i in Goo())
{
Console.WriteLine(i);
}
foreach (var i in Goo())
{
Console.WriteLine(i);
}
}
public static System.Collections.Generic.IEnumerable<int> Goo()
{
for (int i = 0; i < 5; ++i)
{
yield return i;
}
}
}
";
string expectedOutput = @"0
1
2
3
4
0
1
2
3
4
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
True
True
Method 5
File 1
True
True
True
True
Method 8
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestFieldInitializerCoverage()
{
string source = @"
public class C
{
public static void Main() // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
C local = new C(); local = new C(1, s_z);
}
static int Init() => 33; // Method 3
C() // Method 4
{
_z = 12;
}
static C() // Method 5
{
s_z = 123;
}
int _x = Init();
int _y = Init() + 12;
int _z;
static int s_x = Init();
static int s_y = Init() + 153;
static int s_z;
C(int x) // Method 6
{
_z = x;
}
C(int a, int b) // Method 7
{
_z = a + b;
}
int Prop1 { get; } = 15;
static int Prop2 { get; } = 255;
}
";
string expectedOutput = @"
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
Method 5
File 1
True
True
Method 6
File 1
True
True
True
True
True
Method 7
File 1
True
True
True
True
True
Method 9
File 1
True
True
True
True
True
Method 13
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
verifier.VerifyMethodBody("C..ctor", @"
{
// Code size 120 (0x78)
.maxstack 5
.locals init (bool[] V_0)
// sequence point: <hidden>
IL_0000: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0005: ldtoken ""C..ctor()""
IL_000a: ldelem.ref
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0034
IL_000f: ldsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0014: ldtoken ""C..ctor()""
IL_0019: ldtoken Source Document 0
IL_001e: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0023: ldtoken ""C..ctor()""
IL_0028: ldelema ""bool[]""
IL_002d: ldc.i4.5
IL_002e: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)""
IL_0033: stloc.0
IL_0034: ldloc.0
IL_0035: ldc.i4.0
IL_0036: ldc.i4.1
IL_0037: stelem.i1
// sequence point: int _x = Init();
IL_0038: ldloc.0
IL_0039: ldc.i4.1
IL_003a: ldc.i4.1
IL_003b: stelem.i1
IL_003c: ldarg.0
IL_003d: call ""int C.Init()""
IL_0042: stfld ""int C._x""
// sequence point: int _y = Init() + 12;
IL_0047: ldloc.0
IL_0048: ldc.i4.2
IL_0049: ldc.i4.1
IL_004a: stelem.i1
IL_004b: ldarg.0
IL_004c: call ""int C.Init()""
IL_0051: ldc.i4.s 12
IL_0053: add
IL_0054: stfld ""int C._y""
// sequence point: 15
IL_0059: ldloc.0
IL_005a: ldc.i4.3
IL_005b: ldc.i4.1
IL_005c: stelem.i1
IL_005d: ldarg.0
IL_005e: ldc.i4.s 15
IL_0060: stfld ""int C.<Prop1>k__BackingField""
// sequence point: C()
IL_0065: ldarg.0
IL_0066: call ""object..ctor()""
// sequence point: _z = 12;
IL_006b: ldloc.0
IL_006c: ldc.i4.4
IL_006d: ldc.i4.1
IL_006e: stelem.i1
IL_006f: ldarg.0
IL_0070: ldc.i4.s 12
IL_0072: stfld ""int C._z""
// sequence point: }
IL_0077: ret
}
");
}
[Fact]
public void TestImplicitConstructorCoverage()
{
string source = @"
public class C
{
public static void Main() // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
C local = new C();
int x = local._x + local._y + C.s_x + C.s_y + C.s_z;
}
static int Init() => 33; // Method 3
// Method 6 is the implicit instance constructor.
// Method 7 is the implicit shared constructor.
int _x = Init();
int _y = Init() + 12;
static int s_x = Init();
static int s_y = Init() + 153;
static int s_z = 144;
int Prop1 { get; } = 15;
static int Prop2 { get; } = 255;
}
";
string expectedOutput = @"
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
Method 5
File 1
True
True
Method 8
File 1
True
True
True
Method 9
File 1
True
True
True
True
Method 11
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestImplicitConstructorsWithLambdasCoverage()
{
string source = @"
using System;
public class C
{
public static void Main() // Method 1
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void TestMain() // Method 2
{
int y = s_c._function();
D d = new D();
int z = d._c._function();
int zz = D.s_c._function();
int zzz = d._c1._function();
}
public C(Func<int> f) // Method 3
{
_function = f;
}
static C s_c = new C(() => 115);
Func<int> _function;
}
partial class D
{
}
partial class D
{
}
partial class D
{
public C _c = new C(() => 120);
public static C s_c = new C(() => 144);
public C _c1 = new C(() => 130);
public static C s_c1 = new C(() => 156);
}
partial class D
{
}
partial struct E
{
}
partial struct E
{
public static C s_c = new C(() => 1444);
public static C s_c1 = new C(() => { return 1567; });
}
// Method 4 is the synthesized static constructor for C.
// Method 5 is the synthesized instance constructor for D.
// Method 6 is the synthesized static constructor for D.
";
string expectedOutput = @"
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
True
True
True
True
True
True
Method 5
File 1
True
True
Method 6
File 1
True
True
Method 7
File 1
True
True
True
True
Method 8
File 1
True
False
True
True
Method 11
File 1
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
CompilationVerifier verifier = CompileAndVerify(source + InstrumentationHelperSource, expectedOutput: expectedOutput);
verifier.VerifyDiagnostics();
}
[Fact]
public void MissingMethodNeededForAnalysis()
{
string source = @"
namespace System
{
public class Object { }
public struct Int32 { }
public struct Boolean { }
public class String { }
public class Exception { }
public class ValueType { }
public class Enum { }
public struct Void { }
public class Guid { }
}
public class Console
{
public static void WriteLine(string s) { }
public static void WriteLine(int i) { }
public static void WriteLine(bool b) { }
}
public class Program
{
public static void Main(string[] args)
{
TestMain();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static int TestMain()
{
return 3;
}
}
";
ImmutableArray<Diagnostic> diagnostics = CreateEmptyCompilation(source + InstrumentationHelperSource).GetEmitDiagnostics(EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)));
foreach (Diagnostic diagnostic in diagnostics)
{
if (diagnostic.Code == (int)ErrorCode.ERR_MissingPredefinedMember &&
diagnostic.Arguments[0].Equals("System.Guid") && diagnostic.Arguments[1].Equals(".ctor"))
{
return;
}
}
Assert.True(false);
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_Method()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
[ExcludeFromCodeCoverage]
void M1() { Console.WriteLine(1); }
void M2() { Console.WriteLine(1); }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C.M1");
AssertInstrumented(verifier, "C.M2");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_Ctor()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
int a = 1;
[ExcludeFromCodeCoverage]
public C() { Console.WriteLine(3); }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C..ctor");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_Cctor()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
static int a = 1;
[ExcludeFromCodeCoverage]
static C() { Console.WriteLine(3); }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C..cctor");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LocalFunctionsAndLambdas_InMethod()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
[ExcludeFromCodeCoverage]
static void M1() { L1(); void L1() { new Action(() => { Console.WriteLine(1); }).Invoke(); } }
static void M2() { L2(); void L2() { new Action(() => { Console.WriteLine(2); }).Invoke(); } }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C.M1");
AssertNotInstrumented(verifier, "C.<M1>g__L1|0_0");
AssertNotInstrumented(verifier, "C.<>c.<M1>b__0_1");
AssertInstrumented(verifier, "C.M2");
AssertInstrumented(verifier, "C.<>c__DisplayClass1_0.<M2>g__L2|0"); // M2:L2
AssertInstrumented(verifier, "C.<>c__DisplayClass1_0.<M2>b__1"); // M2:L2 lambda
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LocalFunctionAttributes()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
static void M1()
{
L1();
[ExcludeFromCodeCoverage]
void L1() { new Action(() => { Console.WriteLine(1); }).Invoke(); }
}
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource,
options: TestOptions.ReleaseDll,
parseOptions: TestOptions.Regular9);
AssertInstrumented(verifier, "C.M1");
AssertNotInstrumented(verifier, "C.<M1>g__L1|0_0()");
AssertNotInstrumented(verifier, "C.<>c.<M1>b__0_1()");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LocalFunctionAttributes_Multiple()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
static void M1()
{
#pragma warning disable 8321 // Unreferenced local function
void L1() { Console.WriteLine(1); }
[ExcludeFromCodeCoverage]
void L2() { Console.WriteLine(2); }
void L3() { Console.WriteLine(3); }
}
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource,
options: TestOptions.ReleaseDll,
parseOptions: TestOptions.Regular9);
AssertInstrumented(verifier, "C.M1");
AssertInstrumented(verifier, "C.<M1>g__L1|0_0(ref C.<>c__DisplayClass0_0)");
AssertNotInstrumented(verifier, "C.<M1>g__L2|0_1()");
AssertInstrumented(verifier, "C.<M1>g__L3|0_2(ref C.<>c__DisplayClass0_0)");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LocalFunctionAttributes_Nested()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
static void M1()
{
L1();
void L1()
{
new Action(() =>
{
L2();
[ExcludeFromCodeCoverage]
void L2()
{
new Action(() =>
{
L3();
void L3()
{
Console.WriteLine(1);
}
}).Invoke();
}
}).Invoke();
}
}
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource,
options: TestOptions.ReleaseDll,
parseOptions: TestOptions.Regular9);
AssertInstrumented(verifier, "C.M1");
AssertInstrumented(verifier, "C.<>c__DisplayClass0_0.<M1>g__L1|0()");
AssertInstrumented(verifier, "C.<>c__DisplayClass0_0.<M1>b__1()");
AssertNotInstrumented(verifier, "C.<M1>g__L2|0_2()");
AssertNotInstrumented(verifier, "C.<>c.<M1>b__0_3");
AssertNotInstrumented(verifier, "C.<M1>g__L3|0_4()");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LocalFunctionsAndLambdas_InInitializers()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
Action IF = new Action(() => { Console.WriteLine(1); });
Action IP { get; } = new Action(() => { Console.WriteLine(2); });
static Action SF = new Action(() => { Console.WriteLine(3); });
static Action SP { get; } = new Action(() => { Console.WriteLine(4); });
[ExcludeFromCodeCoverage]
C() {}
static C() {}
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C..ctor");
AssertNotInstrumented(verifier, "C.<>c.<.ctor>b__8_0");
AssertNotInstrumented(verifier, "C.<>c.<.ctor>b__8_1");
AssertInstrumented(verifier, "C..cctor");
AssertInstrumented(verifier, "C.<>c__DisplayClass9_0.<.cctor>b__0");
AssertInstrumented(verifier, "C.<>c__DisplayClass9_0.<.cctor>b__1");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LocalFunctionsAndLambdas_InAccessors()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
[ExcludeFromCodeCoverage]
int P1
{
get { L1(); void L1() { Console.WriteLine(1); } return 1; }
set { L2(); void L2() { Console.WriteLine(2); } }
}
int P2
{
get { L3(); void L3() { Console.WriteLine(3); } return 3; }
set { L4(); void L4() { Console.WriteLine(4); } }
}
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C.P1.get");
AssertNotInstrumented(verifier, "C.P1.set");
AssertNotInstrumented(verifier, "C.<get_P1>g__L1|1_0");
AssertNotInstrumented(verifier, "C.<set_P1>g__L2|2_0");
AssertInstrumented(verifier, "C.P2.get");
AssertInstrumented(verifier, "C.P2.set");
AssertInstrumented(verifier, "C.<get_P2>g__L3|4_0");
AssertInstrumented(verifier, "C.<set_P2>g__L4|5_0");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_LambdaAttributes()
{
string source =
@"using System;
using System.Diagnostics.CodeAnalysis;
class Program
{
static void M1()
{
Action a1 = static () =>
{
Func<bool, int> f1 = [ExcludeFromCodeCoverage] static (bool b) => { if (b) return 0; return 1; };
Func<bool, int> f2 = static (bool b) => { if (b) return 0; return 1; };
};
}
static void M2()
{
Action a2 = [ExcludeFromCodeCoverage] static () =>
{
Func<bool, int> f3 = [ExcludeFromCodeCoverage] static (bool b) => { if (b) return 0; return 1; };
Func<bool, int> f4 = static (bool b) => { if (b) return 0; return 1; };
};
}
}";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview);
AssertInstrumented(verifier, "Program.M1");
AssertInstrumented(verifier, "Program.<>c__DisplayClass0_0.<M1>b__0()");
AssertNotInstrumented(verifier, "Program.<>c.<M1>b__0_1(bool)");
AssertInstrumented(verifier, "Program.<>c__DisplayClass0_0.<M1>b__2(bool)");
AssertInstrumented(verifier, "Program.M2");
AssertNotInstrumented(verifier, "Program.<>c.<M2>b__1_0()");
AssertNotInstrumented(verifier, "Program.<>c.<M2>b__1_1(bool)");
AssertNotInstrumented(verifier, "Program.<>c.<M2>b__1_2(bool)");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_Type()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
[ExcludeFromCodeCoverage]
class C
{
int x = 1;
static C() { }
void M1() { Console.WriteLine(1); }
int P { get => 1; set { } }
event Action E { add { } remove { } }
}
class D
{
int x = 1;
static D() { }
void M1() { Console.WriteLine(1); }
int P { get => 1; set { } }
event Action E { add { } remove { } }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C..ctor");
AssertNotInstrumented(verifier, "C..cctor");
AssertNotInstrumented(verifier, "C.M1");
AssertNotInstrumented(verifier, "C.P.get");
AssertNotInstrumented(verifier, "C.P.set");
AssertNotInstrumented(verifier, "C.E.add");
AssertNotInstrumented(verifier, "C.E.remove");
AssertInstrumented(verifier, "D..ctor");
AssertInstrumented(verifier, "D..cctor");
AssertInstrumented(verifier, "D.M1");
AssertInstrumented(verifier, "D.P.get");
AssertInstrumented(verifier, "D.P.set");
AssertInstrumented(verifier, "D.E.add");
AssertInstrumented(verifier, "D.E.remove");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_NestedType()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class A
{
class B1
{
[ExcludeFromCodeCoverage]
class C
{
void M1() { Console.WriteLine(1); }
}
void M2() { Console.WriteLine(2); }
}
[ExcludeFromCodeCoverage]
partial class B2
{
partial class C1
{
void M3() { Console.WriteLine(3); }
}
class C2
{
void M4() { Console.WriteLine(4); }
}
void M5() { Console.WriteLine(5); }
}
partial class B2
{
[ExcludeFromCodeCoverage]
partial class C1
{
void M6() { Console.WriteLine(6); }
}
void M7() { Console.WriteLine(7); }
}
void M8() { Console.WriteLine(8); }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "A.B1.C.M1");
AssertInstrumented(verifier, "A.B1.M2");
AssertNotInstrumented(verifier, "A.B2.C1.M3");
AssertNotInstrumented(verifier, "A.B2.C2.M4");
AssertNotInstrumented(verifier, "A.B2.C1.M6");
AssertNotInstrumented(verifier, "A.B2.M7");
AssertInstrumented(verifier, "A.M8");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_Accessors()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
[ExcludeFromCodeCoverage]
int P1 { get => 1; set {} }
[ExcludeFromCodeCoverage]
event Action E1 { add { } remove { } }
int P2 { get => 1; set {} }
event Action E2 { add { } remove { } }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
AssertNotInstrumented(verifier, "C.P1.get");
AssertNotInstrumented(verifier, "C.P1.set");
AssertNotInstrumented(verifier, "C.E1.add");
AssertNotInstrumented(verifier, "C.E1.remove");
AssertInstrumented(verifier, "C.P2.get");
AssertInstrumented(verifier, "C.P2.set");
AssertInstrumented(verifier, "C.E2.add");
AssertInstrumented(verifier, "C.E2.remove");
}
[CompilerTrait(CompilerFeature.InitOnlySetters)]
[Fact]
public void ExcludeFromCodeCoverageAttribute_Accessors_Init()
{
string source = @"
using System;
using System.Diagnostics.CodeAnalysis;
class C
{
[ExcludeFromCodeCoverage]
int P1 { get => 1; init {} }
int P2 { get => 1; init {} }
}
";
var verifier = CompileAndVerify(source + InstrumentationHelperSource + IsExternalInitTypeDefinition,
options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular9);
AssertNotInstrumented(verifier, "C.P1.get");
AssertNotInstrumented(verifier, "C.P1.init");
AssertInstrumented(verifier, "C.P2.get");
AssertInstrumented(verifier, "C.P2.init");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_CustomDefinition_Good()
{
string source = @"
using System.Diagnostics.CodeAnalysis;
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class ExcludeFromCodeCoverageAttribute : Attribute
{
public ExcludeFromCodeCoverageAttribute() {}
}
}
[ExcludeFromCodeCoverage]
class C
{
void M() {}
}
class D
{
void M() {}
}
";
var c = CreateCompilationWithMscorlib40(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
c.VerifyDiagnostics();
var verifier = CompileAndVerify(c, emitOptions: EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)));
c.VerifyEmitDiagnostics();
AssertNotInstrumented(verifier, "C.M");
AssertInstrumented(verifier, "D.M");
}
[Fact]
public void ExcludeFromCodeCoverageAttribute_CustomDefinition_Bad()
{
string source = @"
using System.Diagnostics.CodeAnalysis;
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class ExcludeFromCodeCoverageAttribute : Attribute
{
public ExcludeFromCodeCoverageAttribute(int x) {}
}
}
[ExcludeFromCodeCoverage(1)]
class C
{
void M() {}
}
class D
{
void M() {}
}
";
var c = CreateCompilationWithMscorlib40(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll);
c.VerifyDiagnostics();
var verifier = CompileAndVerify(c, emitOptions: EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)));
c.VerifyEmitDiagnostics();
AssertInstrumented(verifier, "C.M");
AssertInstrumented(verifier, "D.M");
}
[Fact]
public void TestPartialMethodsWithImplementation()
{
var source = @"
using System;
public partial class Class1<T>
{
partial void Method1<U>(int x);
public void Method2(int x)
{
Console.WriteLine($""Method2: x = {x}"");
Method1<T>(x);
}
}
public partial class Class1<T>
{
partial void Method1<U>(int x)
{
Console.WriteLine($""Method1: x = {x}"");
if (x > 0)
{
Console.WriteLine(""Method1: x > 0"");
Method1<U>(0);
}
else if (x < 0)
{
Console.WriteLine(""Method1: x < 0"");
}
}
}
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
Console.WriteLine(""Test"");
var c = new Class1<int>();
c.Method2(1);
}
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(3, 1, "partial void Method1<U>(int x)")
.True(@"Console.WriteLine($""Method1: x = {x}"");")
.True(@"Console.WriteLine(""Method1: x > 0"");")
.True("Method1<U>(0);")
.False(@"Console.WriteLine(""Method1: x < 0"");")
.True("x < 0)")
.True("x > 0)");
checker.Method(4, 1, "public void Method2(int x)")
.True(@"Console.WriteLine($""Method2: x = {x}"");")
.True("Method1<T>(x);");
checker.Method(5, 1, ".ctor()", expectBodySpan: false);
checker.Method(6, 1, "public static void Main(string[] args)")
.True("Test();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();");
checker.Method(7, 1, "static void Test()")
.True(@"Console.WriteLine(""Test"");")
.True("var c = new Class1<int>();")
.True("c.Method2(1);");
checker.Method(10, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
var expectedOutput = @"Test
Method2: x = 1
Method1: x = 1
Method1: x > 0
Method1: x = 0
" + checker.ExpectedOutput;
var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyIL("Class1<T>.Method1<U>",
@"
{
// Code size 138 (0x8a)
.maxstack 5
.locals init (bool[] V_0)
IL_0000: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0005: ldtoken ""void Class1<T>.Method1<U>(int)""
IL_000a: ldelem.ref
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: brtrue.s IL_0034
IL_000f: ldsfld ""System.Guid <PrivateImplementationDetails>.MVID""
IL_0014: ldtoken ""void Class1<T>.Method1<U>(int)""
IL_0019: ldtoken Source Document 0
IL_001e: ldsfld ""bool[][] <PrivateImplementationDetails>.PayloadRoot0""
IL_0023: ldtoken ""void Class1<T>.Method1<U>(int)""
IL_0028: ldelema ""bool[]""
IL_002d: ldc.i4.7
IL_002e: call ""bool[] Microsoft.CodeAnalysis.Runtime.Instrumentation.CreatePayload(System.Guid, int, int, ref bool[], int)""
IL_0033: stloc.0
IL_0034: ldloc.0
IL_0035: ldc.i4.0
IL_0036: ldc.i4.1
IL_0037: stelem.i1
IL_0038: ldloc.0
IL_0039: ldc.i4.1
IL_003a: ldc.i4.1
IL_003b: stelem.i1
IL_003c: ldstr ""Method1: x = {0}""
IL_0041: ldarg.1
IL_0042: box ""int""
IL_0047: call ""string string.Format(string, object)""
IL_004c: call ""void System.Console.WriteLine(string)""
IL_0051: ldloc.0
IL_0052: ldc.i4.6
IL_0053: ldc.i4.1
IL_0054: stelem.i1
IL_0055: ldarg.1
IL_0056: ldc.i4.0
IL_0057: ble.s IL_0073
IL_0059: ldloc.0
IL_005a: ldc.i4.2
IL_005b: ldc.i4.1
IL_005c: stelem.i1
IL_005d: ldstr ""Method1: x > 0""
IL_0062: call ""void System.Console.WriteLine(string)""
IL_0067: ldloc.0
IL_0068: ldc.i4.3
IL_0069: ldc.i4.1
IL_006a: stelem.i1
IL_006b: ldarg.0
IL_006c: ldc.i4.0
IL_006d: call ""void Class1<T>.Method1<U>(int)""
IL_0072: ret
IL_0073: ldloc.0
IL_0074: ldc.i4.5
IL_0075: ldc.i4.1
IL_0076: stelem.i1
IL_0077: ldarg.1
IL_0078: ldc.i4.0
IL_0079: bge.s IL_0089
IL_007b: ldloc.0
IL_007c: ldc.i4.4
IL_007d: ldc.i4.1
IL_007e: stelem.i1
IL_007f: ldstr ""Method1: x < 0""
IL_0084: call ""void System.Console.WriteLine(string)""
IL_0089: ret
}
");
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestPartialMethodsWithoutImplementation()
{
var source = @"
using System;
public partial class Class1<T>
{
partial void Method1<U>(int x);
public void Method2(int x)
{
Console.WriteLine($""Method2: x = {x}"");
Method1<T>(x);
}
}
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
Console.WriteLine(""Test"");
var c = new Class1<int>();
c.Method2(1);
}
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(3, 1, "public void Method2(int x)")
.True(@"Console.WriteLine($""Method2: x = {x}"");");
checker.Method(4, 1, ".ctor()", expectBodySpan: false);
checker.Method(5, 1, "public static void Main(string[] args)")
.True("Test();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();");
checker.Method(6, 1, "static void Test()")
.True(@"Console.WriteLine(""Test"");")
.True("var c = new Class1<int>();")
.True("c.Method2(1);");
checker.Method(9, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
var expectedOutput = @"Test
Method2: x = 1
" + checker.ExpectedOutput;
var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestSynthesizedConstructorWithSpansInMultipleFilesCoverage()
{
var source1 = @"
using System;
public partial class Class1<T>
{
private int x = 1;
}
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
Console.WriteLine(""Test"");
var c = new Class1<int>();
c.Method1(1);
}
}
" + InstrumentationHelperSource;
var source2 = @"
public partial class Class1<T>
{
private int y = 2;
}
public partial class Class1<T>
{
private int z = 3;
}";
var source3 = @"
using System;
public partial class Class1<T>
{
private Action<int> a = i =>
{
Console.WriteLine(i);
};
public void Method1(int i)
{
a(i);
Console.WriteLine(x);
Console.WriteLine(y);
Console.WriteLine(z);
}
}";
var sources = new[] {
(Name: "b.cs", Content: source1),
(Name: "c.cs", Content: source2),
(Name: "a.cs", Content: source3)
};
var expectedOutput = @"Test
1
1
2
3
Flushing
Method 3
File 1
True
True
True
True
True
Method 4
File 1
File 2
File 3
True
True
True
True
True
Method 5
File 2
True
True
True
Method 6
File 2
True
True
True
True
Method 9
File 2
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
var verifier = CompileAndVerify(sources, expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(sources, expectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestSynthesizedStaticConstructorWithSpansInMultipleFilesCoverage()
{
var source1 = @"
using System;
public partial class Class1<T>
{
private static int x = 1;
}
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
Console.WriteLine(""Test"");
var c = new Class1<int>();
Class1<int>.Method1(1);
}
}
" + InstrumentationHelperSource;
var source2 = @"
public partial class Class1<T>
{
private static int y = 2;
}
public partial class Class1<T>
{
private static int z = 3;
}";
var source3 = @"
using System;
public partial class Class1<T>
{
private static Action<int> a = i =>
{
Console.WriteLine(i);
};
public static void Method1(int i)
{
a(i);
Console.WriteLine(x);
Console.WriteLine(y);
Console.WriteLine(z);
}
}";
var sources = new[] {
(Name: "b.cs", Content: source1),
(Name: "c.cs", Content: source2),
(Name: "a.cs", Content: source3)
};
var expectedOutput = @"Test
1
1
2
3
Flushing
Method 3
File 1
True
True
True
True
True
Method 4
File 2
Method 5
File 1
File 2
File 3
True
True
True
True
True
Method 6
File 2
True
True
True
Method 7
File 2
True
True
True
True
Method 10
File 2
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
var verifier = CompileAndVerify(sources, expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(sources, expectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
}
[Fact]
public void TestLineDirectiveCoverage()
{
var source = @"
using System;
public class Program
{
public static void Main(string[] args)
{
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
}
static void Test()
{
#line 300 ""File2.cs""
Console.WriteLine(""Start"");
#line hidden
Console.WriteLine(""Hidden"");
#line default
Console.WriteLine(""Visible"");
#line 400 ""File3.cs""
Console.WriteLine(""End"");
}
}
" + InstrumentationHelperSource;
var expectedOutput = @"Start
Hidden
Visible
End
Flushing
Method 3
File 1
True
True
True
Method 4
File 1
File 2
File 3
True
True
True
True
True
Method 7
File 3
True
True
False
True
True
True
True
True
True
True
True
True
True
True
True
True
";
var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe);
verifier.VerifyDiagnostics();
}
[Fact]
[CompilerTrait(CompilerFeature.TopLevelStatements)]
public void TopLevelStatements_01()
{
var source = @"
using System;
Test();
Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();
static void Test()
{
Console.WriteLine(""Test"");
}
" + InstrumentationHelperSource;
var checker = new CSharpInstrumentationChecker();
checker.Method(1, 1, snippet: "", expectBodySpan: false)
.True("Test();")
.True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();")
.True(@"Console.WriteLine(""Test"");");
checker.Method(5, 1)
.True()
.False()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True()
.True();
var expectedOutput = @"Test
" + checker.ExpectedOutput;
var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular9);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9);
checker.CompleteCheck(verifier.Compilation, source);
verifier.VerifyDiagnostics();
}
private static void AssertNotInstrumented(CompilationVerifier verifier, string qualifiedMethodName)
=> AssertInstrumented(verifier, qualifiedMethodName, expected: false);
private static void AssertInstrumented(CompilationVerifier verifier, string qualifiedMethodName, bool expected = true)
{
string il = verifier.VisualizeIL(qualifiedMethodName);
// Tests using this helper are constructed such that instrumented methods contain a call to CreatePayload,
// lambdas a reference to payload bool array.
bool instrumented = il.Contains("CreatePayload") || il.Contains("bool[]");
Assert.True(expected == instrumented, $"Method '{qualifiedMethodName}' should {(expected ? "be" : "not be")} instrumented. Actual IL:{Environment.NewLine}{il}");
}
private CompilationVerifier CompileAndVerify(string source, string expectedOutput = null, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null, Verification verify = default)
{
return base.CompileAndVerify(
source,
expectedOutput: expectedOutput,
options: (options ?? TestOptions.ReleaseExe).WithDeterministic(true),
parseOptions: parseOptions,
emitOptions: EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)),
verify: verify);
}
private CompilationVerifier CompileAndVerify((string Path, string Content)[] sources, string expectedOutput = null, CSharpCompilationOptions options = null)
{
var trees = ArrayBuilder<SyntaxTree>.GetInstance();
foreach (var source in sources)
{
// The trees must be assigned unique file names in order for instrumentation to work correctly.
trees.Add(Parse(source.Content, filename: source.Path));
}
var compilation = CreateCompilation(trees.ToArray(), options: (options ?? TestOptions.ReleaseExe).WithDeterministic(true));
trees.Free();
return base.CompileAndVerify(compilation, expectedOutput: expectedOutput, emitOptions: EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage)));
}
}
}
|