Skip to content

Commit 454ece9

Browse files
authored
Merge pull request #21976 from unoplatform/copilot/add-xaml-error-test
fix: Generate UXAML0001 error for invalid property values with valid C# fallback
2 parents 41d7a62 + b616c48 commit 454ece9

17 files changed

+1069
-21
lines changed

src/SourceGenerators/Uno.UI.SourceGenerators.Tests/XamlCodeGeneratorTests/Given_Parser.cs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,155 @@ public async Task When_Invalid_Object_Property()
307307
await test.RunAsync();
308308
}
309309

310+
[TestMethod]
311+
public async Task When_Invalid_Margin_Value()
312+
{
313+
var xamlFiles = new[]
314+
{
315+
new XamlFile(
316+
"MainPage.xaml",
317+
"""
318+
<Page x:Class="TestRepro.MainPage"
319+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
320+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
321+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
322+
323+
<Grid Margin="auto,0,0,0">
324+
</Grid>
325+
</Page>
326+
"""),
327+
};
328+
329+
var test = new Verify.Test(xamlFiles) { TestState = { Sources = { _emptyCodeBehind } } }.AddGeneratedSources();
330+
331+
test.ExpectedDiagnostics.AddRange([
332+
DiagnosticResult.CompilerError("UXAML0001").WithSpan("C:/Project/0/MainPage.xaml", 6, 3, 6, 3).WithArguments("Invalid Thickness value 'auto,0,0,0'. Each component must be a valid number"),
333+
// ==> When XAML is invalid, we still generate the class structure, so we should not miss InitializeComponent.
334+
]);
335+
336+
await test.RunAsync();
337+
}
338+
339+
[TestMethod]
340+
public async Task When_Invalid_CornerRadius_Value()
341+
{
342+
var xamlFiles = new[]
343+
{
344+
new XamlFile(
345+
"MainPage.xaml",
346+
"""
347+
<Page x:Class="TestRepro.MainPage"
348+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
349+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
350+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
351+
352+
<Border CornerRadius="auto,0,0,0">
353+
</Border>
354+
</Page>
355+
"""),
356+
};
357+
358+
var test = new Verify.Test(xamlFiles) { TestState = { Sources = { _emptyCodeBehind } } }.AddGeneratedSources();
359+
360+
test.ExpectedDiagnostics.AddRange([
361+
DiagnosticResult.CompilerError("UXAML0001").WithSpan("C:/Project/0/MainPage.xaml", 6, 3, 6, 3).WithArguments("Invalid CornerRadius value 'auto,0,0,0'. Each component must be a valid number"),
362+
// ==> When XAML is invalid, we still generate the class structure, so we should not miss InitializeComponent.
363+
]);
364+
365+
await test.RunAsync();
366+
}
367+
368+
[TestMethod]
369+
public async Task When_Invalid_GridLength_Value()
370+
{
371+
var xamlFiles = new[]
372+
{
373+
new XamlFile(
374+
"MainPage.xaml",
375+
"""
376+
<Page x:Class="TestRepro.MainPage"
377+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
378+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
379+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
380+
381+
<Grid>
382+
<Grid.RowDefinitions>
383+
<RowDefinition Height="invalid" />
384+
</Grid.RowDefinitions>
385+
</Grid>
386+
</Page>
387+
"""),
388+
};
389+
390+
var test = new Verify.Test(xamlFiles) { TestState = { Sources = { _emptyCodeBehind } } }.AddGeneratedSources();
391+
392+
test.ExpectedDiagnostics.AddRange([
393+
DiagnosticResult.CompilerError("UXAML0001").WithSpan("C:/Project/0/MainPage.xaml", 8, 5, 8, 5).WithArguments("Invalid GridLength value 'invalid', expected a number (e.g., '100'), 'Auto', or a star value (e.g., '2*')"),
394+
// ==> When XAML is invalid, we still generate the class structure, so we should not miss InitializeComponent.
395+
]);
396+
397+
await test.RunAsync();
398+
}
399+
400+
[TestMethod]
401+
public async Task When_Invalid_Double_Value()
402+
{
403+
var xamlFiles = new[]
404+
{
405+
new XamlFile(
406+
"MainPage.xaml",
407+
"""
408+
<Page x:Class="TestRepro.MainPage"
409+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
410+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
411+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
412+
413+
<Grid Width="invalid">
414+
</Grid>
415+
</Page>
416+
"""),
417+
};
418+
419+
var test = new Verify.Test(xamlFiles) { TestState = { Sources = { _emptyCodeBehind } } }.AddGeneratedSources();
420+
421+
test.ExpectedDiagnostics.AddRange([
422+
DiagnosticResult.CompilerError("UXAML0001").WithSpan("C:/Project/0/MainPage.xaml", 6, 3, 6, 3).WithArguments("Invalid Single value 'invalid'"),
423+
// ==> When XAML is invalid, we still generate the class structure, so we should not miss InitializeComponent.
424+
]);
425+
426+
await test.RunAsync();
427+
}
428+
429+
[TestMethod]
430+
public async Task When_Invalid_Point_Value()
431+
{
432+
var xamlFiles = new[]
433+
{
434+
new XamlFile(
435+
"MainPage.xaml",
436+
"""
437+
<Page x:Class="TestRepro.MainPage"
438+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
439+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
440+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
441+
442+
<Grid>
443+
<Grid.RenderTransformOrigin>auto,0</Grid.RenderTransformOrigin>
444+
</Grid>
445+
</Page>
446+
"""),
447+
};
448+
449+
var test = new Verify.Test(xamlFiles) { TestState = { Sources = { _emptyCodeBehind } } }.AddGeneratedSources();
450+
451+
test.ExpectedDiagnostics.AddRange([
452+
DiagnosticResult.CompilerError("UXAML0001").WithSpan("C:/Project/0/MainPage.xaml", 7, 31, 7, 31).WithArguments("Invalid Point value 'auto,0'. Each component must be a valid number"),
453+
// ==> When XAML is invalid, we still generate the class structure, so we should not miss InitializeComponent.
454+
]);
455+
456+
await test.RunAsync();
457+
}
458+
310459
[TestMethod]
311460
public async Task When_Multiple_With_Invalid()
312461
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// <autogenerated />
2+
namespace MyProject
3+
{
4+
/// <summary>
5+
/// Contains all the static resources defined for the application
6+
/// </summary>
7+
public sealed partial class GlobalStaticResources
8+
{
9+
static bool _initialized;
10+
private static bool _stylesRegistered;
11+
private static bool _dictionariesRegistered;
12+
internal static global::Uno.UI.Xaml.XamlParseContext __ParseContext_ { get; } = new global::Uno.UI.Xaml.XamlParseContext()
13+
{
14+
AssemblyName = "TestProject",
15+
}
16+
;
17+
18+
static GlobalStaticResources()
19+
{
20+
Initialize();
21+
}
22+
public static void Initialize()
23+
{
24+
if (!_initialized)
25+
{
26+
_initialized = true;
27+
global::Uno.UI.GlobalStaticResources.Initialize();
28+
global::Uno.UI.GlobalStaticResources.RegisterDefaultStyles();
29+
global::Uno.UI.GlobalStaticResources.RegisterResourceDictionariesBySource();
30+
}
31+
}
32+
public static void RegisterDefaultStyles()
33+
{
34+
if(!_stylesRegistered)
35+
{
36+
_stylesRegistered = true;
37+
RegisterDefaultStyles_MainPage_d6cd66944958ced0c513e0a04797b51d();
38+
}
39+
}
40+
// Register ResourceDictionaries using ms-appx:/// syntax, this is called for external resources
41+
public static void RegisterResourceDictionariesBySource()
42+
{
43+
if(!_dictionariesRegistered)
44+
{
45+
_dictionariesRegistered = true;
46+
}
47+
}
48+
// Register ResourceDictionaries using ms-resource:/// syntax, this is called for local resources
49+
internal static void RegisterResourceDictionariesBySourceLocal()
50+
{
51+
}
52+
static partial void RegisterDefaultStyles_MainPage_d6cd66944958ced0c513e0a04797b51d();
53+
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// <auto-generated />
2+
[assembly: global::System.Reflection.AssemblyMetadata("UnoHasLocalizationResources", "False")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// <autogenerated />
2+
#pragma warning disable CS0114
3+
#pragma warning disable CS0108
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Linq;
8+
using Uno.UI;
9+
using Uno.UI.Xaml;
10+
using Microsoft.UI.Xaml;
11+
using Microsoft.UI.Xaml.Controls;
12+
using Microsoft.UI.Xaml.Controls.Primitives;
13+
using Microsoft.UI.Xaml.Data;
14+
using Microsoft.UI.Xaml.Documents;
15+
using Microsoft.UI.Xaml.Media;
16+
using Microsoft.UI.Xaml.Media.Animation;
17+
using Microsoft.UI.Xaml.Shapes;
18+
using Windows.UI.Text;
19+
using Uno.Extensions;
20+
using Uno;
21+
using Uno.UI.Helpers;
22+
using Uno.UI.Helpers.Xaml;
23+
using MyProject;
24+
25+
#if HAS_UNO_SKIA
26+
using _View = Microsoft.UI.Xaml.UIElement;
27+
#elif __ANDROID__
28+
using _View = Android.Views.View;
29+
#elif __APPLE_UIKIT__ || __IOS__ || __TVOS__
30+
using _View = UIKit.UIView;
31+
#else
32+
using _View = Microsoft.UI.Xaml.UIElement;
33+
#endif
34+
35+
namespace TestRepro
36+
{
37+
partial class MainPage : global::Microsoft.UI.Xaml.Controls.Page
38+
{
39+
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
40+
private const string __baseUri_prefix_MainPage_d6cd66944958ced0c513e0a04797b51d = "ms-appx:///TestProject/";
41+
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
42+
private const string __baseUri_MainPage_d6cd66944958ced0c513e0a04797b51d = "ms-appx:///TestProject/";
43+
private global::Microsoft.UI.Xaml.NameScope __nameScope = new global::Microsoft.UI.Xaml.NameScope();
44+
[global::System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Generated code")]
45+
[global::System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Generated code")]
46+
private void InitializeComponent()
47+
{
48+
NameScope.SetNameScope(this, __nameScope);
49+
var __that = this;
50+
base.IsParsing = true;
51+
// Source 0\MainPage.xaml (Line 1:2)
52+
base.Content =
53+
new global::Microsoft.UI.Xaml.Controls.Border
54+
{
55+
IsParsing = true,
56+
CornerRadius = new Microsoft.UI.Xaml.CornerRadius(0),
57+
// Source 0\MainPage.xaml (Line 6:3)
58+
}
59+
.MainPage_d6cd66944958ced0c513e0a04797b51d_XamlApply((MainPage_d6cd66944958ced0c513e0a04797b51dXamlApplyExtensions.XamlApplyHandler0)(__p1 =>
60+
{
61+
global::Uno.UI.FrameworkElementHelper.SetBaseUri(__p1, __baseUri_MainPage_d6cd66944958ced0c513e0a04797b51d);
62+
__p1.CreationComplete();
63+
}
64+
))
65+
;
66+
67+
this
68+
.MainPage_d6cd66944958ced0c513e0a04797b51d_XamlApply((MainPage_d6cd66944958ced0c513e0a04797b51dXamlApplyExtensions.XamlApplyHandler1)(__p1 =>
69+
{
70+
// Source 0\MainPage.xaml (Line 1:2)
71+
72+
// [WARNING] C:/Project/0/MainPage.xaml(1,2): Property 'base' does not exist on 'Page', this error was however considered irrelevant by the XamlFileGenerator.
73+
}
74+
))
75+
.MainPage_d6cd66944958ced0c513e0a04797b51d_XamlApply((MainPage_d6cd66944958ced0c513e0a04797b51dXamlApplyExtensions.XamlApplyHandler1)(__p1 =>
76+
{
77+
// Class TestRepro.MainPage
78+
global::Uno.UI.FrameworkElementHelper.SetBaseUri(__p1, __baseUri_MainPage_d6cd66944958ced0c513e0a04797b51d);
79+
__p1.CreationComplete();
80+
}
81+
))
82+
;
83+
OnInitializeCompleted();
84+
85+
}
86+
partial void OnInitializeCompleted();
87+
}
88+
}
89+
namespace MyProject
90+
{
91+
static class MainPage_d6cd66944958ced0c513e0a04797b51dXamlApplyExtensions
92+
{
93+
public delegate void XamlApplyHandler0(global::Microsoft.UI.Xaml.Controls.Border instance);
94+
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
95+
public static global::Microsoft.UI.Xaml.Controls.Border MainPage_d6cd66944958ced0c513e0a04797b51d_XamlApply(this global::Microsoft.UI.Xaml.Controls.Border instance, XamlApplyHandler0 handler)
96+
{
97+
handler(instance);
98+
return instance;
99+
}
100+
public delegate void XamlApplyHandler1(global::Microsoft.UI.Xaml.Controls.Page instance);
101+
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
102+
public static global::Microsoft.UI.Xaml.Controls.Page MainPage_d6cd66944958ced0c513e0a04797b51d_XamlApply(this global::Microsoft.UI.Xaml.Controls.Page instance, XamlApplyHandler1 handler)
103+
{
104+
handler(instance);
105+
return instance;
106+
}
107+
}
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// <autogenerated />
2+
namespace MyProject
3+
{
4+
/// <summary>
5+
/// Contains all the static resources defined for the application
6+
/// </summary>
7+
public sealed partial class GlobalStaticResources
8+
{
9+
static bool _initialized;
10+
private static bool _stylesRegistered;
11+
private static bool _dictionariesRegistered;
12+
internal static global::Uno.UI.Xaml.XamlParseContext __ParseContext_ { get; } = new global::Uno.UI.Xaml.XamlParseContext()
13+
{
14+
AssemblyName = "TestProject",
15+
}
16+
;
17+
18+
static GlobalStaticResources()
19+
{
20+
Initialize();
21+
}
22+
public static void Initialize()
23+
{
24+
if (!_initialized)
25+
{
26+
_initialized = true;
27+
global::Uno.UI.GlobalStaticResources.Initialize();
28+
global::Uno.UI.GlobalStaticResources.RegisterDefaultStyles();
29+
global::Uno.UI.GlobalStaticResources.RegisterResourceDictionariesBySource();
30+
}
31+
}
32+
public static void RegisterDefaultStyles()
33+
{
34+
if(!_stylesRegistered)
35+
{
36+
_stylesRegistered = true;
37+
RegisterDefaultStyles_MainPage_d6cd66944958ced0c513e0a04797b51d();
38+
}
39+
}
40+
// Register ResourceDictionaries using ms-appx:/// syntax, this is called for external resources
41+
public static void RegisterResourceDictionariesBySource()
42+
{
43+
if(!_dictionariesRegistered)
44+
{
45+
_dictionariesRegistered = true;
46+
}
47+
}
48+
// Register ResourceDictionaries using ms-resource:/// syntax, this is called for local resources
49+
internal static void RegisterResourceDictionariesBySourceLocal()
50+
{
51+
}
52+
static partial void RegisterDefaultStyles_MainPage_d6cd66944958ced0c513e0a04797b51d();
53+
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// <auto-generated />
2+
[assembly: global::System.Reflection.AssemblyMetadata("UnoHasLocalizationResources", "False")]

0 commit comments

Comments
 (0)