Skip to content

Commit cf480a9

Browse files
committed
Count of Controls and Prefabs used in test cases and Prefab are tracked now
Whenever we add a control or prefab to an automation processs or any test case or fixture, we add a reference to it in project references file along with version used. However, this information is not enough to tell what test cases or prefabs hold these references. To answer this, we are storing usage count of each control and prefab now in test case and fixture definition file.
1 parent ff6287c commit cf480a9

File tree

26 files changed

+735
-33
lines changed

26 files changed

+735
-33
lines changed

src/Pixel.Automation.AppExplorer.ViewModels/Control/ControlExplorerViewModel.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ public async Task ChangeImageFromExistingAsync(ControlDescriptionViewModel selec
363363
{
364364
try
365365
{
366+
Guard.Argument(selectedControl, nameof(selectedControl)).NotNull();
366367
OpenFileDialog openFileDialog = new OpenFileDialog();
367368
openFileDialog.Filter = "PNG File (*.Png)|*.Png";
368369
openFileDialog.InitialDirectory = Environment.CurrentDirectory;
@@ -398,8 +399,7 @@ public async Task CloneControl(ControlDescriptionViewModel controlToClone)
398399
{
399400
try
400401
{
401-
Guard.Argument(controlToClone).NotNull();
402-
402+
Guard.Argument(controlToClone, nameof(controlToClone)).NotNull();
403403
var clonedControl = controlToClone.ControlDescription.Clone() as ControlDescription;
404404
var controlDescriptionViewModel = new ControlDescriptionViewModel(clonedControl);
405405
controlDescriptionViewModel.ControlName = Path.GetRandomFileName();
@@ -423,8 +423,7 @@ public async Task CreateRevision(ControlDescriptionViewModel control)
423423
{
424424
try
425425
{
426-
Guard.Argument(control).NotNull();
427-
426+
Guard.Argument(control, nameof(control)).NotNull();
428427
var clonedControl = control.ControlDescription.Clone() as ControlDescription;
429428
clonedControl.ControlId = control.ControlId;
430429
clonedControl.Version = new Version(control.Version.Major + 1, 0);
@@ -452,6 +451,7 @@ public async Task CreateRevision(ControlDescriptionViewModel control)
452451
/// <returns></returns>
453452
public async Task SaveControlDetails(ControlDescriptionViewModel controlToSave, bool updateApplication)
454453
{
454+
Guard.Argument(controlToSave, nameof(controlToSave)).NotNull();
455455
await this.applicationDataManager.AddOrUpdateControlAsync(controlToSave.ControlDescription);
456456
lock (locker)
457457
{
@@ -505,6 +505,19 @@ private async Task SaveBitMapSource(ControlDescription controlDescription, Image
505505
throw new ArgumentException($"{nameof(imageSource)} must be a BitmapImage");
506506
}
507507

508+
509+
/// <summary>
510+
/// Broadcast a FilterTestMessage which is processed by Test explorer view to filter and show only those test cases
511+
/// which uses this prefab
512+
/// </summary>
513+
/// <param name="targetPrefab"></param>
514+
/// <returns></returns>
515+
public async Task ShowUsage(ControlDescriptionViewModel controlDescription)
516+
{
517+
Guard.Argument(controlDescription, nameof(controlDescription)).NotNull();
518+
await this.eventAggregator.PublishOnUIThreadAsync(new TestFilterNotification("control", controlDescription.ControlId));
519+
}
520+
508521
#endregion Manage Controls
509522

510523
/// <summary>

src/Pixel.Automation.AppExplorer.ViewModels/PrefabDropHandler/PrefabDropHandlerViewModel.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using Pixel.Automation.AppExplorer.ViewModels.Prefab;
1+
using Caliburn.Micro;
2+
using Dawn;
3+
using Pixel.Automation.AppExplorer.ViewModels.Prefab;
24
using Pixel.Automation.Core;
35
using Pixel.Automation.Core.Attributes;
46
using Pixel.Automation.Core.Components.Prefabs;
7+
using Pixel.Automation.Core.Components.TestCase;
58
using Pixel.Automation.Core.Interfaces;
69
using Pixel.Automation.Editor.Core;
710
using Pixel.Automation.Editor.Core.ViewModels;
@@ -22,6 +25,7 @@ public class PrefabDropHandlerViewModel : Wizard
2225
private readonly IProjectFileSystem projectFileSystem;
2326
private readonly IScriptEngine scriptEngine;
2427
private readonly IScriptEditorFactory scriptEditorFactory;
28+
private readonly IEventAggregator eventAggregator;
2529

2630
private PrefabEntity prefabEntity;
2731
private EntityComponentViewModel dropTarget;
@@ -30,14 +34,18 @@ public class PrefabDropHandlerViewModel : Wizard
3034
/// constructor
3135
/// </summary>
3236
/// <param name="entityManager">EntityManager of the Entity which is the drop target</param>
33-
public PrefabDropHandlerViewModel(IEntityManager entityManager, PrefabProjectViewModel prefabProjectViewModel, EntityComponentViewModel dropTarget)
37+
public PrefabDropHandlerViewModel(IEntityManager entityManager, PrefabProjectViewModel prefabProjectViewModel,
38+
EntityComponentViewModel dropTarget)
3439
{
35-
this.DisplayName = "(1/3) Select prefab version and mapping scripts";
40+
Guard.Argument(entityManager, nameof(entityManager)).NotNull();
41+
Guard.Argument(prefabProjectViewModel, nameof(prefabProjectViewModel)).NotNull();
42+
this.DisplayName = "(1/3) Select prefab version and mapping scripts";
3643
this.scriptEditorFactory = entityManager.GetServiceOfType<IScriptEditorFactory>(); ;
3744
this.projectFileSystem = entityManager.GetCurrentFileSystem() as IProjectFileSystem;
3845
this.prefabFileSystem = entityManager.GetServiceOfType<IPrefabFileSystem>();
3946
this.scriptEngine = entityManager.GetScriptEngine();
40-
this.dropTarget = dropTarget;
47+
this.eventAggregator = entityManager.GetServiceOfType<IEventAggregator>();
48+
this.dropTarget = Guard.Argument(dropTarget, nameof(dropTarget)).NotNull();
4149

4250
this.prefabEntity = new PrefabEntity()
4351
{
@@ -71,6 +79,21 @@ public PrefabDropHandlerViewModel(IEntityManager entityManager, PrefabProjectVie
7179
this.stagedScreens.Add(prefabOutputMappingScript);
7280
}
7381

82+
83+
public override async Task Finish()
84+
{
85+
var addToComponent = this.dropTarget.Model;
86+
if (addToComponent.TryGetAnsecstorOfType<TestCaseEntity>(out TestCaseEntity testCaseEntity))
87+
{
88+
await this.eventAggregator.PublishOnBackgroundThreadAsync(new PrefabAddedEventArgs(this.prefabEntity.PrefabId, testCaseEntity.Tag));
89+
}
90+
else if (addToComponent.TryGetAnsecstorOfType<TestFixtureEntity>(out TestFixtureEntity testFixtureEntity))
91+
{
92+
await this.eventAggregator.PublishOnBackgroundThreadAsync(new PrefabAddedEventArgs(this.prefabEntity.PrefabId, testFixtureEntity.Tag));
93+
}
94+
await base.Finish();
95+
}
96+
7497
public override async Task Cancel()
7598
{
7699
var prefabComponentViewModel = this.dropTarget.ComponentCollection.FirstOrDefault(a => a.Model.Equals(this.prefabEntity));

src/Pixel.Automation.AppExplorer.ViewModels/PrefabDropHandler/PrefabVersionSelectorViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
using Microsoft.Win32;
22
using Pixel.Automation.AppExplorer.ViewModels.Prefab;
33
using Pixel.Automation.Core.Components.Prefabs;
4+
using Pixel.Automation.Core.Components.TestCase;
45
using Pixel.Automation.Core.Interfaces;
56
using Pixel.Automation.Core.Models;
67
using Pixel.Automation.Editor.Core;
78
using Pixel.Automation.Editor.Core.ViewModels;
89
using Pixel.Automation.Reference.Manager;
910
using Pixel.Automation.Reference.Manager.Contracts;
10-
using Pixel.Persistence.Services.Client.Interfaces;
1111
using Serilog;
1212
using System.IO;
1313
using System.Windows;

src/Pixel.Automation.AppExplorer.Views/Control/ControlExplorerView.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<MenuItem x:Name="CreateRevision" Header="Create Revision" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action CreateRevision($dataContext)]"></MenuItem>
7777
<MenuItem x:Name="ChangeImage" Header="Change Image" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action ChangeImageFromExistingAsync($dataContext)]"></MenuItem>
7878
<MenuItem x:Name="MoveToScreen" Header="Move To Screen" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action MoveToScreen($dataContext)]"></MenuItem>
79+
<MenuItem x:Name="ShowUsage" Header="Show Usage" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action ShowUsage($dataContext)]"></MenuItem>
7980
<MenuItem x:Name="Delete" Header="Delete" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action DeleteControlAsync($dataContext)]" ></MenuItem>
8081
</StackPanel>
8182
</ControlTemplate>

src/Pixel.Automation.Core/Component/EntityHelpers.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Text;
78

89
namespace Pixel.Automation.Core
910
{
@@ -28,6 +29,33 @@ public static List<IComponent> GetAllComponents(this Entity rootEntity)
2829
return childComponents;
2930
}
3031

32+
/// <summary>
33+
/// Check if a component of given type exists within specified SearchScope of the Entity
34+
/// </summary>
35+
/// <typeparam name="T"></typeparam>
36+
/// <param name="rootEntity"></param>
37+
/// <param name="searchScope"></param>
38+
/// <returns></returns>
39+
public static bool HasComponentsOfType<T>(this Entity rootEntity, SearchScope searchScope = SearchScope.Descendants)
40+
{
41+
switch (searchScope)
42+
{
43+
case SearchScope.Children:
44+
return rootEntity.Components.OfType<T>().Any();
45+
case SearchScope.Descendants:
46+
List<IComponent> childComponents = GetAllComponents(rootEntity);
47+
foreach (var component in childComponents)
48+
{
49+
if (component is T)
50+
{
51+
return true;
52+
}
53+
}
54+
break;
55+
}
56+
return false;
57+
}
58+
3159
/// <summary>
3260
/// Returns all components of Type : T in the rootEntity or any of its children
3361
/// </summary>
@@ -310,7 +338,7 @@ public static IEnumerable<ILoop> GetInnerLoops(this Entity rootEntity)
310338
/// <param name="component"></param>
311339
/// <returns></returns>
312340
/// <exception cref="MissingComponentException">Thrown when ancestor of specified type T could not be found</exception>
313-
public static T GetAnsecstorOfType<T>(this IComponent component) where T : class,IComponent
341+
public static T GetAnsecstorOfType<T>(this IComponent component) where T : class, IComponent
314342
{
315343
var parent = component.Parent;
316344
while(parent!=null)
@@ -323,7 +351,7 @@ public static T GetAnsecstorOfType<T>(this IComponent component) where T : class
323351
}
324352
throw new MissingComponentException($"Ancestor of type :{typeof(T)} could not be located");
325353
}
326-
354+
327355
/// <summary>
328356
/// Try to get an ancestor component of type T
329357
/// </summary>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace Pixel.Automation.Core.Models
4+
{
5+
/// <summary>
6+
/// Tracks usage of a control across fixtures and test cases
7+
/// </summary>
8+
[DataContract]
9+
public class ControlUsage
10+
{
11+
/// <summary>
12+
/// Identifier of Control
13+
/// </summary>
14+
[DataMember(IsRequired = true, Order = 10)]
15+
public string ControlId { get; set; }
16+
17+
/// <summary>
18+
/// Reference count
19+
/// </summary>
20+
[DataMember(IsRequired = true, Order = 20)]
21+
public int Count { get; set; }
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Runtime.Serialization;
2+
3+
namespace Pixel.Automation.Core.Models
4+
{
5+
/// <summary>
6+
/// Tracks usage of a prefab across fixtures and test cases
7+
/// </summary>
8+
[DataContract]
9+
public class PrefabUsage
10+
{
11+
/// <summary>
12+
/// Identifier of Prefab
13+
/// </summary>
14+
[DataMember(IsRequired = true, Order = 10)]
15+
public string PrefabId { get; set; }
16+
17+
/// <summary>
18+
/// Reference count
19+
/// </summary>
20+
[DataMember(IsRequired = true, Order = 20)]
21+
public int Count { get; set; }
22+
}
23+
}

src/Pixel.Automation.Core/TestData/TestCase.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Pixel.Automation.Core.Attributes;
22
using Pixel.Automation.Core.Enums;
3+
using Pixel.Automation.Core.Models;
34
using System;
5+
using System.Collections.Generic;
46
using System.Runtime.Serialization;
57
using System.Text.Json.Serialization;
68

@@ -81,6 +83,18 @@ public class TestCase : ICloneable
8183
[DataMember(IsRequired = true, Order = 110)]
8284
public TagCollection Tags { get; private set; } = new TagCollection();
8385

86+
/// <summary>
87+
/// Collection of Identifiers of controls used by the test case
88+
/// </summary>
89+
[DataMember(IsRequired = true, Order = 120)]
90+
public List<ControlUsage> ControlsUsed { get; private set; } = new();
91+
92+
/// <summary>
93+
/// Collection of Identifiers of Prefabs used by the test case
94+
/// </summary>
95+
[DataMember(IsRequired = true, Order = 130)]
96+
public List<PrefabUsage> PrefabsUsed { get; private set; } = new();
97+
8498
/// <summary>
8599
/// Indicates if the TestCase is deleted. Deleted test cases are not loaded in explorer.
86100
/// </summary>

src/Pixel.Automation.Core/TestData/TestFixture.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Pixel.Automation.Core.Attributes;
2+
using Pixel.Automation.Core.Models;
23
using System;
34
using System.Collections.Generic;
45
using System.Runtime.Serialization;
@@ -66,6 +67,18 @@ public class TestFixture
6667
[DataMember(IsRequired = true, Order = 90)]
6768
public TagCollection Tags { get; private set; } = new TagCollection();
6869

70+
/// <summary>
71+
/// Collection of Identifiers of controls used by the test fixture
72+
/// </summary>
73+
[DataMember(IsRequired = true, Order = 120)]
74+
public List<ControlUsage> ControlsUsed { get; private set; } = new();
75+
76+
/// <summary>
77+
/// Collection of Identifiers of Prefabs used by the test fixture
78+
/// </summary>
79+
[DataMember(IsRequired = true, Order = 130)]
80+
public List<PrefabUsage> PrefabsUsed { get; private set; } = new();
81+
6982
/// <summary>
7083
/// Indicates if the fixture was deleted. Deleted fixtures are not loaded in explorer.
7184
/// </summary>

src/Pixel.Automation.Designer.ViewModels/AutomationBuilder/EditorViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using Pixel.Automation.Editor.Core.Interfaces;
1010
using Pixel.Automation.Editor.Core.ViewModels;
1111
using Serilog;
12-
using System.IO;
1312
using System.Windows;
1413
using IDropTarget = GongSolutions.Wpf.DragDrop.IDropTarget;
1514
using MessageBox = System.Windows.MessageBox;
@@ -94,7 +93,8 @@ public async Task DeleteComponent(ComponentViewModel componentViewModel)
9493
scripts = this.scriptExtractor.ExtractScripts(componentViewModel.Model).ToList();
9594
}
9695

97-
var deleteScriptsViewModel = new DeleteComponentViewModel(componentViewModel, scripts ?? Enumerable.Empty<ScriptStatus>(), projectManager);
96+
var deleteScriptsViewModel = new DeleteComponentViewModel(componentViewModel, scripts ?? Enumerable.Empty<ScriptStatus>(),
97+
this.projectManager, this.globalEventAggregator);
9898
var result = await this.windowManager.ShowDialogAsync(deleteScriptsViewModel);
9999
if (!result.GetValueOrDefault())
100100
{

0 commit comments

Comments
 (0)