Skip to content
This repository has been archived by the owner on Jan 8, 2023. It is now read-only.

Explore button + ability to run individual tests #122

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/nunit.xamarin/Extensions/XamarinExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
// ***********************************************************************

using NUnit.Framework.Interfaces;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;

namespace NUnit.Runner.Extensions
Expand Down Expand Up @@ -54,5 +57,15 @@ public static Color Color(this ResultState result)
return Xamarin.Forms.Color.Gray;
}
}

public static IEnumerable<string> PropertyValues(this ITest test, string propertyName)
{
if (!test.Properties.ContainsKey(propertyName)) yield break;
else
{
for (int i = 0; i < test.Properties[propertyName].Count; i++)
yield return test.Properties[propertyName][i]?.ToString();
}
}
}
}
38 changes: 35 additions & 3 deletions src/nunit.xamarin/Helpers/TestPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
// ***********************************************************************

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using NUnit.Framework.Api;
Expand All @@ -42,20 +43,51 @@ public void AddAssembly(Assembly testAssembly, Dictionary<string,object> options
_testAssemblies.Add( (testAssembly, options) );
}

public async Task<TestRunResult> ExecuteTests()
public async Task<TestRunResult> ExecuteTests() => await ExecuteTests(Enumerable.Empty<string>());

public async Task<TestRunResult> ExecuteTests(IEnumerable<string> testNames)
{
var testFilter = testNames?.Any() == true ?
(TestFilter.FromXml($"<filter><or>{string.Join("", testNames.Select(n => $"<test>{n}</test>"))}</or></filter>")) :
TestFilter.Empty;

var resultPackage = new TestRunResult();

foreach (var (assembly,options) in _testAssemblies)
foreach (var (assembly, options) in _testAssemblies)
{
NUnitTestAssemblyRunner runner = await LoadTestAssemblyAsync(assembly, options).ConfigureAwait(false);
ITestResult result = await Task.Run(() => runner.Run(TestListener.NULL, TestFilter.Empty)).ConfigureAwait(false);
ITestResult result = await Task.Run(() => runner.Run(TestListener.NULL, testFilter)).ConfigureAwait(false);
resultPackage.AddResult(result);
}
resultPackage.CompleteTestRun();
return resultPackage;
}

public async Task<IEnumerable<(ITest Test, string Assembly)>> EnumerateTestsAsync()
{
var resultTests = new List<(ITest Test, string Assembly)>();
foreach (var (assembly, options) in _testAssemblies)
{
var runner = await LoadTestAssemblyAsync(assembly, options).ConfigureAwait(false);
var assemblyTests = await Task.Run(() => runner.ExploreTests(TestFilter.Empty));
resultTests.AddRange(EnumerateTestsInternal(assemblyTests).Select(t => (t, assembly.GetName().Name)));
}
return resultTests;
}

private IEnumerable<ITest> EnumerateTestsInternal(ITest parent)
{
var resultTests = new List<ITest>();

foreach (var test in parent.Tests)
{
if (test.HasChildren) resultTests.AddRange(EnumerateTestsInternal(test));
else resultTests.Add(test);
}

return resultTests;
}

private static async Task<NUnitTestAssemblyRunner> LoadTestAssemblyAsync(Assembly assembly, Dictionary<string, object> options)
{
var runner = new NUnitTestAssemblyRunner(new DefaultTestAssemblyBuilder());
Expand Down
23 changes: 16 additions & 7 deletions src/nunit.xamarin/View/SummaryView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@
</DataTrigger>
</ContentPage.Triggers>
<StackLayout Orientation="Vertical" Spacing="4" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="{DynamicResource defaultBackground}">
<Button Text="Run Tests" Command="{Binding RunTestsCommand}" HorizontalOptions="FillAndExpand">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding Running}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Button Text="Run Tests" Command="{Binding RunTestsCommand}" HorizontalOptions="StartAndExpand">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding Running}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>
<Button Text="Explore" Command="{Binding ExploreTestsCommand}" HorizontalOptions="EndAndExpand">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding Running}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>
</Button>
</StackLayout>

<ScrollView Orientation="Vertical"
VerticalOptions="FillAndExpand"
Expand Down
40 changes: 40 additions & 0 deletions src/nunit.xamarin/View/TestExplorerView.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="Test Explorer"
Padding="10"
BackgroundColor="{DynamicResource defaultBackground}"
x:Name="testExplorerPage"
x:Class="NUnit.Runner.View.TestExplorerView">
<ContentPage.Triggers>
<DataTrigger TargetType="ContentPage" Binding="{Binding Busy}" Value="True">
<Setter Property="IsBusy" Value="True" />
</DataTrigger>
</ContentPage.Triggers>
<StackLayout Orientation="Vertical" Spacing="4" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="{DynamicResource defaultBackground}">
<ListView IsGroupingEnabled="True" ItemsSource="{Binding TestList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" VerticalOptions="Center" Padding="5">
<Label VerticalOptions="Center" FontSize="Small" Text="{Binding Name}" VerticalTextAlignment="Center" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference testExplorerPage}, Path=BindingContext.RunTestCommand}" NumberOfTapsRequired="2" CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="#005B0C" >
<Label Margin="5" FontAttributes="Bold" Text="{Binding GroupName}" FontSize="Small" TextColor="White" VerticalTextAlignment="Center" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>

</StackLayout>
</ContentPage>
43 changes: 43 additions & 0 deletions src/nunit.xamarin/View/TestExplorerView.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using NUnit.Runner.Messages;
using NUnit.Runner.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace NUnit.Runner.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestExplorerView : ContentPage
{
TestExplorerViewModel _model;

internal TestExplorerView(TestExplorerViewModel model)
{
_model = model;
_model.Navigation = Navigation;
BindingContext = _model;
InitializeComponent();

MessagingCenter.Subscribe<ErrorMessage>(this, ErrorMessage.Name, error =>
{
Device.BeginInvokeOnMainThread(async () => await DisplayAlert("Error", error.Message, "OK"));
});
}

/// <summary>
/// Called when the view is appearing
/// </summary>
protected override void OnAppearing()
{
base.OnAppearing();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
_model.LoadTestsAsync();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
}
}
43 changes: 43 additions & 0 deletions src/nunit.xamarin/ViewModel/GroupedTestsViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// ***********************************************************************
// Copyright (c) 2015 NUnit Project
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace NUnit.Runner.ViewModel
{
class GroupedTestsViewModel : ObservableCollection<TestDescriptionViewModel>
{
public GroupedTestsViewModel(string groupName, IEnumerable<TestDescriptionViewModel> tests)
{
GroupName = groupName;
foreach (var test in (tests ?? Enumerable.Empty<TestDescriptionViewModel>()))
Items.Add(test);
}

public string GroupName { get; }
}
}
21 changes: 21 additions & 0 deletions src/nunit.xamarin/ViewModel/SummaryViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using NUnit.Runner.Services;

using Xamarin.Forms;
using System.Linq;

namespace NUnit.Runner.ViewModel
{
Expand All @@ -45,6 +46,9 @@ public SummaryViewModel()
{
_testPackage = new TestPackage();
RunTestsCommand = new Command(async o => await ExecuteTestsAync(), o => !Running);

ExploreTestsCommand = new Command(async o => await Navigation.PushAsync(new TestExplorerView(new TestExplorerViewModel(_testPackage, this) { Navigation = Navigation})), o => !Running);

ViewAllResultsCommand = new Command(
async o => await Navigation.PushAsync(new ResultsView(new ResultsViewModel(_results.GetTestResults(), true))),
o => !HasResults);
Expand Down Expand Up @@ -123,6 +127,7 @@ public bool Running
public ICommand RunTestsCommand { set; get; }
public ICommand ViewAllResultsCommand { set; get; }
public ICommand ViewFailedResultsCommand { set; get; }
public ICommand ExploreTestsCommand { get; set; }

/// <summary>
/// Adds an assembly to be tested.
Expand All @@ -138,6 +143,7 @@ async Task ExecuteTestsAync()
{
Running = true;
Results = null;

TestRunResult results = await _testPackage.ExecuteTests();
ResultSummary summary = new ResultSummary(results);

Expand All @@ -155,6 +161,21 @@ async Task ExecuteTestsAync()
});
}

internal async Task DisplayResults(TestRunResult results)
{
ResultSummary summary = new ResultSummary(results);

_resultProcessor = TestResultProcessor.BuildChainOfResponsability(Options);
await _resultProcessor.Process(summary).ConfigureAwait(false);

Device.BeginInvokeOnMainThread(
() =>
{
Results = summary;
Running = false;
});
}

public static void TerminateWithSuccess()
{
#if __IOS__
Expand Down
73 changes: 73 additions & 0 deletions src/nunit.xamarin/ViewModel/TestDescriptionViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// ***********************************************************************
// Copyright (c) 2015 NUnit Project
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ***********************************************************************

using NUnit.Framework.Interfaces;
using NUnit.Runner.Extensions;
using NUnit.Runner.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NUnit.Runner.ViewModel
{
class TestDescriptionViewModel : BaseViewModel
{
public TestDescriptionViewModel(ITest test, string assembly)
{
_test = test;
Name = StringOrUnknown(_test.Name);
MethodName = StringOrUnknown(_test.MethodName);
ClassName = StringOrUnknown(_test.ClassName);
FullName = StringOrUnknown(_test.FullName);
Assembly = assembly;
Categories = _test.PropertyValues("Category").ToArray();
}

private readonly ITest _test;
public string Name { get; }
public string MethodName { get; }
public string ClassName { get; }
public string FullName { get; }
public string Assembly { get; }
public string[] Categories { get; }

private bool _selected;

/// <summary>
/// True if test is selected
/// </summary>
public bool Selected
{
get { return _selected; }
set
{
if (value.Equals(_selected)) return;
_selected = value;
OnPropertyChanged();
}
}

private string StringOrUnknown(string str) => string.IsNullOrWhiteSpace(str) ? "<unknown>" : str;
}
}