Skip to content

Commit

Permalink
housekeeping: .NET Core App 2.0 tests & Tests Cleanups (#1926)
Browse files Browse the repository at this point in the history
* housekeeping: .NET Core App 2.0 tests & Tests Cleanups

* Now compiling for .net core app 2.0, .net 4.6.1, uwp (not yet being run --next pass)
* Turned on most of the analyzers for the test project. Cleaned up approriately
* Updated the cake build script to do coverage tests on platforms where they are supported
* Updated open cover version.
* Approval tests now include the framework version.
  • Loading branch information
glennawatson committed Jan 24, 2019
1 parent 17d5bfa commit 5989379
Show file tree
Hide file tree
Showing 174 changed files with 7,195 additions and 5,224 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Expand Up @@ -246,7 +246,7 @@ src/ReactiveUI.Events*/Events_*.cs
# macOS
.DS_Store

src/*.Tests/API/*.received.txt
src/*.Tests/**/ApiApprovalTests*.received.txt
.idea/

# Fody Weavers (for tests)
Expand Down
5 changes: 5 additions & 0 deletions azure-pipelines-tests.yml
Expand Up @@ -12,6 +12,11 @@ pool:
vmImage: vs2017-win2016

steps:
- task: DotNetCoreInstaller@0
displayName: Install Dot Net Core v2.2.1
inputs:
version: '2.2.103'

- task: BatchScript@1
inputs:
filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\VsDevCmd.bat"
Expand Down
5 changes: 5 additions & 0 deletions azure-pipelines.yml
Expand Up @@ -12,6 +12,11 @@ pool:
vmImage: vs2017-win2016

steps:
- task: DotNetCoreInstaller@0
displayName: Install Dot Net Core v2.2.1
inputs:
version: '2.2.103'

- task: BatchScript@1
inputs:
filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\VsDevCmd.bat"
Expand Down
101 changes: 61 additions & 40 deletions build.cake
Expand Up @@ -7,7 +7,7 @@
//////////////////////////////////////////////////////////////////////

#addin "nuget:?package=Cake.FileHelpers&version=3.1.0"
#addin "nuget:?package=Cake.Coveralls&version=0.9.0"
#addin "nuget:?package=Cake.Coverlet&version=2.2.1"
#addin "nuget:?package=Cake.PinNuGetDependency&loaddependencies=true&version=3.2.3"
#addin "nuget:?package=Cake.Powershell&version=0.4.7"
#addin "nuget:?package=Cake.Codecov&version=0.5.0"
Expand All @@ -22,11 +22,11 @@
// TOOLS
//////////////////////////////////////////////////////////////////////

#tool "nuget:?package=OpenCover&version=4.6.519"
#tool "nuget:?package=ReportGenerator&version=4.0.4"
#tool "nuget:?package=vswhere&version=2.5.2"
#tool "nuget:?package=xunit.runner.console&version=2.4.1"
#tool "nuget:?package=Codecov&version=1.1.0"
#tool "nuget:?package=OpenCover&version=4.7.906-rc"

//////////////////////////////////////////////////////////////////////
// DOTNET TOOLS
Expand All @@ -45,6 +45,12 @@ if (string.IsNullOrWhiteSpace(target))
target = "Default";
}

var configuration = Argument("configuration", "Release");
if (string.IsNullOrWhiteSpace(configuration))
{
configuration = "Release";
}

var includePrerelease = Argument("includePrerelease", false);
var vsLocationString = Argument("vsLocation", string.Empty);
var msBuildPathString = Argument("msBuildPath", string.Empty);
Expand Down Expand Up @@ -98,10 +104,16 @@ var packageWhitelist = new[]
"ReactiveUI.LeakTests"
};

var packageTestWhitelist = new[]
(string projectName, bool performCoverageTesting)[] packageTestWhitelist = new[]
{
"ReactiveUI.Tests",
"ReactiveUI.Fody.Tests"
("ReactiveUI.Tests", true),
("ReactiveUI.Fody.Tests", true),
};

(string name, bool performCoverageTesting)[] testFrameworks = new[]
{
("net461", true),
("netcoreapp2.0", false),
};

(string targetName, string destination)[] eventGenerators = new[]
Expand Down Expand Up @@ -152,9 +164,9 @@ Teardown(context =>
//////////////////////////////////////////////////////////////////////
// HELPER METHODS
//////////////////////////////////////////////////////////////////////
Action<string, string, bool, bool> Build = (solution, packageOutputPath, createPackage, forceUseFullDebugType) =>
Action<string, string> Build = (solution, packageOutputPath) =>
{
Information("Building {0} using {1}, createPackage = {2}, forceUseFullDebugType = {3}", solution, msBuildPath, createPackage, forceUseFullDebugType);
Information("Building {0} using {1}", solution, msBuildPath);
var msBuildSettings = new MSBuildSettings() {
ToolPath = msBuildPath,
Expand All @@ -163,26 +175,13 @@ Action<string, string, bool, bool> Build = (solution, packageOutputPath, createP
Restore = true
}
.WithProperty("TreatWarningsAsErrors", treatWarningsAsErrors.ToString())
.SetConfiguration("Release")
.SetConfiguration(configuration)
.WithTarget("build;pack")
.SetVerbosity(Verbosity.Minimal);
if (forceUseFullDebugType)
{
msBuildSettings = msBuildSettings.WithProperty("DebugType", "full");
}
if (createPackage)
{
if (!string.IsNullOrWhiteSpace(packageOutputPath))
{
msBuildSettings = msBuildSettings.WithProperty("PackageOutputPath", MakeAbsolute(Directory(packageOutputPath)).ToString().Quote());
}
msBuildSettings = msBuildSettings.WithTarget("build;pack");
}
else
if (!string.IsNullOrWhiteSpace(packageOutputPath))
{
msBuildSettings = msBuildSettings.WithTarget("build");
msBuildSettings = msBuildSettings.WithProperty("PackageOutputPath", MakeAbsolute(Directory(packageOutputPath)).ToString().Quote());
}
MSBuild(solution, msBuildSettings);
Expand All @@ -195,7 +194,7 @@ Action<string, string, bool, bool> Build = (solution, packageOutputPath, createP
Task("BuildEventBuilder")
.Does(() =>
{
Build("./src/EventBuilder.sln", artifactDirectory + "eventbuilder", false, false);
Build("./src/EventBuilder.sln", artifactDirectory + "eventbuilder");
});

Task("GenerateEvents")
Expand Down Expand Up @@ -243,9 +242,9 @@ Task("BuildReactiveUIPackages")
CleanDirectories("./src/**/obj/Release");
CleanDirectories("./src/**/bin/Release");
foreach(var package in packageWhitelist)
foreach(var packageName in packageWhitelist)
{
Build("./src/" + package + "/" + package + ".csproj", packagesArtifactDirectory, true, false);
Build($"./src/{packageName}/{packageName}.csproj", packagesArtifactDirectory);
}
CopyFiles(GetFiles("./src/**/bin/Release/**/*"), Directory(binariesArtifactDirectory), true);
Expand All @@ -265,20 +264,21 @@ Task("RunUnitTests")
CleanDirectories("./src/**/obj/Release");
CleanDirectories("./src/**/bin/Release");
foreach (var package in fodyPackages)
foreach (var packageName in fodyPackages)
{
Build("./src/" + package + "/" + package + ".csproj", null, true, true);
Build($"./src/{packageName}/{packageName}.csproj", null);
}
var openCoverSettings = new OpenCoverSettings {
ReturnTargetCodeOffset = 0,
MergeOutput = true,
}
.WithFilter("+[*]*")
.WithFilter("+[ReactiveUI*]*")
.WithFilter("-[*.Testing]*")
.WithFilter("-[*.Tests*]*")
.WithFilter("-[ReactiveUI.Events]*")
.WithFilter("-[Splat*]*")
.WithFilter("-[ReactiveUI.Events*]*")
.WithFilter("-[ReactiveUI*]ReactiveUI.*Legacy*")
.WithFilter("-[ReactiveUI*]ThisAssembly*")
.ExcludeByAttribute("*.ExcludeFromCodeCoverage*")
.ExcludeByFile("*/*Designer.cs")
.ExcludeByFile("*/*.g.cs")
Expand All @@ -292,16 +292,37 @@ Task("RunUnitTests")
NoAppDomain = true
};
foreach (var projectName in packageTestWhitelist)
foreach (var packageDetails in packageTestWhitelist)
{
OpenCover(tool =>
{
Build("./src/" + projectName + "/" + projectName + ".csproj", null, true, true);
var packageName = packageDetails.projectName;
var projectName = $"./src/{packageName}/{packageName}.csproj";
Build(projectName, null);
tool.XUnit2("./src/" + projectName + "/bin/" + "**/*.Tests.dll", xunitSettings);
},
testCoverageOutputFile,
openCoverSettings);
foreach (var testFramework in testFrameworks)
{
if (testFramework.performCoverageTesting && packageDetails.performCoverageTesting)
{
Information($"Generate OpenCover information for {packageName} {testFramework.name}");
OpenCover(
tool => tool.XUnit2($"./src/{packageName}/bin/{configuration}/{testFramework.name}/**/*.Tests.dll", xunitSettings),
testCoverageOutputFile,
openCoverSettings);
}
else
{
Information($"Running unit tests only for {packageName} {testFramework.name}");
var testSettings = new DotNetCoreTestSettings {
NoBuild = true,
Framework = testFramework.name,
Configuration = configuration,
ResultsDirectory = testsArtifactDirectory,
Logger = $"trx;LogFileName=testresults-{packageName}-{testFramework.name}.trx",
TestAdapterPath = GetDirectories("./tools/xunit.runner.console*/**/netcoreapp2.0").FirstOrDefault(),
};
DotNetCoreTest(projectName, testSettings);
}
}
}
ReportGenerator(testCoverageOutputFile, testsArtifactDirectory + "Report/");
Expand Down
8 changes: 4 additions & 4 deletions src/ApiGeneratorGlobalSuppressions.cs
@@ -1,7 +1,7 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// 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 full license information.

[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "NuGet Inclusion", Scope = "member", Target = "~M:PublicApiGenerator.ApiGenerator.AddCtorToTypeDeclaration(System.CodeDom.CodeTypeDeclaration,Mono.Cecil.MethodDefinition,System.Collections.Generic.HashSet{System.String})")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "NuGet Inclusion", Scope = "member", Target = "~M:PublicApiGenerator.ApiGenerator.AddFieldToTypeDeclaration(System.CodeDom.CodeTypeDeclaration,Mono.Cecil.FieldDefinition,System.Collections.Generic.HashSet{System.String})")]
Expand Down
5 changes: 1 addition & 4 deletions src/Directory.build.props
Expand Up @@ -14,10 +14,7 @@
<!-- Embed source files that are not tracked by the source control manager in the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Include PDB in the built .nupkg -->
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>

<PropertyGroup Condition="!$(IsTestProject)">
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)analyzers.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

Expand Down
@@ -0,0 +1,31 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")]
[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v2.0", FrameworkDisplayName="")]
namespace ReactiveUI.Fody.Helpers
{
[System.AttributeUsageAttribute(System.AttributeTargets.Method | System.AttributeTargets.Property | System.AttributeTargets.All)]
public class ObservableAsPropertyAttribute : System.Attribute
{
public ObservableAsPropertyAttribute() { }
}
public class static ObservableAsPropertyExtensions
{
public static ReactiveUI.ObservableAsPropertyHelper<TRet> ToPropertyEx<TObj, TRet>(this System.IObservable<TRet> @this, TObj source, System.Linq.Expressions.Expression<System.Func<TObj, TRet>> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null)
where TObj : ReactiveUI.ReactiveObject { }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.All)]
public class ReactiveAttribute : System.Attribute
{
public ReactiveAttribute() { }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.All)]
public class ReactiveDependencyAttribute : System.Attribute
{
public ReactiveDependencyAttribute(string targetName) { }
public string Target { get; }
public string TargetProperty { get; set; }
}
}
18 changes: 2 additions & 16 deletions src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs
Expand Up @@ -14,26 +14,12 @@
namespace ReactiveUI.Fody.Tests.API
{
[ExcludeFromCodeCoverage]
public class ApiApprovalTests
public class ApiApprovalTests : ApiApprovalBase
{
[Fact]
public void ReactiveUIFody()
{
var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(ReactiveAttribute).Assembly));
publicApi.ShouldMatchApproved();
}

private static string Filter(string text)
{
return string.Join(Environment.NewLine, text.Split(
new[]
{
Environment.NewLine
}, StringSplitOptions.RemoveEmptyEntries)
.Where(l => !l.StartsWith("[assembly: AssemblyVersion("))
.Where(l => !l.StartsWith("[assembly: AssemblyFileVersion("))
.Where(l => !l.StartsWith("[assembly: AssemblyInformationalVersion("))
.Where(l => !string.IsNullOrWhiteSpace(l)));
CheckApproval(typeof(ReactiveAttribute).Assembly);
}
}
}
70 changes: 70 additions & 0 deletions src/ReactiveUI.Fody.Tests/ApiApprovalBase.cs
@@ -0,0 +1,70 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// 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 full license information.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using PublicApiGenerator;
using Shouldly;
using Splat;
using Xunit;

namespace ReactiveUI.Fody.Tests
{
[ExcludeFromCodeCoverage]
public abstract class ApiApprovalBase
{
private static readonly Regex _removeCoverletSectionRegex = new Regex(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled);

protected static void CheckApproval(Assembly assembly, [CallerMemberName]string memberName = null, [CallerFilePath]string filePath = null)
{
var targetFrameworkName = Assembly.GetExecutingAssembly().GetTargetFrameworkName();

var sourceDirectory = Path.GetDirectoryName(filePath);

var approvedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.approved.txt");
var receivedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.received.txt");

string approvedPublicApi = string.Empty;

if (File.Exists(approvedFileName))
{
approvedPublicApi = File.ReadAllText(approvedFileName);
}

var receivedPublicApi = Filter(ApiGenerator.GeneratePublicApi(assembly));

if (!string.Equals(receivedPublicApi, approvedPublicApi, StringComparison.InvariantCulture))
{
File.WriteAllText(receivedFileName, receivedPublicApi);
ShouldlyConfiguration.DiffTools.GetDiffTool().Open(receivedFileName, approvedFileName, true);
}

Assert.Equal(approvedPublicApi, receivedPublicApi);
}

private static string Filter(string text)
{
text = _removeCoverletSectionRegex.Replace(text, string.Empty);
return string.Join(Environment.NewLine, text.Split(
new[]
{
Environment.NewLine
}, StringSplitOptions.RemoveEmptyEntries)
.Where(l =>
!l.StartsWith("[assembly: AssemblyVersion(", StringComparison.InvariantCulture) &&
!l.StartsWith("[assembly: AssemblyFileVersion(", StringComparison.InvariantCulture) &&
!l.StartsWith("[assembly: AssemblyInformationalVersion(", StringComparison.InvariantCulture) &&
!string.IsNullOrWhiteSpace(l)));
}
}
}

0 comments on commit 5989379

Please sign in to comment.