Skip to content

Commit b6e77d4

Browse files
authored
Fixed test result duration (#15)
1 parent 437ab26 commit b6e77d4

14 files changed

Lines changed: 192 additions & 215 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
runs-on: windows-latest
2020

2121
env:
22-
VERSION: 1.2.1
22+
VERSION: 1.2.2
2323

2424
strategy:
2525
matrix:

AzurePipelines.TestLogger.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{B8315F74-E
1818
build.cake = build.cake
1919
build.ps1 = build.ps1
2020
build.ruleset = build.ruleset
21+
.github\workflows\build.yml = .github\workflows\build.yml
2122
Directory.Build.props = Directory.Build.props
2223
LICENSE = LICENSE
2324
README.md = README.md

ReleaseNotes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.2.2
2+
3+
- [Fix] Fixed test result duration. (#15, @icnocop).
4+
15
# 1.2.1
26

37
- [Fix] Added Semver dependency to NuGet package. (#12, @icnocop).

src/AzurePipelines.TestLogger/ApiClient.cs

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,20 @@ public IApiClient WithDefaultCredentials()
5959
return this;
6060
}
6161

62-
public async Task<string> MarkTestCasesCompleted(int testRunId, IEnumerable<TestResultParent> testCases, DateTime completedDate, CancellationToken cancellationToken)
63-
{
64-
string requestBody = GetTestCasesAsCompleted(testCases, completedDate);
65-
66-
return await SendAsync(new HttpMethod("PATCH"), $"/{testRunId}/results", requestBody, cancellationToken).ConfigureAwait(false);
67-
}
68-
6962
public async Task<int> AddTestRun(TestRun testRun, CancellationToken cancellationToken)
7063
{
71-
string requestBody = new Dictionary<string, object>
72-
{
73-
{ "name", testRun.Name },
74-
{ "build", new Dictionary<string, object> { { "id", testRun.BuildId } } },
75-
{ "startedDate", testRun.StartedDate.ToString(_dateFormatString) },
76-
{ "isAutomated", true }
64+
string requestBody = new Dictionary<string, object>
65+
{
66+
{ "name", testRun.Name },
67+
{ "build", new Dictionary<string, object> { { "id", testRun.BuildId } } },
68+
{ "startedDate", testRun.StartedDate.ToString(_dateFormatString) },
69+
{ "isAutomated", true }
7770
}.ToJson();
7871

7972
string responseString = await SendAsync(HttpMethod.Post, null, requestBody, cancellationToken).ConfigureAwait(false);
8073
using (StringReader reader = new StringReader(responseString))
8174
{
82-
JsonObject response = JsonDeserializer.Deserialize(reader) as JsonObject;
75+
JsonObject response = JsonDeserializer.Deserialize(reader) as JsonObject;
8376
return response.ValueAsInt("id");
8477
}
8578
}
@@ -108,13 +101,13 @@ public async Task<int[]> AddTestCases(int testRunId, string[] testCaseNames, Dat
108101
{
109102
Dictionary<string, object> properties = new Dictionary<string, object>
110103
{
111-
{ "testCaseTitle", x },
112-
{ "automatedTestName", x },
113-
{ "resultGroupType", "generic" },
114-
{ "outcome", "Passed" }, // Start with a passed outcome initially
115-
{ "state", "InProgress" },
116-
{ "startedDate", startedDate.ToString(_dateFormatString) },
117-
{ "automatedTestType", "UnitTest" },
104+
{ "testCaseTitle", x },
105+
{ "automatedTestName", x },
106+
{ "resultGroupType", "generic" },
107+
{ "outcome", "Passed" }, // Start with a passed outcome initially
108+
{ "state", "InProgress" },
109+
{ "startedDate", startedDate.ToString(_dateFormatString) },
110+
{ "automatedTestType", "UnitTest" },
118111
{ "automatedTestTypeId", "13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" } // This is used in the sample response and also appears in web searches
119112
};
120113
if (!string.IsNullOrEmpty(source))
@@ -145,18 +138,17 @@ public async Task<int[]> AddTestCases(int testRunId, string[] testCaseNames, Dat
145138
}
146139
}
147140

148-
public async Task MarkTestRunCompleted(int testRunId, DateTime startedDate, DateTime completedDate, CancellationToken cancellationToken)
141+
public async Task MarkTestRunCompleted(int testRunId, bool aborted, DateTime completedDate, CancellationToken cancellationToken)
149142
{
150143
// Mark the overall test run as completed
151144
string requestBody = $@"{{
152-
""state"": ""Completed"",
153-
""startedDate"": ""{startedDate.ToString(_dateFormatString)}"",
145+
""state"": ""{(aborted ? "Aborted" : "Completed")}"",
154146
""completedDate"": ""{completedDate.ToString(_dateFormatString)}""
155147
}}";
156148

157149
await SendAsync(new HttpMethod("PATCH"), $"/{testRunId}", requestBody, cancellationToken).ConfigureAwait(false);
158-
}
159-
150+
}
151+
160152
protected Dictionary<string, object> GetTestResultProperties(ITestResult testResult)
161153
{
162154
// https://docs.microsoft.com/en-us/rest/api/azure/devops/test/results/list?view=azure-devops-rest-6.0#testcaseresult
@@ -184,47 +176,48 @@ protected Dictionary<string, object> GetTestResultProperties(ITestResult testRes
184176
}
185177

186178
Dictionary<string, object> properties = new Dictionary<string, object>
187-
{
179+
{
188180
{ "outcome", testOutcome },
189181
{ "computerName", testResult.ComputerName },
190182
{ "runBy", new Dictionary<string, object> { { "displayName", BuildRequestedFor } } }
191183
};
192184

193185
AddAdditionalTestResultProperties(testResult, properties);
194186

195-
if (testResult.Outcome == TestOutcome.Passed || testResult.Outcome == TestOutcome.Failed)
196-
{
197-
long duration = Convert.ToInt64(testResult.Duration.TotalMilliseconds);
198-
properties.Add("durationInMs", duration.ToString(CultureInfo.InvariantCulture));
199-
200-
string errorStackTrace = testResult.ErrorStackTrace;
201-
if (!string.IsNullOrEmpty(errorStackTrace))
202-
{
203-
properties.Add("stackTrace", errorStackTrace);
204-
}
205-
206-
string errorMessage = testResult.ErrorMessage;
207-
208-
if (!string.IsNullOrEmpty(errorMessage))
209-
{
210-
properties.Add("errorMessage", errorMessage);
211-
}
212-
}
213-
else
214-
{
215-
// Handle output type skip, NotFound and None
187+
if (testResult.Outcome == TestOutcome.Passed || testResult.Outcome == TestOutcome.Failed)
188+
{
189+
properties.Add("startedDate", testResult.StartTime.ToString(_dateFormatString));
190+
properties.Add("completedDate", testResult.EndTime.ToString(_dateFormatString));
191+
192+
long duration = Convert.ToInt64(testResult.Duration.TotalMilliseconds);
193+
properties.Add("durationInMs", duration.ToString(CultureInfo.InvariantCulture));
194+
195+
string errorStackTrace = testResult.ErrorStackTrace;
196+
if (!string.IsNullOrEmpty(errorStackTrace))
197+
{
198+
properties.Add("stackTrace", errorStackTrace);
199+
}
200+
201+
string errorMessage = testResult.ErrorMessage;
202+
203+
if (!string.IsNullOrEmpty(errorMessage))
204+
{
205+
properties.Add("errorMessage", errorMessage);
206+
}
207+
}
208+
else
209+
{
210+
// Handle output type skip, NotFound and None
216211
}
217212

218213
return properties;
219214
}
220215

221-
internal abstract string GetTestCasesAsCompleted(IEnumerable<TestResultParent> testCases, DateTime completedDate);
222-
223216
internal abstract string GetTestResults(
224217
Dictionary<string, TestResultParent> testCaseTestResults,
225218
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent,
226-
DateTime completedDate);
227-
219+
DateTime completedDate);
220+
228221
internal virtual void AddAdditionalTestResultProperties(ITestResult testResult, Dictionary<string, object> properties)
229222
{
230223
}

src/AzurePipelines.TestLogger/ApiClientV3.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,6 @@ public ApiClientV3(string collectionUri, string teamProject, string apiVersionSt
1212
{
1313
}
1414

15-
internal override string GetTestCasesAsCompleted(IEnumerable<TestResultParent> testCases, DateTime completedDate)
16-
{
17-
// https://docs.microsoft.com/en-us/azure/devops/integrate/previous-apis/test/results?view=tfs-2015#add-test-results-to-a-test-run
18-
return "[ " + string.Join(", ", testCases.Select(x =>
19-
$@"{{
20-
""TestResult"": {{ ""Id"": {x.Id} }},
21-
""testCase"": {{ ""id"": {x.Id} }},
22-
""state"": ""Completed"",
23-
""startedDate"": ""{x.StartedDate.ToString(_dateFormatString)}"",
24-
""completedDate"": ""{completedDate.ToString(_dateFormatString)}""
25-
}}")) + " ]";
26-
}
27-
2815
internal override string GetTestResults(
2916
Dictionary<string, TestResultParent> testCaseTestResults,
3017
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent,

src/AzurePipelines.TestLogger/ApiClientV5.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,6 @@ public ApiClientV5(string collectionUri, string teamProject, string apiVersionSt
1414
{
1515
}
1616

17-
internal override string GetTestCasesAsCompleted(IEnumerable<TestResultParent> testCases, DateTime completedDate)
18-
{
19-
// https://docs.microsoft.com/en-us/rest/api/azure/devops/test/results/add?view=azure-devops-rest-5.0
20-
return "[ " + string.Join(", ", testCases.Select(x =>
21-
$@"{{
22-
""id"": {x.Id},
23-
""state"": ""Completed"",
24-
""startedDate"": ""{x.StartedDate.ToString(_dateFormatString)}"",
25-
""completedDate"": ""{completedDate.ToString(_dateFormatString)}""
26-
}}")) + " ]";
27-
}
28-
2917
internal override string GetTestResults(
3018
Dictionary<string, TestResultParent> testCaseTestResults,
3119
IEnumerable<IGrouping<string, ITestResult>> testResultsByParent,

src/AzurePipelines.TestLogger/AzurePipelinesTestLogger.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ public void Initialize(TestLoggerEvents events, Dictionary<string, string> param
113113

114114
// Register for the events
115115
events.TestRunMessage += TestMessageHandler;
116+
117+
// when a single test has finished
116118
events.TestResult += TestResultHandler;
119+
120+
// when the entire test run is finished
117121
events.TestRunComplete += TestRunCompleteHandler;
118122
}
119123

@@ -137,6 +141,6 @@ private void TestResultHandler(object sender, TestResultEventArgs e) =>
137141
_queue.Enqueue(new VstpTestResult(e.Result));
138142

139143
private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) =>
140-
_queue.Flush(new VstpTestRunComplete(e.AttachmentSets));
144+
_queue.Flush(new VstpTestRunComplete(e.IsAborted || e.IsCanceled, e.AttachmentSets));
141145
}
142146
}

src/AzurePipelines.TestLogger/IApiClient.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@ internal interface IApiClient
1616

1717
IApiClient WithDefaultCredentials();
1818

19-
Task<string> MarkTestCasesCompleted(int testRunId, IEnumerable<TestResultParent> testCases, DateTime completedDate, CancellationToken cancellationToken);
20-
2119
Task<int> AddTestRun(TestRun testRun, CancellationToken cancellationToken);
2220

2321
Task UpdateTestResults(int testRunId, Dictionary<string, TestResultParent> parents, IEnumerable<IGrouping<string, ITestResult>> testResultsByParent, CancellationToken cancellationToken);
2422

25-
Task UpdateTestResults(int runId, VstpTestRunComplete testRunComplete, CancellationToken cancellationToken);
23+
Task UpdateTestResults(int testRunId, VstpTestRunComplete testRunComplete, CancellationToken cancellationToken);
2624

2725
Task<int[]> AddTestCases(int testRunId, string[] testCaseNames, DateTime startedDate, string source, CancellationToken cancellationToken);
2826

29-
Task MarkTestRunCompleted(int testRunId, DateTime startedDate, DateTime completedDate, CancellationToken cancellationToken);
27+
Task MarkTestRunCompleted(int testRunId, bool aborted, DateTime completedDate, CancellationToken cancellationToken);
3028
}
3129
}

src/AzurePipelines.TestLogger/ITestResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ internal interface ITestResult
1212
string FullyQualifiedName { get; }
1313
string DisplayName { get; }
1414
TestOutcome Outcome { get; }
15+
DateTimeOffset StartTime { get; }
16+
DateTimeOffset EndTime { get; }
1517
TimeSpan Duration { get; }
1618
string ErrorStackTrace { get; }
1719
string ErrorMessage { get; }

0 commit comments

Comments
 (0)