diff --git a/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj b/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj index db8a9ba48..6d0b92865 100644 --- a/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj +++ b/OpenTabletDriver.Analyzers/OpenTabletDriver.Analyzers.csproj @@ -16,6 +16,7 @@ + @@ -25,7 +26,7 @@ - + diff --git a/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj b/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj index ea28e8e31..5b4cda0bd 100644 --- a/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj +++ b/OpenTabletDriver.Configurations/OpenTabletDriver.Configurations.csproj @@ -14,7 +14,7 @@ - + diff --git a/OpenTabletDriver.Tests/ConfigurationTest.cs b/OpenTabletDriver.Tests/ConfigurationTest.cs index 2331816c7..c110e2037 100644 --- a/OpenTabletDriver.Tests/ConfigurationTest.cs +++ b/OpenTabletDriver.Tests/ConfigurationTest.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Schema.Generation; using OpenTabletDriver.Components; using OpenTabletDriver.Tablet; using Xunit; @@ -270,6 +274,46 @@ static void AssertGroup(List identificationContexts, Iden } } + private static readonly string ConfigurationProjectDir = Path.GetFullPath(Path.Join("../../../..", "OpenTabletDriver.Configurations")); + private static readonly string ConfigurationDir = Path.Join(ConfigurationProjectDir, "Configurations"); + private static readonly IEnumerable<(string, string)> ConfigFiles = Directory.EnumerateFiles(ConfigurationDir, "*.json", SearchOption.AllDirectories) + .Select(f => (Path.GetRelativePath(ConfigurationDir, f), File.ReadAllText(f))); + + [Fact] + public void Configurations_Verify_Configs_With_Schema() + { + var gen = new JSchemaGenerator(); + var schema = gen.Generate(typeof(TabletConfiguration)); + DisallowAdditionalItemsAndProperties(schema); + + var failed = false; + + foreach (var (tabletFilename, tabletConfigString) in ConfigFiles) + { + var tabletConfig = JObject.Parse(tabletConfigString); + if (tabletConfig.IsValid(schema, out IList errors)) continue; + + _testOutputHelper.WriteLine($"Tablet Configuration {tabletFilename} did not match schema:\r\n{string.Join("\r\n", errors)}\r\n"); + failed = true; + } + + Assert.False(failed); + } + + private static void DisallowAdditionalItemsAndProperties(JSchema schema) + { + schema.AllowAdditionalItems = false; + schema.AllowAdditionalProperties = false; + schema.AllowUnevaluatedItems = false; + schema.AllowUnevaluatedProperties = false; + + foreach (var child in schema.Properties) + { + if (child.Key == nameof(TabletConfiguration.Attributes)) continue; + DisallowAdditionalItemsAndProperties(child.Value); + } + } + private static void AssertInequal(IdentificationContext a, IdentificationContext b) { if (IsEqual(a.Identifier, b.Identifier)) diff --git a/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj b/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj index 43dce9a8e..cbd82ee73 100644 --- a/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj +++ b/OpenTabletDriver.Tests/OpenTabletDriver.Tests.csproj @@ -9,6 +9,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/OpenTabletDriver/OpenTabletDriver.csproj b/OpenTabletDriver/OpenTabletDriver.csproj index 7b25fcb96..f6e302238 100644 --- a/OpenTabletDriver/OpenTabletDriver.csproj +++ b/OpenTabletDriver/OpenTabletDriver.csproj @@ -16,7 +16,7 @@ - + diff --git a/OpenTabletDriver/Tablet/ButtonSpecifications.cs b/OpenTabletDriver/Tablet/ButtonSpecifications.cs index 43aa6440a..d33711d8c 100644 --- a/OpenTabletDriver/Tablet/ButtonSpecifications.cs +++ b/OpenTabletDriver/Tablet/ButtonSpecifications.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace OpenTabletDriver.Tablet @@ -13,6 +14,7 @@ public class ButtonSpecifications /// The amount of buttons. /// [DisplayName("Buttons")] + [Required(ErrorMessage = $"{nameof(ButtonCount)} must be defined")] public uint ButtonCount { set; get; } } } diff --git a/OpenTabletDriver/Tablet/DeviceIdentifier.cs b/OpenTabletDriver/Tablet/DeviceIdentifier.cs index f16e53c07..0f8345f49 100644 --- a/OpenTabletDriver/Tablet/DeviceIdentifier.cs +++ b/OpenTabletDriver/Tablet/DeviceIdentifier.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace OpenTabletDriver.Tablet @@ -14,12 +15,16 @@ public class DeviceIdentifier /// The Vendor ID of the device. /// [DisplayName("Vendor ID")] + [Required(ErrorMessage = $"{nameof(VendorID)} identifier must be defined")] + [Range(0, 0xFFFF)] public int VendorID { set; get; } /// /// The Product ID of the device. /// [DisplayName("Product ID")] + [Required(ErrorMessage = $"{nameof(ProductID)} identifier must be defined")] + [Range(0, 0xFFFF)] public int ProductID { set; get; } /// @@ -38,6 +43,7 @@ public class DeviceIdentifier /// The device report parser used by the detected device. /// [DisplayName("Report Parser")] + [RegularExpression(@"^OpenTabletDriver(\.[\w\d]+)+$", ErrorMessage = $"{nameof(ReportParser)} for identifier must match regular expression")] public string ReportParser { set; get; } = string.Empty; /// diff --git a/OpenTabletDriver/Tablet/DigitizerSpecifications.cs b/OpenTabletDriver/Tablet/DigitizerSpecifications.cs index 7683919dc..74ddc6146 100644 --- a/OpenTabletDriver/Tablet/DigitizerSpecifications.cs +++ b/OpenTabletDriver/Tablet/DigitizerSpecifications.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace OpenTabletDriver.Tablet @@ -12,22 +13,26 @@ public class DigitizerSpecifications /// /// The width of the digitizer in millimeters. /// + [Required(ErrorMessage = $"Digitizer ${nameof(Width)} must be defined")] public float Width { set; get; } /// /// The height of the digitizer in millimeters. /// + [Required(ErrorMessage = $"Digitizer ${nameof(Height)} must be defined")] public float Height { set; get; } /// /// The maximum X coordinate for the digitizer. /// + [Required(ErrorMessage = $"Digitizer ${nameof(MaxX)} must be defined")] [DisplayName("Max X")] public float MaxX { set; get; } /// /// The maximum Y coordinate for the digitizer. /// + [Required(ErrorMessage = $"Digitizer ${nameof(MaxY)} must be defined")] [DisplayName("Max Y")] public float MaxY { set; get; } } diff --git a/OpenTabletDriver/Tablet/PenSpecifications.cs b/OpenTabletDriver/Tablet/PenSpecifications.cs index 33979f548..cef7684bf 100644 --- a/OpenTabletDriver/Tablet/PenSpecifications.cs +++ b/OpenTabletDriver/Tablet/PenSpecifications.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace OpenTabletDriver.Tablet @@ -13,6 +14,7 @@ public class PenSpecifications : ButtonSpecifications /// The maximum pressure that the pen supports. /// [DisplayName("Max Pressure")] + [Required(ErrorMessage = $"Pen {nameof(MaxPressure)} must be defined")] public uint MaxPressure { set; get; } } } diff --git a/OpenTabletDriver/Tablet/TabletConfiguration.cs b/OpenTabletDriver/Tablet/TabletConfiguration.cs index 917c8a6cf..22006b627 100644 --- a/OpenTabletDriver/Tablet/TabletConfiguration.cs +++ b/OpenTabletDriver/Tablet/TabletConfiguration.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace OpenTabletDriver.Tablet @@ -13,28 +14,34 @@ public class TabletConfiguration /// /// The tablet's name. /// + [Required(ErrorMessage = $"Tablet {nameof(Name)} is required")] public string Name { set; get; } = string.Empty; /// /// The tablet's specifications. /// + [Required(ErrorMessage = $"Tablet {nameof(Specifications)} is required")] public TabletSpecifications Specifications { set; get; } = new TabletSpecifications(); /// /// The digitizer device identifier. /// + [Required(ErrorMessage = $"Tablet {nameof(DigitizerIdentifiers)} are required")] [DisplayName("Digitizer Identifiers")] + [MinLength(1, ErrorMessage = "Requires at least 1 identifier")] public List DigitizerIdentifiers { set; get; } = new List(); /// /// The auxiliary device identifier. /// + [Required(ErrorMessage = $"Tablet {nameof(AuxiliaryDeviceIdentifiers)} must be present")] [DisplayName("Auxiliary Identifiers")] public List AuxiliaryDeviceIdentifiers { set; get; } = new List(); /// /// Other information about the tablet that can be used in tools or other applications. /// + [Required(ErrorMessage = $"Tablet {nameof(Attributes)} must be present")] public Dictionary Attributes { set; get; } = new Dictionary(); } } diff --git a/OpenTabletDriver/Tablet/TabletSpecifications.cs b/OpenTabletDriver/Tablet/TabletSpecifications.cs index 020f240ce..5f5a93084 100644 --- a/OpenTabletDriver/Tablet/TabletSpecifications.cs +++ b/OpenTabletDriver/Tablet/TabletSpecifications.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace OpenTabletDriver.Tablet @@ -12,11 +13,13 @@ public class TabletSpecifications /// /// Specifications for the tablet digitizer. /// + [Required(ErrorMessage = $"{nameof(Digitizer)} specifications must be defined")] public DigitizerSpecifications? Digitizer { set; get; } /// /// Specifications for the tablet's pen. /// + [Required(ErrorMessage = $"{nameof(Pen)} specifications must be defined")] public PenSpecifications? Pen { set; get; } ///