Add telemetry data points for .NET Core 2.0

This commit is contained in:
William Li 2017-06-05 20:51:58 -07:00 committed by William Lee
parent 676fe41aca
commit 081f208942
29 changed files with 1324 additions and 77 deletions

View file

@ -0,0 +1,165 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.Extensions.DependencyModel.Tests;
using Microsoft.Extensions.EnvironmentAbstractions;
using Moq;
using Xunit;
namespace Microsoft.DotNet.Configurer.UnitTests
{
public class GivenAFunctionReturnStringAndFakeFileSystem
{
private const string DOTNET_USER_PROFILE_FOLDER_PATH = "some path";
private FileSystemMockBuilder _fileSystemMockBuilder;
private UserLevelCacheWriter _userLevelCacheWriter;
private IFileSystem _fileSystemMock;
public GivenAFunctionReturnStringAndFakeFileSystem()
{
_fileSystemMockBuilder = FileSystemMockBuilder.Create();
_fileSystemMock = _fileSystemMockBuilder.Build();
_userLevelCacheWriter =
new UserLevelCacheWriter(
DOTNET_USER_PROFILE_FOLDER_PATH,
_fileSystemMock.File,
_fileSystemMock.Directory);
}
[Fact]
public void ItReturnsTheFunctionResult()
{
_userLevelCacheWriter.RunWithCache("fooKey", () => "foo").Should().Be("foo");
}
[Fact]
public void ItRunsTheFunctionOnlyOnceWhenInvokeTwice()
{
var counter = new Counter();
Func<string> func = () =>
{
counter.Increase();
return "foo";
};
_userLevelCacheWriter.RunWithCache("fookey", func).Should().Be("foo");
_userLevelCacheWriter.RunWithCache("fookey", func).Should().Be("foo");
counter.Count.Should().Be(1);
}
[Fact]
public void ItKeepsTheCacheInUserProfileWithCacheKey()
{
_userLevelCacheWriter.RunWithCache("fooKey", () => "foo");
var path = Path.Combine("some path", $"{Product.Version}_fooKey.dotnetUserLevelCache");
_fileSystemMock.File.Exists(path);
_fileSystemMock.File.ReadAllText(path).Should().Be("foo");
}
[Fact]
public void ItRunsAndReturnsTheValueIfCacheCreationFailed()
{
var mockFile = new Mock<IFile>();
var systemUndertest =
new UserLevelCacheWriter(
DOTNET_USER_PROFILE_FOLDER_PATH,
new NoPermissionFileFake(),
new NoPermissionDirectoryFake());
var counter = new Counter();
Func<string> func = () =>
{
counter.Increase();
return "foo";
};
systemUndertest.RunWithCache("fookey", func).Should().Be("foo");
systemUndertest.RunWithCache("fookey", func).Should().Be("foo");
counter.Count.Should().Be(2);
}
private class NoPermissionFileFake : IFile
{
public bool Exists(string path)
{
return false;
}
public string ReadAllText(string path)
{
throw new UnauthorizedAccessException();
}
public Stream OpenRead(string path)
{
throw new UnauthorizedAccessException();
}
public Stream OpenFile(
string path,
FileMode fileMode,
FileAccess fileAccess,
FileShare fileShare,
int bufferSize,
FileOptions fileOptions)
{
throw new NotImplementedException();
}
public void CreateEmptyFile(string path)
{
throw new UnauthorizedAccessException();
}
public void WriteAllText(string path, string content)
{
throw new UnauthorizedAccessException();
}
}
private class NoPermissionDirectoryFake : IDirectory
{
public ITemporaryDirectory CreateTemporaryDirectory()
{
throw new NotImplementedException();
}
public IEnumerable<string> GetFiles(string path, string searchPattern)
{
throw new UnauthorizedAccessException();
}
public string GetDirectoryFullName(string path)
{
throw new NotImplementedException();
}
public bool Exists(string path)
{
return false;
}
public void CreateDirectory(string path)
{
throw new UnauthorizedAccessException();
}
}
private class Counter
{
public int Count { get; private set; }
public void Increase() { Count++; }
}
}
}

View file

@ -18,6 +18,7 @@ using MSBuildCommand = Microsoft.DotNet.Tools.Test.Utilities.MSBuildCommand;
using System.Diagnostics;
using System.Threading;
// There are tests which modify static Telemetry.CurrentSessionId and they cannot run in parallel
[assembly: CollectionBehavior(DisableTestParallelization = true)]
@ -129,7 +130,7 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests
[Fact]
public void WhenTelemetryIsEnabledTheLoggerIsAddedToTheCommandLine()
{
Telemetry telemetry;
Telemetry.Telemetry telemetry;
string[] allArgs = GetArgsForMSBuild(() => true, out telemetry);
// telemetry will still be disabled if environment variable is set
if (telemetry.Enabled)
@ -156,14 +157,15 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests
private string[] GetArgsForMSBuild(Func<bool> sentinelExists)
{
Telemetry telemetry;
Telemetry.Telemetry telemetry;
return GetArgsForMSBuild(sentinelExists, out telemetry);
}
private string[] GetArgsForMSBuild(Func<bool> sentinelExists, out Telemetry telemetry)
private string[] GetArgsForMSBuild(Func<bool> sentinelExists, out Telemetry.Telemetry telemetry)
{
Telemetry.CurrentSessionId = null; // reset static session id modified by telemetry constructor
telemetry = new Telemetry(new MockNuGetCacheSentinel(sentinelExists));
Telemetry.Telemetry.CurrentSessionId = null; // reset static session id modified by telemetry constructor
telemetry = new Telemetry.Telemetry(new MockNuGetCacheSentinel(sentinelExists));
MSBuildForwardingApp msBuildForwardingApp = new MSBuildForwardingApp(Enumerable.Empty<string>());

View file

@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Telemetry;
namespace Microsoft.DotNet.Tests
{
public class FakeRecordEventNameTelemetry : ITelemetry
{
public bool Enabled { get; set; }
public string EventName { get; set; }
public void TrackEvent(string eventName,
IDictionary<string, string> properties,
IDictionary<string, double> measurements)
{
LogEntries.Add(
new LogEntry
{
EventName = eventName,
Measurement = measurements,
Properties = properties
});
}
public ConcurrentBag<LogEntry> LogEntries { get; set; } = new ConcurrentBag<LogEntry>();
public class LogEntry
{
public string EventName { get; set; }
public IDictionary<string, string> Properties { get; set; }
public IDictionary<string, double> Measurement { get; set; }
}
}
}

View file

@ -106,8 +106,9 @@ namespace Microsoft.DotNet.Tests
command.ExecuteWithCapturedOutput("internal-reportinstallsuccess test").Should().Pass();
var emptyHomeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet"));
emptyHomeFolder.Should().NotExist();
var homeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet"));
string[] fileEntries = Directory.GetFiles(homeFolder.ToString());
fileEntries.Should().OnlyContain(x => !x.Contains(".dotnetFirstUseSentinel"));
}
[Fact]

View file

@ -1,52 +1,229 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.Test.Utilities;
using System.Collections.Generic;
using Xunit;
namespace Microsoft.DotNet.Tests
{
public class MockTelemetry : ITelemetry
public class TelemetryCommandTests : TestBase
{
public bool Enabled { get; set; }
private readonly FakeRecordEventNameTelemetry _fakeTelemetry;
public string EventName { get; set; }
public IDictionary<string, string> Properties { get; set; }
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
public TelemetryCommandTests()
{
EventName = eventName;
Properties = properties;
_fakeTelemetry = new FakeRecordEventNameTelemetry();
TelemetryEventEntry.Subscribe(_fakeTelemetry.TrackEvent);
TelemetryEventEntry.TelemetryFilter = new TelemetryFilter();
}
}
public class TelemetryCommandTests : TestBase
{
[Fact]
public void TestProjectDependencyIsNotAvailableThroughDriver()
public void TopLevelCommandNameShouldBeSentToTelemetry()
{
MockTelemetry mockTelemetry = new MockTelemetry();
string[] args = { "help" };
Microsoft.DotNet.Cli.Program.ProcessArgs(args, mockTelemetry);
Assert.Equal(mockTelemetry.EventName, args[0]);
string[] args = {"help"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == args[0]);
}
[Fact]
public void DotnetNewCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "console";
string[] args = {"new", argumentToSend};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-new" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetHelpCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "something";
string[] args = {"help", argumentToSend};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-help" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "package";
string[] args = {"add", argumentToSend, "aPackageName"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-add" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry2()
{
const string argumentToSend = "reference";
string[] args = {"add", argumentToSend, "aPackageName"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-add" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetRemoveCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "package";
string[] args = {"remove", argumentToSend, "aPackageName"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-remove" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetListCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "reference";
string[] args = {"list", argumentToSend, "aPackageName"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-list" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetSlnCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "list";
string[] args = {"sln", "aSolution", argumentToSend};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-sln" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetNugetCommandFirstArgumentShouldBeSentToTelemetry()
{
const string argumentToSend = "push";
string[] args = {"nuget", argumentToSend, "aRoot"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-nuget" && e.Properties.ContainsKey("argument") &&
e.Properties["argument"] == argumentToSend);
}
[Fact]
public void DotnetNewCommandLanguageOpinionShouldBeSentToTelemetry()
{
const string optionKey = "language";
const string optionValueToSend = "c#";
string[] args = {"new", "console", "--" + optionKey, optionValueToSend};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-new" && e.Properties.ContainsKey(optionKey) &&
e.Properties[optionKey] == optionValueToSend);
}
[Fact]
public void AnyDotnetCommandVerbosityOpinionShouldBeSentToTelemetry()
{
const string optionKey = "verbosity";
const string optionValueToSend = "minimal";
string[] args = {"restore", "--" + optionKey, optionValueToSend};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-restore" && e.Properties.ContainsKey(optionKey) &&
e.Properties[optionKey] == optionValueToSend);
}
[Fact]
public void DotnetBuildAndPublishCommandOpinionsShouldBeSentToTelemetry()
{
const string optionKey = "configuration";
const string optionValueToSend = "Debug";
string[] args = {"build", "--" + optionKey, optionValueToSend};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey(optionKey) &&
e.Properties[optionKey] == optionValueToSend);
}
[Fact]
public void DotnetPublishCommandRuntimeOpinionsShouldBeSentToTelemetry()
{
const string optionKey = "runtime";
const string optionValueToSend = "win10-x64";
string[] args = { "publish", "--" + optionKey, optionValueToSend };
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-publish" && e.Properties.ContainsKey(optionKey) &&
e.Properties[optionKey] == optionValueToSend);
}
[Fact]
public void DotnetBuildAndPublishCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption()
{
string[] args = {"build", "--configuration", "Debug", "--runtime", "osx.10.11-x64"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey("configuration") &&
e.Properties["configuration"] == "Debug");
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey("runtime") &&
e.Properties["runtime"] == "osx.10.11-x64");
}
[Fact]
public void DotnetRunCleanTestCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption()
{
string[] args = {"clean", "--configuration", "Debug", "--framework", "netcoreapp1.0"};
Cli.Program.ProcessArgs(args);
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-clean" && e.Properties.ContainsKey("configuration") &&
e.Properties["configuration"] == "Debug");
_fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "dotnet-clean" && e.Properties.ContainsKey("framework") &&
e.Properties["framework"] == "netcoreapp1.0");
}
[WindowsOnlyFact]
public void InternalreportinstallsuccessCommandCollectExeNameWithEventname()
{
MockTelemetry mockTelemetry = new MockTelemetry();
FakeRecordEventNameTelemetry fakeTelemetry = new FakeRecordEventNameTelemetry();
string[] args = { "c:\\mypath\\dotnet-sdk-latest-win-x64.exe" };
InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, mockTelemetry);
InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, fakeTelemetry);
mockTelemetry.EventName.Should().Be("reportinstallsuccess");
mockTelemetry.Properties["exeName"].Should().Be("dotnet-sdk-latest-win-x64.exe");
fakeTelemetry
.LogEntries.Should()
.Contain(e => e.EventName == "reportinstallsuccess" && e.Properties.ContainsKey("exeName") &&
e.Properties["exeName"] == "dotnet-sdk-latest-win-x64.exe");
}
[Fact]

View file

@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using System;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Configurer;
namespace Microsoft.DotNet.Tests
{
public class TelemetryCommonPropertiesTests : TestBase
{
[Fact]
public void TelemetryCommonPropertiesShouldContainIfItIsInDockerOrNot()
{
var unitUnderTest = new TelemetryCommonProperties(userLevelCacheWriter: new NothingCache());
unitUnderTest.GetTelemetryCommonProperties().Should().ContainKey("Docker Container");
}
[Fact]
public void TelemetryCommonPropertiesShouldReturnHashedPath()
{
var unitUnderTest = new TelemetryCommonProperties(() => "ADirectory", userLevelCacheWriter: new NothingCache());
unitUnderTest.GetTelemetryCommonProperties()["Current Path Hash"].Should().NotBe("ADirectory");
}
[Fact]
public void TelemetryCommonPropertiesShouldReturnHashedMachineId()
{
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => "plaintext", userLevelCacheWriter: new NothingCache());
unitUnderTest.GetTelemetryCommonProperties()["Machine ID"].Should().NotBe("plaintext");
}
[Fact]
public void TelemetryCommonPropertiesShouldReturnNewGuidWhenCannotGetMacAddress()
{
var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache());
var assignedMachineId = unitUnderTest.GetTelemetryCommonProperties()["Machine ID"];
Guid.TryParse(assignedMachineId, out var _).Should().BeTrue("it should be a guid");
}
private class NothingCache : IUserLevelCacheWriter
{
public string RunWithCache(string cacheKey, Func<string> getValueToCache)
{
return getValueToCache();
}
}
}
}

View file

@ -0,0 +1,38 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.DotNet.Cli;
namespace Microsoft.DotNet.Cli.MSBuild.IntegrationTests
{
public class FakeRecordEventNameTelemetry
{
public bool Enabled { get; set; }
public string EventName { get; set; }
public void TrackEvent(string eventName,
IDictionary<string, string> properties,
IDictionary<string, double> measurements)
{
LogEntries.Add(
new LogEntry
{
EventName = eventName,
Measurement = measurements,
Properties = properties
});
}
public ConcurrentBag<LogEntry> LogEntries { get; set; } = new ConcurrentBag<LogEntry>();
public class LogEntry
{
public string EventName { get; set; }
public IDictionary<string, string> Properties { get; set; }
public IDictionary<string, double> Measurement { get; set; }
}
}
}