Skip to content

Commit

Permalink
Merge pull request #17 from lgrabarevic/feature/test-case-count
Browse files Browse the repository at this point in the history
Feature: Show unit test count in build overview
  • Loading branch information
Luka Grabarevic authored Apr 19, 2018
2 parents 689335a + 458a408 commit b603e9e
Show file tree
Hide file tree
Showing 34 changed files with 912 additions and 236 deletions.
7 changes: 7 additions & 0 deletions BuildsAppReborn.Access/BuildsAppReborn.Access.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Svg, Version=2.2.1.39233, Culture=neutral, PublicKeyToken=12a0bac221edeae2, processorArchitecture=MSIL">
<HintPath>..\packages\Svg.2.3.0\lib\net35\Svg.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
Expand All @@ -62,6 +66,7 @@
<Compile Include="TFS2017\Models\Tfs2017Project.cs" />
<Compile Include="TFS2017\Models\Tfs2017SourceVersion.cs" />
<Compile Include="TFS2017\Models\Tfs2017User.cs" />
<Compile Include="TFS2017\Models\TfsTestRun.cs" />
<Compile Include="TFS2017\Tfs2017BuildProvider.cs" />
<Compile Include="TFS\Models\Internal\LinksContainer.cs" />
<Compile Include="TFS\Models\Internal\Repository.cs" />
Expand All @@ -71,10 +76,12 @@
<Compile Include="TFS\Models\TfsBuildDefinition.cs" />
<Compile Include="TFS\Models\TfsProject.cs" />
<Compile Include="TFS\Models\TfsSourceVersion.cs" />
<Compile Include="TFS\Models\TfsTestRun.cs" />
<Compile Include="TFS\Models\TfsUser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TFS\Models\TfsArtifact.cs" />
<Compile Include="TFS\TfsBuildProviderBase.cs" />
<Compile Include="VSTS\Models\VstsTestRun.cs" />
<Compile Include="VSTS\Models\VstsArtifact.cs" />
<Compile Include="VSTS\Models\VstsBuild.cs" />
<Compile Include="VSTS\Models\VstsBuildDefinition.cs" />
Expand Down
8 changes: 7 additions & 1 deletion BuildsAppReborn.Access/TFS/Models/TfsBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public BuildStatus Status
}
}

[JsonIgnore]
public IEnumerable<ITestRun> TestRuns { get; internal set; }

[JsonProperty("finishTime")]
public DateTime FinishDateTime { get; private set; }

Expand All @@ -98,7 +101,7 @@ public BuildStatus Status
public String Url { get; private set; }

[JsonIgnore]
public String PortalUrl => base.WebLink;
public String WebUrl => base.WebLink;

[JsonIgnore]
public ISourceVersion SourceVersion { get; internal set; }
Expand All @@ -124,6 +127,9 @@ internal String SourceVersionInternal
private set { this.sourceVersionInternal = value; }
}

[JsonProperty("uri")]
internal String Uri { get; private set; }

#endregion

#region Private Fields
Expand Down
2 changes: 1 addition & 1 deletion BuildsAppReborn.Access/TFS/Models/TfsProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace BuildsAppReborn.Access.Models
{
// ReSharper disable once ClassNeverInstantiated.Global
public abstract class TfsProject : IProject
internal abstract class TfsProject : IProject
{
#region Implementation of IProject

Expand Down
43 changes: 43 additions & 0 deletions BuildsAppReborn.Access/TFS/Models/TfsTestRun.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using BuildsAppReborn.Contracts.Models;
using Newtonsoft.Json;

namespace BuildsAppReborn.Access.Models
{
internal abstract class TfsTestRun : ITestRun
{
#region Implementation of ITestRun

[JsonProperty("url")]
public String Url { get; private set; }

[JsonProperty("webAccessUrl")]
public String WebUrl { get; private set; }

[JsonProperty("id")]
public Int32 Id { get; private set; }

[JsonProperty("incompleteTests")]
public Int32 IncompleteTests { get; private set; }

[JsonProperty("name")]
public String Name { get; private set; }

[JsonProperty("notApplicableTests")]
public Int32 NotApplicableTests { get; private set; }

[JsonProperty("passedTests")]
public Int32 PassedTests { get; private set; }

[JsonProperty("state")]
public String State { get; private set; }

[JsonProperty("totalTests")]
public Int32 TotalTests { get; private set; }

[JsonProperty("unanalyzedTests")]
public Int32 FailedTests { get; private set; }

#endregion
}
}
74 changes: 58 additions & 16 deletions BuildsAppReborn.Access/TFS/TfsBuildProviderBase.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

using BuildsAppReborn.Access.Models;
using BuildsAppReborn.Access.Models.Internal;
using BuildsAppReborn.Contracts;
using BuildsAppReborn.Contracts.Models;
using BuildsAppReborn.Infrastructure;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Svg;

namespace BuildsAppReborn.Access
{
internal abstract class TfsBuildProviderBase<TBuild, TBuildDefinition, TUser, TSourceVersion, TArtifact> : TfsBuildProviderBase, IBuildProvider
where TBuild : TfsBuild, new()
where TBuildDefinition : TfsBuildDefinition, new()
where TUser : TfsUser, new()
internal abstract class TfsBuildProviderBase<TBuild, TBuildDefinition, TUser, TSourceVersion, TArtifact, TTestRun> : TfsBuildProviderBase, IBuildProvider
where TBuild : TfsBuild, new()
where TBuildDefinition : TfsBuildDefinition, new()
where TUser : TfsUser, new()
where TSourceVersion : TfsSourceVersion, new()
where TArtifact : TfsArtifact, new()
where TTestRun : TfsTestRun, new()
{
#region Implementation of IBuildProvider

Expand All @@ -42,9 +44,10 @@ public virtual async Task<DataResponse<IEnumerable<IBuildDefinition>>> GetBuildD
buildDefinition.BuildSettingsId = settings.UniqueId;
}

return new DataResponse<IEnumerable<IBuildDefinition>> { Data = data, StatusCode = requestResponse.StatusCode };
return new DataResponse<IEnumerable<IBuildDefinition>> {Data = data, StatusCode = requestResponse.StatusCode};
}
return new DataResponse<IEnumerable<IBuildDefinition>> { Data = Enumerable.Empty<IBuildDefinition>(), StatusCode = requestResponse.StatusCode };

return new DataResponse<IEnumerable<IBuildDefinition>> {Data = Enumerable.Empty<IBuildDefinition>(), StatusCode = requestResponse.StatusCode};
}

throw new Exception($"Error while processing method!");
Expand All @@ -55,7 +58,7 @@ public virtual async Task<DataResponse<IEnumerable<IBuild>>> GetBuilds(IEnumerab
var buildDefinitionsList = buildDefinitions.ToList();
if (!buildDefinitionsList.Any())
{
return new DataResponse<IEnumerable<IBuild>> { Data = Enumerable.Empty<IBuild>(), StatusCode = HttpStatusCode.NoContent };
return new DataResponse<IEnumerable<IBuild>> {Data = Enumerable.Empty<IBuild>(), StatusCode = HttpStatusCode.NoContent};
}

var projectUrl = settings.GetValueStrict<String>(ProjectUrlSettingKey).TrimEnd('/');
Expand All @@ -78,17 +81,17 @@ public virtual async Task<DataResponse<IEnumerable<IBuild>>> GetBuilds(IEnumerab

await ResolveSourceVersion(data, projectUrl, settings);
await ResolveArtifacts(data, projectUrl, settings);
await ResolveTestRuns(data, projectUrl, settings);

return new DataResponse<IEnumerable<IBuild>> { Data = data, StatusCode = requestResponse.StatusCode };
return new DataResponse<IEnumerable<IBuild>> {Data = data, StatusCode = requestResponse.StatusCode};
}
return new DataResponse<IEnumerable<IBuild>> { Data = Enumerable.Empty<IBuild>(), StatusCode = requestResponse.StatusCode };

return new DataResponse<IEnumerable<IBuild>> {Data = Enumerable.Empty<IBuild>(), StatusCode = requestResponse.StatusCode};
}

throw new Exception($"Error while processing method!");
}



#endregion

#region Protected Properties
Expand All @@ -101,10 +104,33 @@ public virtual async Task<DataResponse<IEnumerable<IBuild>>> GetBuilds(IEnumerab

private static async Task<Byte[]> GetImageData(BuildMonitorSettings settings, IUser user)
{
var response = await GetRequestResponse(user.ImageUrl, settings);
if (response.IsSuccessStatusCode)
try
{
return await response.Content.ReadAsByteArrayAsync();
var response = await GetRequestResponse(user.ImageUrl, settings);
if (response.IsSuccessStatusCode)
{
if (response.Content.Headers.ContentType.MediaType == "image/png")
{
return await response.Content.ReadAsByteArrayAsync();
}

if (response.Content.Headers.ContentType.MediaType == "image/svg+xml")
{
using (var memoryStream = new MemoryStream())
{
await response.Content.CopyToAsync(memoryStream);
memoryStream.Position = 0;
var svgDoc = SvgDocument.Open<SvgDocument>(memoryStream);

var converter = new ImageConverter();
return (Byte[]) converter.ConvertTo(svgDoc.Draw(128, 128), typeof(Byte[]));
}
}
}
}
catch (Exception)
{
return null;
}

return null;
Expand All @@ -119,6 +145,7 @@ private static async Task<HttpResponseMessage> GetRequestResponse(String request
{
return await HttpRequestHelper.GetRequestResponse(requestUrl, accessToken);
}

return await HttpRequestHelper.GetRequestResponse(requestUrl, credentials);
}

Expand Down Expand Up @@ -206,6 +233,21 @@ private async Task ResolveSourceVersion(IEnumerable<TBuild> builds, String proje
}
}

private async Task ResolveTestRuns(IEnumerable<TBuild> builds, String projectUrl, BuildMonitorSettings settings)
{
foreach (var build in builds)
{
var requestUrl = $"{projectUrl}/_apis/test/runs?api-version=1.0&buildUri={build.Uri}";

var requestResponse = await GetRequestResponse(requestUrl, settings);
if (requestResponse.IsSuccessStatusCode)
{
var result = await requestResponse.Content.ReadAsStringAsync();
build.TestRuns = JsonConvert.DeserializeObject<IEnumerable<TTestRun>>(JObject.Parse(result)["value"].ToString());
}
}
}

private async Task SetSourceVersion<TInnerSourceVersion>(BuildMonitorSettings settings, String requestUrl, TBuild build) where TInnerSourceVersion : ISourceVersion, new()
{
var requestResponse = await GetRequestResponse(requestUrl, settings);
Expand Down
7 changes: 7 additions & 0 deletions BuildsAppReborn.Access/TFS2017/Models/TfsTestRun.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace BuildsAppReborn.Access.Models
{
// ReSharper disable once UnusedMember.Global
internal class Tfs2017TestRun : TfsTestRun
{
}
}
2 changes: 1 addition & 1 deletion BuildsAppReborn.Access/TFS2017/Tfs2017BuildProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BuildsAppReborn.Access
{
[BuildProviderExport(typeof(IBuildProvider), Id, Name, AuthenticationModes.Default | AuthenticationModes.AccessToken)]
[PartCreationPolicy(CreationPolicy.Shared)]
internal class Tfs2017BuildProvider : TfsBuildProviderBase<Tfs2017Build, Tfs2017BuildDefinition, Tfs2017User, Tfs2017SourceVersion, Tfs2017Artifact>
internal class Tfs2017BuildProvider : TfsBuildProviderBase<Tfs2017Build, Tfs2017BuildDefinition, Tfs2017User, Tfs2017SourceVersion, Tfs2017Artifact, Tfs2017TestRun>
{
#region Overrides of Base

Expand Down
1 change: 1 addition & 0 deletions BuildsAppReborn.Access/VSTS/Models/VstsBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal class VstsBuild : TfsBuild
[JsonConverter(typeof(InterfaceTypeConverter<VstsUser, IUser>))]
public override IUser Requester { get; protected set; }


#endregion
}
}
7 changes: 7 additions & 0 deletions BuildsAppReborn.Access/VSTS/Models/VstsTestRun.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace BuildsAppReborn.Access.Models
{
// ReSharper disable once UnusedMember.Global
internal class VstsTestRun : TfsTestRun
{
}
}
2 changes: 1 addition & 1 deletion BuildsAppReborn.Access/VSTS/VstsBuildProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BuildsAppReborn.Access
{
[BuildProviderExport(typeof(IBuildProvider), Id, Name, AuthenticationModes.AccessToken)]
[PartCreationPolicy(CreationPolicy.Shared)]
internal class VstsBuildProvider : TfsBuildProviderBase<VstsBuild, VstsBuildDefinition, VstsUser, VstsSourceVersion, VstsArtifact>
internal class VstsBuildProvider : TfsBuildProviderBase<VstsBuild, VstsBuildDefinition, VstsUser, VstsSourceVersion, VstsArtifact, VstsTestRun>
{
#region Overrides of Base

Expand Down
1 change: 1 addition & 0 deletions BuildsAppReborn.Access/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<packages>
<package id="log4net" version="2.0.8" targetFramework="net461" />
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net461" />
<package id="Svg" version="2.3.0" targetFramework="net461" />
</packages>
4 changes: 4 additions & 0 deletions BuildsAppReborn.Client/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using BuildsAppReborn.Client.ViewModels;
using BuildsAppReborn.Contracts;
using Hardcodet.Wpf.TaskbarNotification;
Expand Down Expand Up @@ -34,6 +35,9 @@ protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);

ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));

RegisterToWindowsStartUp();

EnsureOnlyOneInstance();
Expand Down
12 changes: 12 additions & 0 deletions BuildsAppReborn.Client/BuildsAppReborn.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,20 @@
<Compile Include="Cache\BuildCache.cs" />
<Compile Include="Cache\BuildCacheStatus.cs" />
<Compile Include="Consts.cs" />
<Compile Include="Controls\StackedBarGraph.xaml.cs">
<DependentUpon>StackedBarGraph.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\StackedBarGraphHeightConverter.cs" />
<Compile Include="Controls\StackedItem.cs" />
<Compile Include="Controls\StackedItemCollection.cs" />
<Compile Include="Converter\BuildItemToRelativeMarginConverter.cs" />
<Compile Include="Converter\BuildStatusToImageConverter.cs" />
<Compile Include="Converter\BuildStatusToSolidColorBrushForegroundConverter.cs" />
<Compile Include="Converter\BuildStatusToSolidColorBrushBackgroundConverter.cs" />
<Compile Include="Converter\CountToVisibilityConverter.cs" />
<Compile Include="Converter\NullVisibilityConverter.cs" />
<Compile Include="Converter\MinutesToTimeSpanConverter.cs" />
<Compile Include="Converter\TestRunToStackedItemsConverter.cs" />
<Compile Include="Converter\TimeSpanToNiceStringConverter.cs" />
<Compile Include="Interfaces\ISaveable.cs" />
<Compile Include="Interfaces\IHasDataContext.cs" />
Expand All @@ -174,6 +182,10 @@
<Compile Include="ViewModels\BuildsStatusViewModel.cs" />
<Compile Include="ViewModels\NotifyIconViewModel.cs" />
<Compile Include="ViewModels\Settings\ServerSettingsViewModel.cs" />
<Page Include="Controls\StackedBarGraph.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\BuildsStatusView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down
11 changes: 8 additions & 3 deletions BuildsAppReborn.Client/Cache/BuildCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,15 @@ private void OnBuildsUpdated(ICollection<IBuild> builds)
var groupByDefinition = builds.GroupBy(a => a.Definition, build => build, this.buildDefinitionEqualityComparer);
foreach (var grp in groupByDefinition)
{
var newStatus = new BuildStatusGroup();
var oldStatus = BuildsStatus.SingleOrDefault(a => this.buildDefinitionEqualityComparer.Equals(grp.Key, a.BuildDefinition));
var newStatus = new BuildStatusGroup(grp.Key, grp.Select(a => new BuildItem(a)).ToList());

if (oldStatus != null)
{
// ToDo: implement proper update of bound viewmodel objects instead of creating new ones everytime
newStatus.AdditionalInformationShown = oldStatus.AdditionalInformationShown;
}

newStatus.BuildDefinition = grp.Key;
newStatus.AllBuildItems = grp.Select(a => new BuildItem(a)).ToList();
buildStatusGroups.Add(newStatus);
}
Application.Current.Dispatcher.Invoke(() =>
Expand Down
Loading

0 comments on commit b603e9e

Please sign in to comment.