-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
1,257 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/ubuntu/.devcontainer/base.Dockerfile | ||
|
||
# [Choice] Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon): ubuntu-22.04, ubuntu-20.04, ubuntu-18.04 | ||
ARG VARIANT="jammy" | ||
FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} | ||
|
||
# [Optional] Uncomment this section to install additional OS packages. | ||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ | ||
# && apt-get -y install --no-install-recommends <your-package-list-here> | ||
|
||
RUN ln -s /usr/share/dotnet /usr/local/dotnet |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: | ||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/ubuntu | ||
{ | ||
"name": "Ubuntu", | ||
"build": { | ||
"dockerfile": "Dockerfile", | ||
// Update 'VARIANT' to pick an Ubuntu version: jammy / ubuntu-22.04, focal / ubuntu-20.04, bionic /ubuntu-18.04 | ||
// Use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon. | ||
"args": { "VARIANT": "ubuntu-20.04" } | ||
}, | ||
|
||
// Use 'forwardPorts' to make a list of ports inside the container available locally. | ||
// "forwardPorts": [], | ||
|
||
// Use 'postCreateCommand' to run commands after the container is created. | ||
// "postCreateCommand": "uname -a", | ||
|
||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. | ||
"remoteUser": "vscode", | ||
"features": { | ||
"dotnet": "6.0" | ||
}, | ||
"customizations": { | ||
"vscode": { | ||
"extensions": [ | ||
"Ionide.Ionide-fsharp", | ||
"ms-dotnettools.csharp" | ||
] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
bin | ||
obj | ||
|
||
.fake |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"editor.inlayHints.enabled": "offUnlessPressed" | ||
} |
33 changes: 33 additions & 0 deletions
33
BuildkiteTestAnalytics.Tests/BuildkiteTestAnalytics.Tests.fsproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<IsPackable>false</IsPackable> | ||
<GenerateProgramFile>false</GenerateProgramFile> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="PayloadTests.fs" /> | ||
<Compile Include="TracingTests.fs" /> | ||
<Compile Include="TestDataTests.fs" /> | ||
<Compile Include="TimingTests.fs" /> | ||
<Compile Include="Program.fs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> | ||
<PackageReference Include="xunit" Version="2.4.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="3.1.2"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\BuildkiteTestAnalytics\BuildkiteTestAnalytics.fsproj" /> | ||
<ProjectReference Include="..\BuildkiteTestAnalytics.XUnitCollector\BuildkiteTestAnalytics.XUnitCollector.fsproj" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
module PayloadTests | ||
|
||
open System | ||
open System.Collections.Generic | ||
open Xunit | ||
open BuildkiteTestAnalytics | ||
|
||
let getEnvVarFactory (env: Map<string, string>) : (string -> string) = | ||
(fun key -> | ||
let maybe = env |> Map.tryFind key | ||
Option.defaultValue "" maybe) | ||
|
||
let rand = Random() | ||
|
||
[<Fact>] | ||
let ``when it cannot detect the environment it returns none`` () = | ||
let getEnvVar = getEnvVarFactory (Map []) | ||
let payload = Payload.Init(Some getEnvVar) | ||
Assert.Same(payload, None) | ||
|
||
[<Fact>] | ||
let ``when it detects a Buildkite CI environment it returns an empty payload`` () = | ||
let buildId = Guid.NewGuid.ToString() | ||
|
||
let env = | ||
Map [ ("BUILDKITE_BUILD_ID", buildId) | ||
("BUILDKITE_BUILD_URL", sprintf "https://example.test/buildkite/%s" buildId) | ||
("BUILDKITE_BRANCH", "feat/add-mr-fusion-to-delorean") | ||
("BUILDKITE_COMMIT", buildId) | ||
("BUILDKITE_BUILD_NUMBER", rand.Next(999).ToString()) | ||
("BUILDKITE_JOB_ID", rand.Next(999).ToString()) | ||
("BUILDKITE_MESSAGE", | ||
"Silence, Earthling! My Name Is Darth Vader. I Am An Extraterrestrial From The Planet Vulcan!") ] | ||
|
||
let payload = Payload.Init(Some(getEnvVarFactory env)) | ||
Assert.True(Option.isSome payload) | ||
|
||
let payload = payload.Value | ||
let now = Timing.now () | ||
Assert.InRange(payload.StartedAt, now - 20, now + 20) | ||
Assert.Empty(payload.Data) | ||
|
||
let runEnv = payload.RuntimeEnvironment | ||
Assert.Equal(runEnv.Ci, "buildkite") | ||
Assert.Equal(runEnv.Key, env.["BUILDKITE_BUILD_ID"]) | ||
Assert.Equal(runEnv.Number, Some(env.["BUILDKITE_BUILD_NUMBER"])) | ||
Assert.Equal(runEnv.JobId, Some(env.["BUILDKITE_JOB_ID"])) | ||
Assert.Equal(runEnv.Branch, Some(env.["BUILDKITE_BRANCH"])) | ||
Assert.Equal(runEnv.CommitSha, Some(env.["BUILDKITE_COMMIT"])) | ||
Assert.Equal(runEnv.Message, Some(env.["BUILDKITE_MESSAGE"])) | ||
Assert.Equal(runEnv.Url, Some(env.["BUILDKITE_BUILD_URL"])) | ||
|
||
|
||
[<Fact>] | ||
let ``when it detects a Github Actions CI environment it returns an empty payload`` () = | ||
let env = | ||
Map [ ("GITHUB_ACTION", "__doc-brown_grandfather-paradox_flux-capacitor") | ||
("GITHUB_RUN_NUMBER", rand.Next(999).ToString()) | ||
("GITHUB_RUN_ATTEMPT", rand.Next(999).ToString()) | ||
("GITHUB_REPOSITORY", "doc-brown/flux-capacitor") | ||
("GITHUB_REF", "feat/add-time-circuits") | ||
("GITHUB_RUN_ID", Guid.NewGuid.ToString()) | ||
("GITHUB_SHA", Guid.NewGuid.ToString()) ] | ||
|
||
let payload = Payload.Init(Some(getEnvVarFactory env)) | ||
Assert.True(Option.isSome payload) | ||
|
||
let payload = payload.Value | ||
let now = Timing.now () | ||
Assert.InRange(payload.StartedAt, now - 20, now + 20) | ||
Assert.Empty(payload.Data) | ||
|
||
let runEnv = payload.RuntimeEnvironment | ||
Assert.Equal(runEnv.Ci, "github_actions") | ||
|
||
Assert.Equal( | ||
runEnv.Key, | ||
sprintf "%s-%s-%s" env.["GITHUB_ACTION"] env.["GITHUB_RUN_NUMBER"] env.["GITHUB_RUN_ATTEMPT"] | ||
) | ||
|
||
Assert.Equal(runEnv.Number, Some(env.["GITHUB_RUN_NUMBER"])) | ||
Assert.Same(runEnv.JobId, None) | ||
Assert.Equal(runEnv.Branch, Some(env.["GITHUB_REF"])) | ||
Assert.Equal(runEnv.CommitSha, Some(env.["GITHUB_SHA"])) | ||
Assert.Equal(runEnv.Message, None) | ||
|
||
Assert.Equal( | ||
runEnv.Url, | ||
Some(sprintf "https://github.com/doc-brown/flux-capacitor/actions/run/%s" env.["GITHUB_RUN_ID"]) | ||
) | ||
|
||
[<Fact>] | ||
let ``when it detects a Circle CI environment it returns an empty payload`` () = | ||
let env = | ||
Map [ ("CIRCLE_BUILD_NUM", rand.Next(999).ToString()) | ||
("CIRCLE_WORKFLOW_ID", Guid.NewGuid.ToString()) | ||
("CIRCLE_BUILD_URL", "https://example.test/circle") | ||
("CIRCLE_BRANCH", "rufus") | ||
("CIRCLE_SHA1", Guid.NewGuid.ToString()) ] | ||
|
||
let payload = Payload.Init(Some(getEnvVarFactory env)) | ||
Assert.True(Option.isSome payload) | ||
|
||
let payload = payload.Value | ||
let now = Timing.now () | ||
Assert.InRange(payload.StartedAt, now - 20, now + 20) | ||
Assert.Empty(payload.Data) | ||
|
||
let runEnv = payload.RuntimeEnvironment | ||
Assert.Equal(runEnv.Ci, "circleci") | ||
Assert.Equal(runEnv.Key, sprintf "%s-%s" env.["CIRCLE_WORKFLOW_ID"] env.["CIRCLE_BUILD_NUM"]) | ||
Assert.Equal(runEnv.Number, Some(env.["CIRCLE_BUILD_NUM"])) | ||
Assert.Same(runEnv.JobId, None) | ||
Assert.Equal(runEnv.Branch, Some(env.["CIRCLE_BRANCH"])) | ||
Assert.Equal(runEnv.CommitSha, Some(env.["CIRCLE_SHA1"])) | ||
Assert.Equal(runEnv.Message, None) | ||
Assert.Equal(runEnv.Url, Some "https://example.test/circle") | ||
|
||
[<Fact>] | ||
let ``when it detects a generic CI environment it returns an empty payload`` () = | ||
let env = Map [ ("CI", "true") ] | ||
|
||
let payload = Payload.Init(Some(getEnvVarFactory env)) | ||
Assert.True(Option.isSome payload) | ||
|
||
let payload = payload.Value | ||
let now = Timing.now () | ||
Assert.InRange(payload.StartedAt, now - 20, now + 20) | ||
Assert.Empty(payload.Data) | ||
|
||
let runEnv = payload.RuntimeEnvironment | ||
Assert.Equal(runEnv.Ci, "generic") | ||
Guid.Parse(runEnv.Key) |> ignore | ||
Assert.Same(runEnv.Number, None) | ||
Assert.Same(runEnv.JobId, None) | ||
Assert.Same(runEnv.Branch, None) | ||
Assert.Same(runEnv.CommitSha, None) | ||
Assert.Same(runEnv.Message, None) | ||
Assert.Same(runEnv.Url, None) | ||
|
||
let genericEmptyPayload () = | ||
let env = Map [ ("CI", "true") ] | ||
Payload.Init(Some(getEnvVarFactory env)).Value | ||
|
||
let fakeTest () = | ||
TestData.Init(Guid.NewGuid.ToString(), Some("scope"), Some("name"), "identifier", Some "location", Some "fileName") | ||
|
||
[<Fact>] | ||
let ``AddTestResult adds a TestResult.Test to the Collection`` () = | ||
let payload = genericEmptyPayload () | ||
|
||
Assert.Empty(payload.Data) | ||
|
||
let test = fakeTest() | ||
|
||
let payload = Payload.AddTestResult(payload, test) | ||
|
||
Assert.NotEmpty(payload.Data) | ||
|
||
Assert.Equal(test, payload.Data.Head) | ||
|
||
[<Fact>] | ||
let ``when the payload has fewer tests than the batch size IntoBatches returns the original payload`` () = | ||
let payload = Payload.AddTestResult(genericEmptyPayload(), fakeTest()) | ||
let payloads = Payload.IntoBatches(payload, 10) | ||
Assert.Equal(payloads.Length, 1) | ||
Assert.Equal(payload, payloads.Head) | ||
|
||
let rec payloadStuffer(payload: Payload.Payload, remaining: int) : Payload.Payload = | ||
if remaining = 0 then | ||
payload | ||
else | ||
let payload = Payload.AddTestResult(payload, fakeTest()) | ||
payloadStuffer(payload, remaining - 1) | ||
|
||
|
||
[<Fact>] | ||
let ``when the payload has more tests than the batch size IntoBatches returns multiple payloads`` () = | ||
let payload = genericEmptyPayload() | ||
let payload = payloadStuffer(payload, 95) | ||
let payloads = Payload.IntoBatches(payload, 10) | ||
|
||
Assert.Equal(payloads.Length, 10) | ||
|
||
let first_payload = payloads.Head | ||
let last_payload = List.last payloads | ||
|
||
Assert.Equal(first_payload.Data.Length, 10) | ||
Assert.Equal(last_payload.Data.Length, 5) | ||
|
||
[<Fact>] | ||
let ``AsJson converts the payload into a map`` () = | ||
let payload = genericEmptyPayload() | ||
let payload = payloadStuffer(payload, 3) | ||
let json = Payload.AsJson(payload) | ||
|
||
Assert.Equal(json["format"], "json") | ||
Assert.Contains("run_env", json.Keys) | ||
Assert.Contains("data", json.Keys) | ||
|
||
let env = json["run_env"] :?> Map<string, obj> | ||
Assert.Equal(env["CI"], "generic") | ||
|
||
let data = json["data"] :?> Map<string, obj> list | ||
Assert.Equal(data.Length, 3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module Program = let [<EntryPoint>] main _ = 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
module TestDataTests | ||
|
||
open System | ||
open Xunit | ||
open BuildkiteTestAnalytics | ||
|
||
let rand = Random() | ||
|
||
let fakeTest () = | ||
TestData.Init(Guid.NewGuid.ToString(), Some("scope"), Some("name"), "identifier", Some "location", Some "fileName") | ||
|
||
let fakeSpan () = | ||
Tracing.Init(Tracing.Section.Sql, rand.Next(1000), None, None, None) | ||
|
||
[<Fact>] | ||
let ``Init creates a new test`` () = | ||
let id = Guid.NewGuid().ToString() | ||
let scope = "BuildkiteTestAnalytics.Tests" | ||
let name = "Init creates a new test" | ||
let identifier = "BuildkiteTestAnalytics.TestDataTests.Init creates a new test" | ||
let location = "BuildkiteTestAnalytics.TestDataTests/TestDataTests.fs: line 8" | ||
let fileName = "TestDataTests.fs" | ||
|
||
let testData = | ||
TestData.Init(id, Some(scope), Some(name), identifier, Some(location), Some(fileName)) | ||
|
||
let now = Timing.now () | ||
|
||
Assert.Equal(testData.Id, id) | ||
Assert.Equal(testData.Scope, Some(scope)) | ||
Assert.Equal(testData.Name, Some(name)) | ||
Assert.Equal(testData.Identifier, identifier) | ||
Assert.Equal(testData.Location, Some(location)) | ||
Assert.Equal(testData.FileName, Some(fileName)) | ||
Assert.InRange(testData.StartAt, now - 5, now + 5) | ||
Assert.Equal(testData.EndAt, None) | ||
Assert.Equal(testData.Duration, None) | ||
Assert.Empty(testData.Children) | ||
Assert.Same(testData.Result, TestData.TestResult.Unknown) | ||
|
||
|
||
[<Fact>] | ||
let ``AddSpan adds a span to a test`` () = | ||
let test = fakeTest () | ||
let span = fakeSpan () | ||
let test = TestData.AddSpan(test, span) | ||
Assert.NotEmpty(test.Children) | ||
|
||
[<Fact>] | ||
let ``Passed marks the test as passed`` () = | ||
let test = fakeTest () | ||
let test = TestData.Passed(test) | ||
|
||
Assert.Same(test.Result, TestData.TestResult.Passed) | ||
|
||
[<Fact>] | ||
let ``Failed marks the test as failed`` () = | ||
let test = fakeTest () | ||
let test = TestData.Failed(test, None) | ||
|
||
Assert.Equal(test.Result, TestData.TestResult.Failed None) | ||
|
||
[<Fact>] | ||
let ``AsJson converts the test into a dict`` () = | ||
let now = Timing.now () | ||
let test = fakeTest () | ||
let test = TestData.AddSpan(test, fakeSpan ()) | ||
let json = TestData.AsJson(test, now) | ||
|
||
Assert.Equal(json["id"], test.Id) | ||
Assert.Equal(json["scope"], test.Scope.Value) | ||
Assert.Equal(json["name"], test.Name.Value) | ||
Assert.Equal(json["identifier"], test.Identifier) | ||
Assert.Equal(json["result"], "unknown") | ||
|
||
let test = TestData.Passed(test) | ||
let json = TestData.AsJson(test, now) | ||
Assert.Equal(json["result"], "passed") | ||
|
||
let test = TestData.Failed(test, None) | ||
let json = TestData.AsJson(test, now) | ||
Assert.Equal(json["result"], "failed") | ||
Assert.DoesNotContain(json.Keys, (fun key -> key = "failure_reason")) | ||
|
||
let test = TestData.Failed(test, Some("a perfectly reasonable reason to fail")) | ||
let json = TestData.AsJson(test, now) | ||
Assert.Equal(json["result"], "failed") | ||
Assert.Equal(json["failure_reason"], "a perfectly reasonable reason to fail") | ||
|
||
let history = json["history"] :?> Map<string, obj> | ||
Assert.Equal(history["section"], "top") | ||
Assert.Equal(history["start_at"], Timing.elapsedSeconds (test.StartAt, now)) | ||
Assert.Equal(history["end_at"], Timing.elapsedSeconds (test.EndAt.Value, now)) | ||
Assert.Equal(history["duration"], Timing.elapsedSeconds (test.EndAt.Value, test.StartAt)) | ||
|
||
let test = TestData.WithDuration(test, 32000) | ||
let json = TestData.AsJson(test, now) | ||
let history = json["history"] :?> Map<string, obj> | ||
Assert.Equal(history["duration"], 32.0) |
Oops, something went wrong.