diff --git a/build/ci/.azure-pipelines.RuntimeTests.Skia.yml b/build/ci/.azure-pipelines.RuntimeTests.Skia.yml new file mode 100644 index 0000000000..0a5d0e708f --- /dev/null +++ b/build/ci/.azure-pipelines.RuntimeTests.Skia.yml @@ -0,0 +1,56 @@ +jobs: +- job: Skia_Tests + displayName: 'Runtime Tests - Skia GTK' + timeoutInMinutes: 60 + + pool: + vmImage: 'ubuntu-20.04' + + variables: + NUGET_PACKAGES: $(build.sourcesdirectory)/.nuget + + steps: + - checkout: self + clean: true + + - task: UseDotNet@2 + displayName: 'Use .NET' + inputs: + packageType: 'sdk' + version: '7.x' + + - script: | + dotnet tool install -g uno.check + uno-check --target skiagtk --fix --non-interactive --ci + + displayName: 'Run uno-check' + + - script: dotnet build Uno.Extensions.RuntimeTests.Skia.Gtk.csproj -c Debug -p:UnoTargetFrameworkOverride=net7.0 -p:GeneratePackageOnBuild=false -bl:$(Build.ArtifactStagingDirectory)/skia-gtk-runtime-test-build.binlog + displayName: 'Build Runtime Tests app (GTK)' + workingDirectory: $(Build.SourcesDirectory)/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Gtk + + - task: PublishBuildArtifacts@1 + displayName: Publish Build Logs + retryCountOnTaskFailure: 3 + condition: always() + inputs: + PathtoPublish: $(build.artifactstagingdirectory)/skia-gtk-runtime-test-build.binlog + ArtifactName: skia-runtime-test-build + ArtifactType: Container + + - script: xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' dotnet Uno.Extensions.RuntimeTests.Skia.Gtk.dll + displayName: 'Run Runtime Tests (GTK)' + workingDirectory: $(Build.SourcesDirectory)/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Gtk/bin/Debug/net7.0 + env: + UNO_RUNTIME_TESTS_RUN_TESTS: '{}' + UNO_RUNTIME_TESTS_OUTPUT_PATH: '$(Common.TestResultsDirectory)/skia-gtk-runtime-tests-results.xml' + + - task: PublishTestResults@2 + displayName: 'Publish GTK Runtime Tests Results' + condition: always() + retryCountOnTaskFailure: 3 + inputs: + testRunTitle: 'GTK Runtime Tests Run' + testResultsFormat: 'NUnit' + testResultsFiles: '$(Common.TestResultsDirectory)/skia-gtk-runtime-tests-results.xml' + failTaskOnFailedTests: true diff --git a/build/ci/.azure-pipelines.yml b/build/ci/.azure-pipelines.yml index fc071fd4a2..935589cf4c 100644 --- a/build/ci/.azure-pipelines.yml +++ b/build/ci/.azure-pipelines.yml @@ -56,5 +56,6 @@ jobs: - template: .azure-pipelines.Wasm.yml - template: .azure-pipelines.UITests.Wasm.yml +- template: .azure-pipelines.RuntimeTests.Skia.yml - template: .azure-pipelines.Changelog.yml diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index a58f9264e0..0fa98e159a 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -49,7 +49,7 @@ - + diff --git a/src/Uno.Extensions.Reactive.UI.Tests/Generator/Given_VMWithCommands.cs b/src/Uno.Extensions.Reactive.UI.Tests/Generator/Given_VMWithCommands.cs index 182dc9ae4f..996fee52cd 100644 --- a/src/Uno.Extensions.Reactive.UI.Tests/Generator/Given_VMWithCommands.cs +++ b/src/Uno.Extensions.Reactive.UI.Tests/Generator/Given_VMWithCommands.cs @@ -32,12 +32,12 @@ public async Task When_ParameterFeed_Then_SubscribedWithSameContext() { var vm = new BindableWhen_ParameterFeed_Then_SubscribedWithSameContext_ViewModel(); - await UIHelper.WaitFor(() => vm.MyFeed != 0, default); + await TestHelper.WaitFor(() => vm.MyFeed != 0, default); var current = vm.MyFeed; vm.DoSomething.Execute(null); - await UIHelper.WaitFor(() => vm.Model.CommandLastParameter != -1, default); + await TestHelper.WaitFor(() => vm.Model.CommandLastParameter != -1, default); Assert.AreEqual(1, vm.FeedInvokeCount); Assert.AreEqual(current, vm.Model.CommandLastParameter); @@ -64,12 +64,12 @@ public async Task When_ParameterFeed_Then_SubscribedWithSameContext_UsingUI() await UIHelper.Load(ui, default); - await UIHelper.WaitFor(() => vm.MyFeed is not 0, default); + await TestHelper.WaitFor(() => vm.MyFeed is not 0, default); var current = vm.MyFeed; doSomething.Command.Execute(null); - await UIHelper.WaitFor(() => vm.Model.CommandLastParameter != -1, default); + await TestHelper.WaitFor(() => vm.Model.CommandLastParameter != -1, default); Assert.AreEqual(1, vm.FeedInvokeCount); Assert.AreEqual(current, vm.Model.CommandLastParameter); diff --git a/src/Uno.Extensions.Reactive.UI.Tests/Given_FeedView.cs b/src/Uno.Extensions.Reactive.UI.Tests/Given_FeedView.cs index ae0cdc35d9..33eed460a5 100644 --- a/src/Uno.Extensions.Reactive.UI.Tests/Given_FeedView.cs +++ b/src/Uno.Extensions.Reactive.UI.Tests/Given_FeedView.cs @@ -39,7 +39,7 @@ public async Task When_Loading() tcs.SetResult(42); - await UIHelper.WaitFor(() => isLoadingValues.Count > 0, CT); + await TestHelper.WaitFor(() => isLoadingValues.Count > 0, CT); } [TestMethod] @@ -58,7 +58,7 @@ public async Task When_NotVisible_Then_DoesNotSubscribeToSource() root.Visibility = Visibility.Visible; - await UIHelper.WaitFor(() => isLoaded, CT); + await TestHelper.WaitFor(() => isLoaded, CT); isLoaded.Should().BeTrue("The FeedView should have subscribed to the source when it became visible."); } diff --git a/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Edition.cs b/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Edition.cs index 8e2174fc35..404161f804 100644 --- a/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Edition.cs +++ b/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Edition.cs @@ -53,7 +53,7 @@ await UIHelper.WaitFor(async ct => var lvItems = Array.Empty(); await UIHelper.WaitFor(async ct => { - lvItems = UIHelper.FindChildren(lv).ToArray(); + lvItems = UIHelper.GetChildren(lv).ToArray(); return lvItems.Length > 0; }, CT); diff --git a/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Selection.cs b/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Selection.cs index c7702da257..c9dfbbb664 100644 --- a/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Selection.cs +++ b/src/Uno.Extensions.Reactive.UI.Tests/Presentation/Bindings/Collections/Given_BindableCollection_Selection.cs @@ -40,7 +40,7 @@ public async Task When_SelectSingleFromView_ListView() InputInjectorHelper.Current.Tap(items[1]); - await UIHelper.WaitFor(async ct => (await vm.Items.GetSelectedItem(ct))?.Value == 42, CT); + await TestHelper.WaitFor(async ct => (await vm.Items.GetSelectedItem(ct))?.Value == 42, CT); } [TestMethod] @@ -55,7 +55,7 @@ public async Task When_SelectMultipleFromView_ListView() InputInjectorHelper.Current.Tap(items[0]); InputInjectorHelper.Current.Tap(items[1]); - await UIHelper.WaitFor(async ct => + await TestHelper.WaitFor(async ct => { return (await vm.Items.GetSelectedItems(ct)).SequenceEqual(new MyItem[] { new(41), new(42) }); }, CT); @@ -72,7 +72,7 @@ public async Task When_EditSingleSelectedItem_Then_SelectionPreserved() InputInjectorHelper.Current.Tap(items[1]); - await UIHelper.WaitFor(async ct => + await TestHelper.WaitFor(async ct => { // Selection is preserved on ... return lv.SelectedItem is MyItem { Value: 42 } // ... the ListView ... @@ -80,9 +80,9 @@ await UIHelper.WaitFor(async ct => && await vm.Items.GetSelectedItem(ct) is { Value: 42 }; // ... and the Feed! }, CT); - await vm.Model.Items.Update(items => items.Replace(items[1], items[1] with { Version = 2 }), CT); + await vm.Model.Items.UpdateAsync(items => items.Replace(items[1], items[1] with { Version = 2 }), CT); - await UIHelper.WaitFor(async ct => + await TestHelper.WaitFor(async ct => { // Selection is preserved on ... return lv.SelectedItem is MyItem { Value: 42, Version: 2 } // ... the ListView ... @@ -99,9 +99,9 @@ await UIHelper.WaitFor(async ct => await UIHelper.Load(lv, CT); var lvItems = Array.Empty(); - await UIHelper.WaitFor(async ct => + await TestHelper.WaitFor(async ct => { - lvItems = UIHelper.FindChildren(lv).ToArray(); + lvItems = UIHelper.GetChildren(lv).ToArray(); return lvItems.Length > 0; }, CT); diff --git a/src/Uno.Extensions.RuntimeTests.Core/UIHelper.cs b/src/Uno.Extensions.RuntimeTests.Core/UIHelper.cs index 7d601a6c43..249e79de97 100644 --- a/src/Uno.Extensions.RuntimeTests.Core/UIHelper.cs +++ b/src/Uno.Extensions.RuntimeTests.Core/UIHelper.cs @@ -9,71 +9,10 @@ namespace Uno.UI.RuntimeTests; -public static class UIHelper +public static partial class UIHelper { public static TimeSpan DefaultTimeout => Debugger.IsAttached ? TimeSpan.FromMinutes(60) : TimeSpan.FromSeconds(1); - public static UIElement? Content - { - get => UnitTestsUIContentHelper.Content; - set => UnitTestsUIContentHelper.Content = value; - } - - public static async Task Load(FrameworkElement element, CancellationToken ct) - { - Content = element; - await WaitForLoaded(element, ct); - } - - public static IEnumerable FindChildren(DependencyObject element) - { - if (element is T t) - { - yield return t; - } - - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) - { - foreach (var child in FindChildren(VisualTreeHelper.GetChild(element, i))) - { - yield return child; - } - } - } - - public static async Task WaitForLoaded(FrameworkElement element, CancellationToken ct) - { - if (element.IsLoaded) - { - return; - } - - var tcs = new TaskCompletionSource(); - using var _ = ct.CanBeCanceled ? ct.Register(() => tcs.TrySetCanceled()) : default; - try - { - element.Loaded += OnElementLoaded; - - if (!element.IsLoaded) - { - var timeout = Task.Delay(DefaultTimeout, ct); - if (await Task.WhenAny(tcs.Task, timeout) == timeout) - { - throw new TimeoutException($"Failed to load element within {DefaultTimeout}."); - } - } - } - finally - { - element.Loaded -= OnElementLoaded; - } - - void OnElementLoaded(object sender, RoutedEventArgs e) - { - element.Loaded -= OnElementLoaded; - tcs.TrySetResult(default); - } - } public static async ValueTask WaitFor(Func predicate, CancellationToken ct) => await WaitFor(async _ => predicate(), ct); diff --git a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml b/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml index c0bedd8ac8..2f24efc49c 100644 --- a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml +++ b/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml @@ -1,9 +1,8 @@ - - - - + + + diff --git a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml.cs b/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml.cs index 0acab52678..b9244923e7 100644 --- a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml.cs +++ b/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/App.xaml.cs @@ -1,17 +1,14 @@ using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; using System.Linq; -using System.Threading.Tasks; using System.Windows; +using Uno.UI.Runtime.Skia.Wpf; -namespace RuntimeTests.WPF +namespace RuntimeTests.WPF; + +public partial class App : Application { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application + public App() { + new WpfHost(Dispatcher, () => new Uno.Extensions.RuntimeTests.App()).Run(); } } diff --git a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/MainWindow.xaml b/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/MainWindow.xaml deleted file mode 100644 index 4b011db7e7..0000000000 --- a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/MainWindow.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/MainWindow.xaml.cs b/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/MainWindow.xaml.cs deleted file mode 100644 index 86616cd255..0000000000 --- a/src/Uno.Extensions.RuntimeTests/Uno.Extensions.RuntimeTests.Skia.Wpf/MainWindow.xaml.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace RuntimeTests.WPF -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - - root.Content = new global::Uno.UI.Runtime.Skia.Wpf.WpfHost(Dispatcher, () => new Uno.Extensions.RuntimeTests.App()); - } - } -}