diff --git a/src/PAModel/PAConvert/Parser/SourceLocation.cs b/src/PAModel/PAConvert/Parser/SourceLocation.cs index 0d226d72..9d399ea3 100644 --- a/src/PAModel/PAConvert/Parser/SourceLocation.cs +++ b/src/PAModel/PAConvert/Parser/SourceLocation.cs @@ -4,70 +4,69 @@ using System.Collections.Generic; using System.Linq; -namespace Microsoft.PowerPlatform.Formulas.Tools.IR +namespace Microsoft.PowerPlatform.Formulas.Tools.IR; + +internal struct SourceLocation { - internal struct SourceLocation + public readonly int StartLine; + public readonly int StartChar; + public readonly int EndLine; + public readonly int EndChar; + public readonly string FileName; + + // Indices into file are 1-based. + public SourceLocation(int startLine, int startChar, int endLine, int endChar, string fileName) { - public readonly int StartLine; - public readonly int StartChar; - public readonly int EndLine; - public readonly int EndChar; - public readonly string FileName; + StartLine = startLine; + StartChar = startChar; + EndLine = endLine; + EndChar = endChar; + FileName = fileName; + } - // Indices into file are 1-based. - public SourceLocation(int startLine, int startChar, int endLine, int endChar, string fileName) - { - StartLine = startLine; - StartChar = startChar; - EndLine = endLine; - EndChar = endChar; - FileName = fileName; - } + public static SourceLocation FromFile(string filename) + { + return new SourceLocation(0, 0, 0, 0, filename); + } - public static SourceLocation FromFile(string filename) - { - return new SourceLocation(0, 0, 0, 0, filename); - } + public override string ToString() + { + return $"{FileName}:{StartLine},{StartChar}-{EndLine},{EndChar}"; + } - public override string ToString() - { - return $"{FileName}:{StartLine},{StartChar}-{EndLine},{EndChar}"; - } + public static SourceLocation FromChildren(List<SourceLocation> locations) + { + SourceLocation minLoc = locations.First(), maxLoc = locations.First(); - public static SourceLocation FromChildren(List<SourceLocation> locations) + foreach (var loc in locations) { - SourceLocation minLoc = locations.First(), maxLoc = locations.First(); - - foreach (var loc in locations) + if (loc.StartLine < minLoc.StartLine || + (loc.StartLine == minLoc.StartLine && loc.StartChar < minLoc.StartChar)) { - if (loc.StartLine < minLoc.StartLine || - (loc.StartLine == minLoc.StartLine && loc.StartChar < minLoc.StartChar)) - { - minLoc = loc; - } - if (loc.EndLine > maxLoc.EndLine || - (loc.EndLine == minLoc.EndLine && loc.EndChar > minLoc.EndChar)) - { - maxLoc = loc; - } + minLoc = loc; + } + if (loc.EndLine > maxLoc.EndLine || + (loc.EndLine == minLoc.EndLine && loc.EndChar > minLoc.EndChar)) + { + maxLoc = loc; } - - return new SourceLocation(minLoc.StartLine, minLoc.StartChar, maxLoc.EndLine, maxLoc.EndChar, maxLoc.FileName); } - public override bool Equals(object obj) - { - return obj is SourceLocation other && - other.FileName == FileName && - other.StartChar == StartChar && - other.StartLine == StartLine && - other.EndChar == EndChar && - other.EndLine == EndLine; - } + return new SourceLocation(minLoc.StartLine, minLoc.StartChar, maxLoc.EndLine, maxLoc.EndChar, maxLoc.FileName); + } - public override int GetHashCode() - { - return (FileName, StartChar, EndChar, StartLine, EndLine).GetHashCode(); - } + public override bool Equals(object obj) + { + return obj is SourceLocation other && + other.FileName == FileName && + other.StartChar == StartChar && + other.StartLine == StartLine && + other.EndChar == EndChar && + other.EndLine == EndLine; + } + + public override int GetHashCode() + { + return (FileName, StartChar, EndChar, StartLine, EndLine).GetHashCode(); } } diff --git a/src/PAModelTests/AppTestsTest.cs b/src/PAModelTests/AppTestsTest.cs index beb89347..7174f4c9 100644 --- a/src/PAModelTests/AppTestsTest.cs +++ b/src/PAModelTests/AppTestsTest.cs @@ -2,92 +2,87 @@ // Licensed under the MIT License. using Microsoft.PowerPlatform.Formulas.Tools; -using Microsoft.PowerPlatform.Formulas.Tools.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -using System.IO.Compression; -using System.Linq; -namespace PAModelTests +namespace PAModelTests; + +// DataSources Tests +[TestClass] +public class AppTestsTest { - // DataSources Tests - [TestClass] - public class AppTestsTest + // Validates that the App can be repacked after deleting the EditorState files, when the app contains app tests which refer to screens. + [DataTestMethod] + [DataRow("TestStudio_Test.msapp")] + public void TestPackWhenEditorStateIsDeleted(string appName) { - // Validates that the App can be repacked after deleting the EditorState files, when the app contains app tests which refer to screens. - [DataTestMethod] - [DataRow("TestStudio_Test.msapp")] - public void TestPackWhenEditorStateIsDeleted(string appName) + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; + + // Save to sources + msapp.SaveToSources(outSrcDir); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Delete Entropy directory + var editorStatePath = Path.Combine(outSrcDir, "Src", "EditorState"); + if (Directory.Exists(editorStatePath)) + { + Directory.Delete(editorStatePath, true); + } - using (var tempDir = new TempDir()) + // Load app from the sources after deleting the entropy + var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); + + using (var tempFile = new TempFile()) { - string outSrcDir = tempDir.Dir; - - // Save to sources - msapp.SaveToSources(outSrcDir); - - // Delete Entropy directory - var editorStatePath = Path.Combine(outSrcDir, "Src", "EditorState"); - if (Directory.Exists(editorStatePath)) - { - Directory.Delete(editorStatePath, true); - } - - // Load app from the sources after deleting the entropy - var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); - - using (var tempFile = new TempFile()) - { - // Repack the app - MsAppSerializer.SaveAsMsApp(app, tempFile.FullPath, new ErrorContainer()); - } + // Repack the app + MsAppSerializer.SaveAsMsApp(app, tempFile.FullPath, new ErrorContainer()); } } + } - // Validates that the App can be repacked after deleting the Entropy files, when the app contains app tests which refer to screens. - [DataTestMethod] - [DataRow("TestStudio_Test.msapp")] - public void TestPackWhenEntropyIsDeleted(string appName) + // Validates that the App can be repacked after deleting the Entropy files, when the app contains app tests which refer to screens. + [DataTestMethod] + [DataRow("TestStudio_Test.msapp")] + public void TestPackWhenEntropyIsDeleted(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; + + // Save to sources + msapp.SaveToSources(outSrcDir); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Delete Entropy directory + var entropyPath = Path.Combine(outSrcDir, "Entropy"); + if (Directory.Exists(entropyPath)) + { + Directory.Delete(entropyPath, true); + } - using (var tempDir = new TempDir()) + // Load app from the sources after deleting the entropy + var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); + + using (var tempFile = new TempFile()) { - string outSrcDir = tempDir.Dir; - - // Save to sources - msapp.SaveToSources(outSrcDir); - - // Delete Entropy directory - var entropyPath = Path.Combine(outSrcDir, "Entropy"); - if (Directory.Exists(entropyPath)) - { - Directory.Delete(entropyPath, true); - } - - // Load app from the sources after deleting the entropy - var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); - - using (var tempFile = new TempFile()) - { - // Repack the app - MsAppSerializer.SaveAsMsApp(app, tempFile.FullPath, new ErrorContainer()); - - // re-unpack should succeed - (var msapp1, var errors1) = CanvasDocument.LoadFromMsapp(tempFile.FullPath); - msapp1.SaveToSources(new TempDir().Dir); - } + // Repack the app + MsAppSerializer.SaveAsMsApp(app, tempFile.FullPath, new ErrorContainer()); + + // re-unpack should succeed + (var msapp1, var errors1) = CanvasDocument.LoadFromMsapp(tempFile.FullPath); + msapp1.SaveToSources(new TempDir().Dir); } } } diff --git a/src/PAModelTests/ChecksumTests.cs b/src/PAModelTests/ChecksumTests.cs index 1f9fed3c..e1e7a8bb 100644 --- a/src/PAModelTests/ChecksumTests.cs +++ b/src/PAModelTests/ChecksumTests.cs @@ -3,132 +3,128 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Reflection.PortableExecutable; using System.Text; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class ChecksumTests { - [TestClass] - public class ChecksumTests + [DataTestMethod] + [DataRow("MyWeather.msapp", "C8_ZXZwZAG3P0lmCkNAGjsIjYb503akWCyudsk8DEi2aX0=", 11, "References\\DataSources.json", "C8_2dpVudcymwNaHoHtQugF1MSpzsY1I6syuPiB0B+jTYc=")] + public void TestChecksum(string filename, string expectedChecksum, int expectedFileCount, string file, string innerExpectedChecksum) { - [DataTestMethod] - [DataRow("MyWeather.msapp", "C8_ZXZwZAG3P0lmCkNAGjsIjYb503akWCyudsk8DEi2aX0=", 11, "References\\DataSources.json", "C8_2dpVudcymwNaHoHtQugF1MSpzsY1I6syuPiB0B+jTYc=")] - public void TestChecksum(string filename, string expectedChecksum, int expectedFileCount, string file, string innerExpectedChecksum) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - // Checksums should be very stable. - var actualChecksum = ChecksumMaker.GetChecksum(root); + // Checksums should be very stable. + var actualChecksum = ChecksumMaker.GetChecksum(root); - Assert.AreEqual(expectedChecksum, actualChecksum.wholeChecksum); - Assert.AreEqual(expectedFileCount, actualChecksum.perFileChecksum.Count); - Assert.IsTrue(actualChecksum.perFileChecksum.TryGetValue(file, out var perFileChecksum)); - Assert.AreEqual(innerExpectedChecksum, perFileChecksum); + Assert.AreEqual(expectedChecksum, actualChecksum.wholeChecksum); + Assert.AreEqual(expectedFileCount, actualChecksum.perFileChecksum.Count); + Assert.IsTrue(actualChecksum.perFileChecksum.TryGetValue(file, out var perFileChecksum)); + Assert.AreEqual(innerExpectedChecksum, perFileChecksum); - // Test checksum version - Assert.AreEqual(ChecksumMaker.GetChecksumVersion(expectedChecksum), ChecksumMaker.Version); - } + // Test checksum version + Assert.AreEqual(ChecksumMaker.GetChecksumVersion(expectedChecksum), ChecksumMaker.Version); + } - [DataTestMethod] - [DataRow("a bc", "a bc")] - [DataRow(" a b ", "a b")] // leading, trailing - [DataRow("a\t\r\nb", "a b")] // other chars - public void TestNormWhitespace(string test, string expected) - { - var val = ChecksumMaker.NormFormulaWhitespace(test); - Assert.AreEqual(expected, val); - } + [DataTestMethod] + [DataRow("a bc", "a bc")] + [DataRow(" a b ", "a b")] // leading, trailing + [DataRow("a\t\r\nb", "a b")] // other chars + public void TestNormWhitespace(string test, string expected) + { + var val = ChecksumMaker.NormFormulaWhitespace(test); + Assert.AreEqual(expected, val); + } - // Ensure each fo the checksum constants is unique. - // this makes it easier to tell when 2 normalized values should be the same. - [TestMethod] - public void ChecksumsUnique() + // Ensure each fo the checksum constants is unique. + // this makes it easier to tell when 2 normalized values should be the same. + [TestMethod] + public void ChecksumsUnique() + { + HashSet<string> checksums = new HashSet<string>(StringComparer.Ordinal); + foreach (var prop in this.GetType().GetFields(BindingFlags.Static | BindingFlags.NonPublic)) { - HashSet<string> checksums = new HashSet<string>(StringComparer.Ordinal); - foreach (var prop in this.GetType().GetFields(BindingFlags.Static | BindingFlags.NonPublic)) + if (prop.Name.StartsWith("C")) { - if (prop.Name.StartsWith("C")) - { - var val = prop.GetValue(null).ToString(); - Assert.IsTrue(checksums.Add(val)); - } + var val = prop.GetValue(null).ToString(); + Assert.IsTrue(checksums.Add(val)); } } + } - // Physically check in the actual checksum results. - // Checksum must be very stable since these get checked into all our customers now. - // Use const fields to help for cases when checksums should be the same. - const string C1 = "C8_VrBVZxNCyyq6nNHBFhEi1p8/+LEgauFoOxISpTMfidA="; - const string C2 = "C8_bSgalCJTSBUuXr/l9syvLFQtQveX9OeUkRFO+bXaxsY="; - const string C3 = "C8_pV4gc7whiqiJA6qNnhtYNgnoy0AV19//Bs/JF+bwvks="; - const string C4 = "C8_XRxPTS3gnYmfjZL2jlOD/3STXSC8VniAOx8bfu0p3Bc="; - const string C5 = "C8_6mPsJ1PnWkhrz7kOmeIdrtdFtkUH+WrnfOrR/YWwB4Q="; - const string C6 = "C8_JpWWJaI11Wjp10J8M8WQ/ZKTOLDqyuh2XpuhrnjNIZI="; - const string C7 = "C8_SsPqlOsAkyDo7QmBft/8uwvbbDAEpX0rfiYqNUZdC/s="; - const string C8 = "C8_8ZdpKBDUV+KX/OnFZTsCWB/5mlCFI3DynX5f5H2dN+Y="; - const string C9 = "C8_mBjUrHTZUVjb7YgLtnZAWXjp25eh8h/uN1rgzH0qTmY="; - const string C10 = "C8_B0Pu6r/4VrrelJ4VYXbhTNJQfG6zem/OvUxIEweFAa4="; - const string C11 = "C8_rNmFqjFuTa2nSPd+9J11Dkq2CE1d3UHrdRPbakKNsz8="; - - const string C12 = "C8_xinyFpAi3E1fug553zcwOQphnA26HzffwtFHFYvwBIQ="; - const string C13 = "C8_tLvF8n0SCv0h+OVpNzeBfjvnMsfWN2SvOttGXgkzud8="; - const string C14 = "C8_But9amnuGeX733SQGNPSq/oEvL0TZdsxLrhtxxaTibg="; - const string C15 = "C8_mF50+afGew2Ea527fnP0EhsQSunAh8GbgaBbxpNPKpM="; - const string C16 = "C8_tpIqQWd4zNcNsjI8xQKjvoocOT+cACVDU9oEmpw/I7Y="; - const string C17 = "C8_nxln5Ugv128wodPRdYyn8U9mXYzeCvPefCJwkLyof8A="; - - const string C18 = "C8_HyC9hHMBR+0EPsD6yYguZ+cYv2ovwlgEfh3iygNbWHM="; - const string C19 = "C8_f8iCQ1KTyoVxp/xpbpwNXvjEZgIeNIaTXdzTe0q26ps="; - const string C20 = "C8_2YUFpLVLEYtdFvV9iLN8F6TM+cWczemMx4m0VEIpfrg="; - - - [DataTestMethod] - [DataRow(C1, @"' ab\r\ncd'")] // whitespace - [DataRow(C1, @"'ab\ncd'")] // same - [DataRow(C2, @"'ab\ncd '")] // Trailing whitespace is not trimmed. - [DataRow(C3, "{'InvariantScript' : 'abc'}")] - [DataRow(C12, "[ 'abc']")] // InvariantScript is skipped - [DataRow(C4, "1.00")] // 1.0 = 1 - [DataRow(C4, "1")] - [DataRow(C5, "\"1\"")] // str and number hash differently - [DataRow(C13, "[ 1 ]")] // BAD: array should be different - [DataRow(C6, "{'a':1,\r\n'b':2}")] - [DataRow(C6, "{'b':2,'a':1 }")] // property ordering doesn't matter. - [DataRow(C7, "{'b2':2,'a':1 }")] // property name matters - [DataRow(C8, "null")] - [DataRow(C14, "false")] // null!=false - [DataRow(C9, "{ 'a' : { } }")] // Empty object - [DataRow(C15, "[ 'a' ]")] // Empty object - [DataRow(C10, "{ 'a' : null }")] // Empty object - [DataRow(C11, "{'a':'b'}")] - [DataRow(C16, "['a', 'b']")] // obj vs. array matters - [DataRow(C17, "['ab']")] // concatenation should be different. - [DataRow(C18, "{'LocalDatabaseReferences' : '' }")] // Double encoded json, per LocalDatabaseReferences name. - [DataRow(C19, "{'LocalDatabaseReferences' : '{}' }")] // Should be different - [DataRow(C20, "{'LocalDatabaseReferences' : '[]' }")] // Should be different - public void Checksum(string expectedChecksum, string json) - { - var actualChecksum = Check(json); + // Physically check in the actual checksum results. + // Checksum must be very stable since these get checked into all our customers now. + // Use const fields to help for cases when checksums should be the same. + const string C1 = "C8_VrBVZxNCyyq6nNHBFhEi1p8/+LEgauFoOxISpTMfidA="; + const string C2 = "C8_bSgalCJTSBUuXr/l9syvLFQtQveX9OeUkRFO+bXaxsY="; + const string C3 = "C8_pV4gc7whiqiJA6qNnhtYNgnoy0AV19//Bs/JF+bwvks="; + const string C4 = "C8_XRxPTS3gnYmfjZL2jlOD/3STXSC8VniAOx8bfu0p3Bc="; + const string C5 = "C8_6mPsJ1PnWkhrz7kOmeIdrtdFtkUH+WrnfOrR/YWwB4Q="; + const string C6 = "C8_JpWWJaI11Wjp10J8M8WQ/ZKTOLDqyuh2XpuhrnjNIZI="; + const string C7 = "C8_SsPqlOsAkyDo7QmBft/8uwvbbDAEpX0rfiYqNUZdC/s="; + const string C8 = "C8_8ZdpKBDUV+KX/OnFZTsCWB/5mlCFI3DynX5f5H2dN+Y="; + const string C9 = "C8_mBjUrHTZUVjb7YgLtnZAWXjp25eh8h/uN1rgzH0qTmY="; + const string C10 = "C8_B0Pu6r/4VrrelJ4VYXbhTNJQfG6zem/OvUxIEweFAa4="; + const string C11 = "C8_rNmFqjFuTa2nSPd+9J11Dkq2CE1d3UHrdRPbakKNsz8="; + + const string C12 = "C8_xinyFpAi3E1fug553zcwOQphnA26HzffwtFHFYvwBIQ="; + const string C13 = "C8_tLvF8n0SCv0h+OVpNzeBfjvnMsfWN2SvOttGXgkzud8="; + const string C14 = "C8_But9amnuGeX733SQGNPSq/oEvL0TZdsxLrhtxxaTibg="; + const string C15 = "C8_mF50+afGew2Ea527fnP0EhsQSunAh8GbgaBbxpNPKpM="; + const string C16 = "C8_tpIqQWd4zNcNsjI8xQKjvoocOT+cACVDU9oEmpw/I7Y="; + const string C17 = "C8_nxln5Ugv128wodPRdYyn8U9mXYzeCvPefCJwkLyof8A="; + + const string C18 = "C8_HyC9hHMBR+0EPsD6yYguZ+cYv2ovwlgEfh3iygNbWHM="; + const string C19 = "C8_f8iCQ1KTyoVxp/xpbpwNXvjEZgIeNIaTXdzTe0q26ps="; + const string C20 = "C8_2YUFpLVLEYtdFvV9iLN8F6TM+cWczemMx4m0VEIpfrg="; + + + [DataTestMethod] + [DataRow(C1, @"' ab\r\ncd'")] // whitespace + [DataRow(C1, @"'ab\ncd'")] // same + [DataRow(C2, @"'ab\ncd '")] // Trailing whitespace is not trimmed. + [DataRow(C3, "{'InvariantScript' : 'abc'}")] + [DataRow(C12, "[ 'abc']")] // InvariantScript is skipped + [DataRow(C4, "1.00")] // 1.0 = 1 + [DataRow(C4, "1")] + [DataRow(C5, "\"1\"")] // str and number hash differently + [DataRow(C13, "[ 1 ]")] // BAD: array should be different + [DataRow(C6, "{'a':1,\r\n'b':2}")] + [DataRow(C6, "{'b':2,'a':1 }")] // property ordering doesn't matter. + [DataRow(C7, "{'b2':2,'a':1 }")] // property name matters + [DataRow(C8, "null")] + [DataRow(C14, "false")] // null!=false + [DataRow(C9, "{ 'a' : { } }")] // Empty object + [DataRow(C15, "[ 'a' ]")] // Empty object + [DataRow(C10, "{ 'a' : null }")] // Empty object + [DataRow(C11, "{'a':'b'}")] + [DataRow(C16, "['a', 'b']")] // obj vs. array matters + [DataRow(C17, "['ab']")] // concatenation should be different. + [DataRow(C18, "{'LocalDatabaseReferences' : '' }")] // Double encoded json, per LocalDatabaseReferences name. + [DataRow(C19, "{'LocalDatabaseReferences' : '{}' }")] // Should be different + [DataRow(C20, "{'LocalDatabaseReferences' : '[]' }")] // Should be different + public void Checksum(string expectedChecksum, string json) + { + var actualChecksum = Check(json); - // If this fails, you can run the checksum with <DebugTextHashMaker> instead. - Assert.AreEqual(expectedChecksum, actualChecksum); - } + // If this fails, you can run the checksum with <DebugTextHashMaker> instead. + Assert.AreEqual(expectedChecksum, actualChecksum); + } - // Helper to checksum a json file - static string Check(string json) - { - json = json.Replace('\'', '\"'); // easier esaping - var bytes = Encoding.UTF8.GetBytes(json); + // Helper to checksum a json file + static string Check(string json) + { + json = json.Replace('\'', '\"'); // easier esaping + var bytes = Encoding.UTF8.GetBytes(json); - byte[] checksum = ChecksumMaker.ChecksumFile<Sha256HashMaker>("test.json", bytes); + byte[] checksum = ChecksumMaker.ChecksumFile<Sha256HashMaker>("test.json", bytes); - var str = ChecksumMaker.ChecksumToString(checksum); + var str = ChecksumMaker.ChecksumToString(checksum); - return str; - } + return str; } } diff --git a/src/PAModelTests/DataSourceTests.cs b/src/PAModelTests/DataSourceTests.cs index 6f41bdd6..17ca31bf 100644 --- a/src/PAModelTests/DataSourceTests.cs +++ b/src/PAModelTests/DataSourceTests.cs @@ -5,279 +5,276 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.PowerPlatform.Formulas.Tools.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; using System.Data; using System.IO; using System.IO.Compression; using System.Linq; -namespace PAModelTests +namespace PAModelTests; + +// DataSources Tests +[TestClass] +public class DataSourceTests { - // DataSources Tests - [TestClass] - public class DataSourceTests + // Validates that the TableDefinitions are being added at the end of the DataSources.json when the entropy file is deleted. + [DataTestMethod] + [DataRow("GalleryTestApp.msapp")] + [DataRow("AccountPlanReviewerMaster.msapp")] + public void TestTableDefinitionsAreLastEntriesWhenEntropyDeleted(string appName) { - // Validates that the TableDefinitions are being added at the end of the DataSources.json when the entropy file is deleted. - [DataTestMethod] - [DataRow("GalleryTestApp.msapp")] - [DataRow("AccountPlanReviewerMaster.msapp")] - public void TestTableDefinitionsAreLastEntriesWhenEntropyDeleted(string appName) + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Save to sources + msapp.SaveToSources(outSrcDir); - using (var tempDir = new TempDir()) + // Delete Entropy directory + var entropyPath = Path.Combine(outSrcDir, "Entropy"); + if (Directory.Exists(entropyPath)) { - string outSrcDir = tempDir.Dir; - - // Save to sources - msapp.SaveToSources(outSrcDir); + Directory.Delete(entropyPath, true); + } - // Delete Entropy directory - var entropyPath = Path.Combine(outSrcDir, "Entropy"); - if (Directory.Exists(entropyPath)) - { - Directory.Delete(entropyPath, true); - } + // Load app from the sources after deleting the entropy + var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); - // Load app from the sources after deleting the entropy - var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); + using (var tempFile = new TempFile()) + { + // Repack the app + MsAppSerializer.SaveAsMsApp(app, tempFile.FullPath, new ErrorContainer()); - using (var tempFile = new TempFile()) + using (var stream = new FileStream(tempFile.FullPath, FileMode.Open)) { - // Repack the app - MsAppSerializer.SaveAsMsApp(app, tempFile.FullPath, new ErrorContainer()); + // Read the msapp file + ZipArchive zipOpen = new ZipArchive(stream, ZipArchiveMode.Read); - using (var stream = new FileStream(tempFile.FullPath, FileMode.Open)) + foreach (var entry in zipOpen.Entries) { - // Read the msapp file - ZipArchive zipOpen = new ZipArchive(stream, ZipArchiveMode.Read); + var kind = FileEntry.TriageKind(FilePath.FromMsAppPath(entry.FullName)); - foreach (var entry in zipOpen.Entries) + switch (kind) { - var kind = FileEntry.TriageKind(FilePath.FromMsAppPath(entry.FullName)); - - switch (kind) - { - // Validate that the last entry in the DataSources.json is TableDefinition entry. - case FileKind.DataSources: - { - var dataSourcesFromMsapp = ToObject<DataSourcesJson>(entry); - var last = dataSourcesFromMsapp.DataSources.LastOrDefault(); - Assert.AreEqual(last.TableDefinition != null, true); - return; - } - default: - break; - } + // Validate that the last entry in the DataSources.json is TableDefinition entry. + case FileKind.DataSources: + { + var dataSourcesFromMsapp = ToObject<DataSourcesJson>(entry); + var last = dataSourcesFromMsapp.DataSources.LastOrDefault(); + Assert.AreEqual(last.TableDefinition != null, true); + return; + } + default: + break; } } } } } + } - [DataTestMethod] - [DataRow("EmptyLocalDBRefsHashMismatchProperties.msapp")] - public void TestNoLocalDatabaseRefsWhenLocalDatabaseReferencesPropertyWasEmptyJson(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); + [DataTestMethod] + [DataRow("EmptyLocalDBRefsHashMismatchProperties.msapp")] + public void TestNoLocalDatabaseRefsWhenLocalDatabaseReferencesPropertyWasEmptyJson(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - errors.ThrowOnErrors(); + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + errors.ThrowOnErrors(); - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - msApp.SaveToSources(sourcesTempDirPath); + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + msApp.SaveToSources(sourcesTempDirPath); - var loadedMsApp = SourceSerializer.LoadFromSource(sourcesTempDirPath, new ErrorContainer()); - Assert.IsTrue(loadedMsApp._entropy.WasLocalDatabaseReferencesEmpty.Value); - Assert.IsFalse(loadedMsApp._entropy.LocalDatabaseReferencesAsEmpty); - Assert.IsTrue(loadedMsApp._dataSourceReferences.Count == 0); - } + var loadedMsApp = SourceSerializer.LoadFromSource(sourcesTempDirPath, new ErrorContainer()); + Assert.IsTrue(loadedMsApp._entropy.WasLocalDatabaseReferencesEmpty.Value); + Assert.IsFalse(loadedMsApp._entropy.LocalDatabaseReferencesAsEmpty); + Assert.IsTrue(loadedMsApp._dataSourceReferences.Count == 0); + } - [DataTestMethod] - [DataRow("EmptyLocalDBRefsHashMismatchProperties.msapp")] - public void TestConnectionInstanceIDHandling(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); + [DataTestMethod] + [DataRow("EmptyLocalDBRefsHashMismatchProperties.msapp")] + public void TestConnectionInstanceIDHandling(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - errors.ThrowOnErrors(); + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + errors.ThrowOnErrors(); - // Testing if conn instance id is added to entropy - Assert.IsNotNull(msApp._entropy.LocalConnectionIDReferences); + // Testing if conn instance id is added to entropy + Assert.IsNotNull(msApp._entropy.LocalConnectionIDReferences); - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - ErrorContainer errorsCaptured = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); - errorsCaptured.ThrowOnErrors(); - } + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + ErrorContainer errorsCaptured = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); + errorsCaptured.ThrowOnErrors(); + } - [DataTestMethod] - [DataRow("MultipleDataSourcesWithOneUnused.msapp")] - public void TestUnusedDataSourcesArePreserved(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); + [DataTestMethod] + [DataRow("MultipleDataSourcesWithOneUnused.msapp")] + public void TestUnusedDataSourcesArePreserved(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - errors.ThrowOnErrors(); + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + errors.ThrowOnErrors(); - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); - errors.ThrowOnErrors(); + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); + errors.ThrowOnErrors(); - var (msApp1, errors1) = CanvasDocument.LoadFromSources(sourcesTempDirPath); + var (msApp1, errors1) = CanvasDocument.LoadFromSources(sourcesTempDirPath); - Assert.AreEqual(msApp._dataSourceReferences.First().Key, msApp._dataSourceReferences.First().Key); - var actualDataSources = msApp1._dataSourceReferences.First().Value.dataSources; - var expectedDataSources = msApp._dataSourceReferences.First().Value.dataSources; - Assert.AreEqual(expectedDataSources.Count, actualDataSources.Count); - Assert.IsTrue(actualDataSources.ContainsKey("environment_39a902ba")); - foreach (var kvp in actualDataSources) + Assert.AreEqual(msApp._dataSourceReferences.First().Key, msApp._dataSourceReferences.First().Key); + var actualDataSources = msApp1._dataSourceReferences.First().Value.dataSources; + var expectedDataSources = msApp._dataSourceReferences.First().Value.dataSources; + Assert.AreEqual(expectedDataSources.Count, actualDataSources.Count); + Assert.IsTrue(actualDataSources.ContainsKey("environment_39a902ba")); + foreach (var kvp in actualDataSources) + { + Assert.IsTrue(expectedDataSources.ContainsKey(kvp.Key)); + var expectedDataSource = expectedDataSources[kvp.Key]; + var actualDataSource = kvp.Value; + Assert.AreEqual(expectedDataSource.ExtensionData.Count, actualDataSource.ExtensionData.Count); + foreach (var kvpExtension in actualDataSource.ExtensionData) { - Assert.IsTrue(expectedDataSources.ContainsKey(kvp.Key)); - var expectedDataSource = expectedDataSources[kvp.Key]; - var actualDataSource = kvp.Value; - Assert.AreEqual(expectedDataSource.ExtensionData.Count, actualDataSource.ExtensionData.Count); - foreach (var kvpExtension in actualDataSource.ExtensionData) - { - Assert.IsTrue(expectedDataSource.ExtensionData.ContainsKey(kvpExtension.Key)); - } + Assert.IsTrue(expectedDataSource.ExtensionData.ContainsKey(kvpExtension.Key)); } } + } - [DataTestMethod] - [DataRow("MultipleDataSourcesWithOneUnused.msapp")] - public void TestUnusedDataSourcesAreNotPreservedWhenNotTracked(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); + [DataTestMethod] + [DataRow("MultipleDataSourcesWithOneUnused.msapp")] + public void TestUnusedDataSourcesAreNotPreservedWhenNotTracked(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - var expectedDataSources = msApp._dataSourceReferences.First().Value.dataSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - msApp._dataSourceReferences.First().Value.dataSources.Remove("environment_39a902ba"); - errors.ThrowOnErrors(); + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + var expectedDataSources = msApp._dataSourceReferences.First().Value.dataSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + msApp._dataSourceReferences.First().Value.dataSources.Remove("environment_39a902ba"); + errors.ThrowOnErrors(); - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); - Assert.IsTrue(errors.HasErrors); + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); + Assert.IsTrue(errors.HasErrors); - var (msApp1, errors1) = CanvasDocument.LoadFromSources(sourcesTempDirPath); - errors1.ThrowOnErrors(); + var (msApp1, errors1) = CanvasDocument.LoadFromSources(sourcesTempDirPath); + errors1.ThrowOnErrors(); - var actualDataSources = msApp1._dataSourceReferences.First().Value.dataSources; - Assert.AreEqual(expectedDataSources.Count - actualDataSources.Count, 1); - foreach (var key in expectedDataSources.Keys) + var actualDataSources = msApp1._dataSourceReferences.First().Value.dataSources; + Assert.AreEqual(expectedDataSources.Count - actualDataSources.Count, 1); + foreach (var key in expectedDataSources.Keys) + { + if (key == "environment_39a902ba") { - if (key == "environment_39a902ba") - { - Assert.IsFalse(actualDataSources.ContainsKey(key)); - } - else - { - Assert.IsTrue(actualDataSources.ContainsKey(key)); - } + Assert.IsFalse(actualDataSources.ContainsKey(key)); + } + else + { + Assert.IsTrue(actualDataSources.ContainsKey(key)); } } + } - [DataTestMethod] - [DataRow("MultipleDataSourcesWithOneUnused.msapp")] - public void TestWhenDataSourcesAreNotPresent(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); - - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - msApp._dataSourceReferences["default.cds"].dataSources = null; - errors.ThrowOnErrors(); - - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); - (msApp, errors) = CanvasDocument.LoadFromSources(sourcesTempDirPath); - using var msAppTemp = new TempFile(); - using var sources2 = new TempDir(); - var errors2 = new ErrorContainer(); - MsAppSerializer.SaveAsMsApp(msApp, msAppTemp.FullPath, errors2); - errors = msApp.SaveToSources(sources2.Dir, msAppTemp.FullPath); - errors.ThrowOnErrors(); - - Assert.IsTrue(msApp._dataSourceReferences.ContainsKey("default.cds")); - Assert.IsNull(msApp._dataSourceReferences.First().Value.dataSources); - } - - [DataTestMethod] - [DataRow("MultipleDataSourcesWithOneUnused.msapp")] - public void TestWhenDataSourcesIsSetToEmptyDictionary(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); - - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - msApp._dataSourceReferences["default.cds"].dataSources = new Dictionary<string, LocalDatabaseReferenceDataSource>(); - errors.ThrowOnErrors(); - - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); - (msApp, errors) = CanvasDocument.LoadFromSources(sourcesTempDirPath); - using var msAppTemp = new TempFile(); - using var sources2 = new TempDir(); - var errors2 = new ErrorContainer(); - MsAppSerializer.SaveAsMsApp(msApp, msAppTemp.FullPath, errors2); - errors = msApp.SaveToSources(sources2.Dir, msAppTemp.FullPath); - errors.ThrowOnErrors(); - - Assert.AreEqual(msApp._dataSourceReferences.First().Value.dataSources.Count, 0); - } + [DataTestMethod] + [DataRow("MultipleDataSourcesWithOneUnused.msapp")] + public void TestWhenDataSourcesAreNotPresent(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); + + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + msApp._dataSourceReferences["default.cds"].dataSources = null; + errors.ThrowOnErrors(); + + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); + (msApp, errors) = CanvasDocument.LoadFromSources(sourcesTempDirPath); + using var msAppTemp = new TempFile(); + using var sources2 = new TempDir(); + var errors2 = new ErrorContainer(); + MsAppSerializer.SaveAsMsApp(msApp, msAppTemp.FullPath, errors2); + errors = msApp.SaveToSources(sources2.Dir, msAppTemp.FullPath); + errors.ThrowOnErrors(); + + Assert.IsTrue(msApp._dataSourceReferences.ContainsKey("default.cds")); + Assert.IsNull(msApp._dataSourceReferences.First().Value.dataSources); + } - [DataTestMethod] - [DataRow("NoUnusedDataSources.msapp")] - public void TestAllUsedDataSourcesArePreserved(string appName) - { - var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(pathToMsApp)); + [DataTestMethod] + [DataRow("MultipleDataSourcesWithOneUnused.msapp")] + public void TestWhenDataSourcesIsSetToEmptyDictionary(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); + + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + msApp._dataSourceReferences["default.cds"].dataSources = new Dictionary<string, LocalDatabaseReferenceDataSource>(); + errors.ThrowOnErrors(); + + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + errors = msApp.SaveToSources(sourcesTempDirPath, pathToMsApp); + (msApp, errors) = CanvasDocument.LoadFromSources(sourcesTempDirPath); + using var msAppTemp = new TempFile(); + using var sources2 = new TempDir(); + var errors2 = new ErrorContainer(); + MsAppSerializer.SaveAsMsApp(msApp, msAppTemp.FullPath, errors2); + errors = msApp.SaveToSources(sources2.Dir, msAppTemp.FullPath); + errors.ThrowOnErrors(); + + Assert.AreEqual(msApp._dataSourceReferences.First().Value.dataSources.Count, 0); + } - var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - errors.ThrowOnErrors(); + [DataTestMethod] + [DataRow("NoUnusedDataSources.msapp")] + public void TestAllUsedDataSourcesArePreserved(string appName) + { + var pathToMsApp = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(pathToMsApp)); - using var sourcesDir = new TempDir(); - errors = msApp.SaveToSources(sourcesDir.Dir); - errors.ThrowOnErrors(); + var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); + errors.ThrowOnErrors(); - var (msApp1, errors1) = CanvasDocument.LoadFromSources(sourcesDir.Dir); - errors1.ThrowOnErrors(); + using var sourcesDir = new TempDir(); + errors = msApp.SaveToSources(sourcesDir.Dir); + errors.ThrowOnErrors(); - Assert.AreEqual(msApp._dataSourceReferences["default.cds"].dataSources.Count, msApp._dataSourceReferences["default.cds"].dataSources.Count); - foreach (var entry in msApp._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key).Zip(msApp1._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key))) - { - Assert.AreEqual(entry.First, entry.Second); - } - } + var (msApp1, errors1) = CanvasDocument.LoadFromSources(sourcesDir.Dir); + errors1.ThrowOnErrors(); - [DataTestMethod] - [DataRow(new string[] {"FileNameOne.txt" }, ".txt")] - [DataRow(new string[] {"FileNameTwo.tx<t" }, ".tx%3ct")] - [DataRow(new string[] { "FileNameThr<ee.txt" }, ".txt")] - public void TestGetExtensionEncoded(string[] fileExtension, string encodedExtension) + Assert.AreEqual(msApp._dataSourceReferences["default.cds"].dataSources.Count, msApp._dataSourceReferences["default.cds"].dataSources.Count); + foreach (var entry in msApp._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key).Zip(msApp1._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key))) { - FilePath filePath = new FilePath(fileExtension); - Assert.AreEqual(filePath.GetExtension(), encodedExtension); + Assert.AreEqual(entry.First, entry.Second); } + } - private static T ToObject<T>(ZipArchiveEntry entry) - { - var je = entry.ToJson(); - return je.ToObject<T>(); - } + [DataTestMethod] + [DataRow(new string[] {"FileNameOne.txt" }, ".txt")] + [DataRow(new string[] {"FileNameTwo.tx<t" }, ".tx%3ct")] + [DataRow(new string[] { "FileNameThr<ee.txt" }, ".txt")] + public void TestGetExtensionEncoded(string[] fileExtension, string encodedExtension) + { + FilePath filePath = new FilePath(fileExtension); + Assert.AreEqual(filePath.GetExtension(), encodedExtension); + } + + private static T ToObject<T>(ZipArchiveEntry entry) + { + var je = entry.ToJson(); + return je.ToObject<T>(); } } diff --git a/src/PAModelTests/DefaultValuesTransformTests.cs b/src/PAModelTests/DefaultValuesTransformTests.cs index 372485e9..2b06d087 100644 --- a/src/PAModelTests/DefaultValuesTransformTests.cs +++ b/src/PAModelTests/DefaultValuesTransformTests.cs @@ -1,16 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; -using System.Collections.Generic; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.AppMagic.Authoring.Persistence; using Microsoft.PowerPlatform.Formulas.Tools; +using Microsoft.PowerPlatform.Formulas.Tools.ControlTemplates; using Microsoft.PowerPlatform.Formulas.Tools.EditorState; using Microsoft.PowerPlatform.Formulas.Tools.IR; -using Microsoft.PowerPlatform.Formulas.Tools.ControlTemplates; using Microsoft.PowerPlatform.Formulas.Tools.SourceTransforms; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; -using Microsoft.AppMagic.Authoring.Persistence; namespace PAModelTests { @@ -19,7 +17,7 @@ public class DefaultValuesTransformTests { public static readonly List<String> dynamicPropertiesList = new List<String>{ "LayoutX", "LayoutY", "LayoutWidth", "LayoutHeight" }; - // Testing the DefaultValuesTransform:beforeWrite() behaviour on control node with null dynamic properties + // Testing the DefaultValuesTransform:beforeWrite() behavior on control node with null dynamic properties // Specifically cases where property null but propertynames not null [TestMethod] public void TestCaseWithNullDynamicProperties() diff --git a/src/PAModelTests/EditorStateTests.cs b/src/PAModelTests/EditorStateTests.cs index a25d38b6..5837696a 100644 --- a/src/PAModelTests/EditorStateTests.cs +++ b/src/PAModelTests/EditorStateTests.cs @@ -4,174 +4,172 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.PowerPlatform.Formulas.Tools.EditorState; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class EditorStateTests { - [TestClass] - public class EditorStateTests + private const string EditorStateFileExtension = ".editorstate.json"; + + /// <summary> + /// Tests that the top parent name is set properly on the editor state file. + /// </summary> + [DataTestMethod] + [DataRow("AppWithLabel.msapp", "Screen1")] + [DataRow("DuplicateScreen.msapp", "Screen1")] + public void TestTopParentSerialization(string appName, string topParentName) { - private const string EditorStateFileExtension = ".editorstate.json"; - - /// <summary> - /// Tests that the top parent name is set properly on the editor state file. - /// </summary> - [DataTestMethod] - [DataRow("AppWithLabel.msapp", "Screen1")] - [DataRow("DuplicateScreen.msapp", "Screen1")] - public void TestTopParentSerialization(string appName, string topParentName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - using (var tempDir = new TempDir()) - { - string outSrcDir = tempDir.Dir; + using (var tempDir = new TempDir()) + { + string outSrcDir = tempDir.Dir; - // Save to sources - msapp.SaveToSources(outSrcDir); + // Save to sources + msapp.SaveToSources(outSrcDir); - // Go find the source file for the editor state - string filename = $"{topParentName}{EditorStateFileExtension}"; - string fullFilePath = Path.Combine(outSrcDir, "Src", "EditorState", filename); - if (File.Exists(fullFilePath)) - { - // Get the file for the specific control we're looking for - DirectoryReader.Entry file = new DirectoryReader.Entry(fullFilePath); - ControlTreeState editorState = file.ToObject<ControlTreeState>(); + // Go find the source file for the editor state + string filename = $"{topParentName}{EditorStateFileExtension}"; + string fullFilePath = Path.Combine(outSrcDir, "Src", "EditorState", filename); + if (File.Exists(fullFilePath)) + { + // Get the file for the specific control we're looking for + DirectoryReader.Entry file = new DirectoryReader.Entry(fullFilePath); + ControlTreeState editorState = file.ToObject<ControlTreeState>(); - // Check that the IsTopParent was set correctly - Assert.AreEqual(topParentName, editorState.TopParentName); - } - else - { - Assert.Fail($"Could not find expected file {fullFilePath}."); - } + // Check that the IsTopParent was set correctly + Assert.AreEqual(topParentName, editorState.TopParentName); + } + else + { + Assert.Fail($"Could not find expected file {fullFilePath}."); } } + } - /// <summary> - /// Tests that the `TopParentName` for each control is set to the correct - /// value when the app is deserialized. - /// </summary> - [DataTestMethod] - [DataRow("AppWithLabel.msapp", "Screen1")] - [DataRow("DuplicateScreen.msapp", "Screen1")] - public void TestTopParentNameLoad(string appName, string topParentName) + /// <summary> + /// Tests that the `TopParentName` for each control is set to the correct + /// value when the app is deserialized. + /// </summary> + [DataTestMethod] + [DataRow("AppWithLabel.msapp", "Screen1")] + [DataRow("DuplicateScreen.msapp", "Screen1")] + public void TestTopParentNameLoad(string appName, string topParentName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Save to sources + msapp.SaveToSources(outSrcDir); - using (var tempDir = new TempDir()) + // Go find the source file for the editor state + string filename = $"{topParentName}.editorstate.json"; + string fullFilePath = Path.Combine(outSrcDir, "Src", "EditorState", filename); + if (File.Exists(fullFilePath)) { - string outSrcDir = tempDir.Dir; + // Get the file for the specific control we're looking for + DirectoryReader.Entry file = new DirectoryReader.Entry(fullFilePath); + ControlTreeState editorState = file.ToObject<ControlTreeState>(); - // Save to sources - msapp.SaveToSources(outSrcDir); + // Rename the file so we know that the file name itself wasn't + // used but rather than correct control name. + string newFileName = Guid.NewGuid().ToString(); + string newFilePath = Path.Combine(outSrcDir, "Src", "EditorState", $"{newFileName}{EditorStateFileExtension}"); - // Go find the source file for the editor state - string filename = $"{topParentName}.editorstate.json"; - string fullFilePath = Path.Combine(outSrcDir, "Src", "EditorState", filename); - if (File.Exists(fullFilePath)) - { - // Get the file for the specific control we're looking for - DirectoryReader.Entry file = new DirectoryReader.Entry(fullFilePath); - ControlTreeState editorState = file.ToObject<ControlTreeState>(); - - // Rename the file so we know that the file name itself wasn't - // used but rather than correct control name. - string newFileName = Guid.NewGuid().ToString(); - string newFilePath = Path.Combine(outSrcDir, "Src", "EditorState", $"{newFileName}{EditorStateFileExtension}"); - - File.Move(fullFilePath, newFilePath); - - // Load app from the source folder - var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); - - // Find the relevant controls and check their top parent name - foreach (var control in editorState.ControlStates) - { - app._editorStateStore.TryGetControlState(control.Value.Name, out ControlState state); - Assert.AreEqual(topParentName, state.TopParentName); - } - } - else + File.Move(fullFilePath, newFilePath); + + // Load app from the source folder + var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); + + // Find the relevant controls and check their top parent name + foreach (var control in editorState.ControlStates) { - Assert.Fail($"Could not find expected file {fullFilePath}."); + app._editorStateStore.TryGetControlState(control.Value.Name, out ControlState state); + Assert.AreEqual(topParentName, state.TopParentName); } } + else + { + Assert.Fail($"Could not find expected file {fullFilePath}."); + } } + } + + /// <summary> + /// Test that, when deserializing an older file format for the editor + /// state, the name of the file is used as the `TopParentName` and the + /// controls are deserialized correctly. + /// + /// This preserves backwards compatability for apps packed prior to + /// changing how the `TopParentName` was stored and read. + /// + /// When SourceSerializer is updated past v24, this could be removed entirely. + /// </summary> + [DataTestMethod] + [DataRow("AppWithLabel.msapp", "Screen1")] + [DataRow("DuplicateScreen.msapp", "Screen1")] + public void TestTopParentNameFallback(string appName, string topParentName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - /// <summary> - /// Test that, when deserializing an older file format for the editor - /// state, the name of the file is used as the `TopParentName` and the - /// controls are deserialized correctly. - /// - /// This preserves backwards compatability for apps packed prior to - /// changing how the `TopParentName` was stored and read. - /// - /// When SourceSerializer is updated past v24, this could be removed entirely. - /// </summary> - [DataTestMethod] - [DataRow("AppWithLabel.msapp", "Screen1")] - [DataRow("DuplicateScreen.msapp", "Screen1")] - public void TestTopParentNameFallback(string appName, string topParentName) + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Save to sources + msapp.SaveToSources(outSrcDir); - using (var tempDir = new TempDir()) + // Go find the source file for the editor state + string filename = $"{topParentName}.editorstate.json"; + string fullFilePath = Path.Combine(outSrcDir, "Src", "EditorState", filename); + if (File.Exists(fullFilePath)) { - string outSrcDir = tempDir.Dir; + // Get the file for the specific control we're looking for + DirectoryReader.Entry file = new DirectoryReader.Entry(fullFilePath); + ControlTreeState editorState = file.ToObject<ControlTreeState>(); - // Save to sources - msapp.SaveToSources(outSrcDir); + // Rename the file so we know that the file name itself is used. + string newFileName = Guid.NewGuid().ToString(); + string newFilePath = Path.Combine(outSrcDir, "Src", "EditorState"); - // Go find the source file for the editor state - string filename = $"{topParentName}.editorstate.json"; - string fullFilePath = Path.Combine(outSrcDir, "Src", "EditorState", filename); - if (File.Exists(fullFilePath)) - { - // Get the file for the specific control we're looking for - DirectoryReader.Entry file = new DirectoryReader.Entry(fullFilePath); - ControlTreeState editorState = file.ToObject<ControlTreeState>(); - - // Rename the file so we know that the file name itself is used. - string newFileName = Guid.NewGuid().ToString(); - string newFilePath = Path.Combine(outSrcDir, "Src", "EditorState"); - - // Write out only the dictionary to the file, which is the older format. - DirectoryWriter dir = new DirectoryWriter(newFilePath); - dir.WriteAllJson(newFilePath, $"{newFileName}{EditorStateFileExtension}", editorState.ControlStates); - - // Remove the old file, we only want the re-written and re-named file - File.Delete(fullFilePath); - - // Load app from the source folder - var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); - - // Find the relevant controls and check their top parent name - foreach (var control in editorState.ControlStates) - { - app._editorStateStore.TryGetControlState(control.Value.Name, out ControlState state); - Assert.AreEqual(newFileName, state.TopParentName); - } - } - else + // Write out only the dictionary to the file, which is the older format. + DirectoryWriter dir = new DirectoryWriter(newFilePath); + dir.WriteAllJson(newFilePath, $"{newFileName}{EditorStateFileExtension}", editorState.ControlStates); + + // Remove the old file, we only want the re-written and re-named file + File.Delete(fullFilePath); + + // Load app from the source folder + var app = SourceSerializer.LoadFromSource(outSrcDir, new ErrorContainer()); + + // Find the relevant controls and check their top parent name + foreach (var control in editorState.ControlStates) { - Assert.Fail($"Could not find expected file {fullFilePath}."); + app._editorStateStore.TryGetControlState(control.Value.Name, out ControlState state); + Assert.AreEqual(newFileName, state.TopParentName); } } + else + { + Assert.Fail($"Could not find expected file {fullFilePath}."); + } } } } diff --git a/src/PAModelTests/EntropyTests.cs b/src/PAModelTests/EntropyTests.cs index 695915b6..5d349147 100644 --- a/src/PAModelTests/EntropyTests.cs +++ b/src/PAModelTests/EntropyTests.cs @@ -4,154 +4,151 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.PowerPlatform.Formulas.Tools.Schemas; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -using System.Linq; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class EntropyTests { - [TestClass] - public class EntropyTests + [DataTestMethod] + [DataRow("ComponentTest.msapp", true)] + [DataRow("ComponentWithSameParam.msapp", false)] + public void TestFunctionParameters(string filename, bool invariantScriptsOnInstancesExist) { - [DataTestMethod] - [DataRow("ComponentTest.msapp", true)] - [DataRow("ComponentWithSameParam.msapp", false)] - public void TestFunctionParameters(string filename, bool invariantScriptsOnInstancesExist) + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + if (invariantScriptsOnInstancesExist) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); - - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - if (invariantScriptsOnInstancesExist) - { - Assert.IsNotNull(msapp._entropy.FunctionParamsInvariantScriptsOnInstances); - } - - using (var tempDir = new TempDir()) - { - string outSrcDir = tempDir.Dir; - - // Save to sources - // Also tests repacking, errors captured if any - ErrorContainer errorSources = msapp.SaveToSources(outSrcDir); - errorSources.ThrowOnErrors(); - } + Assert.IsNotNull(msapp._entropy.FunctionParamsInvariantScriptsOnInstances); } - [DataTestMethod] - [DataRow("AnimationControlIdIsGuid.msapp")] - public void TestControlIdGuidParsing(string filename) + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - Assert.IsTrue(msapp._entropy.ControlUniqueGuids.Count > 0); - Assert.AreEqual(msapp._entropy.ControlUniqueIds.Count, 0); + // Save to sources + // Also tests repacking, errors captured if any + ErrorContainer errorSources = msapp.SaveToSources(outSrcDir); + errorSources.ThrowOnErrors(); } + } - [DataTestMethod] - [DataRow("AppWithLabel.msapp")] - public void TestControlIdIntParsing(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); + [DataTestMethod] + [DataRow("AnimationControlIdIsGuid.msapp")] + public void TestControlIdGuidParsing(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - Assert.IsTrue(msapp._entropy.ControlUniqueIds.Count > 0); - Assert.AreEqual(msapp._entropy.ControlUniqueGuids.Count, 0); - } + Assert.IsTrue(msapp._entropy.ControlUniqueGuids.Count > 0); + Assert.AreEqual(msapp._entropy.ControlUniqueIds.Count, 0); + } - // Validate that the control template fields OverridaleProperties and PCFDynamicSchemaForIRRetrieval are stored in entropy while unpacking - // The test app contains control instances with same template but different fields - [DataTestMethod] - [DataRow("ControlInstancesWithDifferentTemplateFields.msapp")] - public void TestControlInstancesWithSameTemplateDifferentFields(string appName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + [DataTestMethod] + [DataRow("AppWithLabel.msapp")] + public void TestControlIdIntParsing(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - Assert.IsTrue(msapp._entropy.OverridablePropertiesEntry.Count > 0); - Assert.IsTrue(msapp._entropy.PCFDynamicSchemaForIRRetrievalEntry.Count > 0); - } + Assert.IsTrue(msapp._entropy.ControlUniqueIds.Count > 0); + Assert.AreEqual(msapp._entropy.ControlUniqueGuids.Count, 0); + } - [DataTestMethod] - [DataRow("AnimationControlIdIsGuid.msapp")] - public void TestGetResourcesJSONIndicesKeyNullException(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); + // Validate that the control template fields OverridaleProperties and PCFDynamicSchemaForIRRetrieval are stored in entropy while unpacking + // The test app contains control instances with same template but different fields + [DataTestMethod] + [DataRow("ControlInstancesWithDifferentTemplateFields.msapp")] + public void TestControlInstancesWithSameTemplateDifferentFields(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - // passing null resource in resourcesJson - msapp._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { null } }; + Assert.IsTrue(msapp._entropy.OverridablePropertiesEntry.Count > 0); + Assert.IsTrue(msapp._entropy.PCFDynamicSchemaForIRRetrievalEntry.Count > 0); + } - TransformResourceJson.PersistOrderingOfResourcesJsonEntries(msapp); - } + [DataTestMethod] + [DataRow("AnimationControlIdIsGuid.msapp")] + public void TestGetResourcesJSONIndicesKeyNullException(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); - // Validate that the pcf control template is stored in entropy while unpacking - // The test app contains control instances with same template but different fields - [DataTestMethod] - [DataRow("PcfTemplates.msapp")] - public void TestPCFControlInstancesWithSameTemplateDifferentFields(string appName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // passing null resource in resourcesJson + msapp._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { null } }; - Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count > 0); - } + TransformResourceJson.PersistOrderingOfResourcesJsonEntries(msapp); + } - [DataTestMethod] - [DataRow("AnimationControlIdIsGuid.msapp")] - public void TestAppWithNoPCFControlInstances(string appName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + // Validate that the pcf control template is stored in entropy while unpacking + // The test app contains control instances with same template but different fields + [DataTestMethod] + [DataRow("PcfTemplates.msapp")] + public void TestPCFControlInstancesWithSameTemplateDifferentFields(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count == 0); - } + Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count > 0); + } - // Validate that a PCF control will still resolve its template by falling back to - // the template store if the control's specific template isn't in Entropy. - [DataTestMethod] - [DataRow("PcfTemplates.msapp")] - public void TestPCFControlWillFallBackToControlTemplate(string appName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + [DataTestMethod] + [DataRow("AnimationControlIdIsGuid.msapp")] + public void TestAppWithNoPCFControlInstances(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count == 0); + } + + // Validate that a PCF control will still resolve its template by falling back to + // the template store if the control's specific template isn't in Entropy. + [DataTestMethod] + [DataRow("PcfTemplates.msapp")] + public void TestPCFControlWillFallBackToControlTemplate(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count > 0); + Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count > 0); - // Clear out the PCF templates in entropy - msapp._entropy.PCFTemplateEntry.Clear(); - Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count == 0); + // Clear out the PCF templates in entropy + msapp._entropy.PCFTemplateEntry.Clear(); + Assert.IsTrue(msapp._entropy.PCFTemplateEntry.Count == 0); - // Repack the app and validate it matches the initial msapp - using (var tempFile = new TempFile()) - { - MsAppSerializer.SaveAsMsApp(msapp, tempFile.FullPath, new ErrorContainer()); - Assert.IsTrue(MsAppTest.Compare(root, tempFile.FullPath, Console.Out)); - } + // Repack the app and validate it matches the initial msapp + using (var tempFile = new TempFile()) + { + MsAppSerializer.SaveAsMsApp(msapp, tempFile.FullPath, new ErrorContainer()); + Assert.IsTrue(MsAppTest.Compare(root, tempFile.FullPath, Console.Out)); } } } diff --git a/src/PAModelTests/ErrorTests.cs b/src/PAModelTests/ErrorTests.cs index 90764d0b..f9e8e544 100644 --- a/src/PAModelTests/ErrorTests.cs +++ b/src/PAModelTests/ErrorTests.cs @@ -2,111 +2,108 @@ // Licensed under the MIT License. using Microsoft.PowerPlatform.Formulas.Tools; -using System; using System.IO; -using System.Text.Json; using Xunit; -namespace PAModelTests +namespace PAModelTests; + +// Verify we get errors (and not exceptions). +public class ErrorTests { - // Verify we get errors (and not exceptions). - public class ErrorTests + public static string PathToValidMsapp = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + public static string PathMissingMsapp = Path.Combine(Environment.CurrentDirectory, "Missing", "Missing.msapp"); + public static string PathMissingDir = Path.Combine(Environment.CurrentDirectory, "MissingDirectory"); + public static int counter = 0; + + [Fact] + public void OpenCorruptedMsApp() + { + // Empty stream is invalid document, should generate a Read error. + MemoryStream ms = new MemoryStream(); + + (var doc, var errors) = CanvasDocument.LoadFromMsapp(ms); + Assert.True(errors.HasErrors); + Assert.Null(doc); + } + + [Fact] + public void OpenMissingMsApp() { - public static string PathToValidMsapp = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - public static string PathMissingMsapp = Path.Combine(Environment.CurrentDirectory, "Missing", "Missing.msapp"); - public static string PathMissingDir = Path.Combine(Environment.CurrentDirectory, "MissingDirectory"); - public static int counter = 0; - - [Fact] - public void OpenCorruptedMsApp() - { - // Empty stream is invalid document, should generate a Read error. - MemoryStream ms = new MemoryStream(); - - (var doc, var errors) = CanvasDocument.LoadFromMsapp(ms); - Assert.True(errors.HasErrors); - Assert.Null(doc); - } - - [Fact] - public void OpenMissingMsApp() - { - (var doc, var errors) = CanvasDocument.LoadFromMsapp(PathMissingMsapp); - Assert.True(errors.HasErrors); - Assert.Null(doc); - } - - [Fact] - public void OpenBadSources() - { - // Common error can be mixing up arguments. Ensure that we detect passing a valid msapp as a source param. - Assert.True(File.Exists(PathToValidMsapp)); - - (var doc, var errors) = CanvasDocument.LoadFromSources(PathToValidMsapp); - Assert.True(errors.HasErrors); - Assert.Null(doc); - } - - [Fact] - public void OpenMissingSources() - { - (var doc, var errors) = CanvasDocument.LoadFromSources(PathMissingDir); - Assert.True(errors.HasErrors); - Assert.Null(doc); - } - - [Fact] - public void BadWriteDir() - { - string path = null; - Assert.Throws<DocumentException>(() => DirectoryWriter.EnsureFileDirExists(path)); - } - - [Theory] - [InlineData("complexChanged1.json", "complexChanged2.json", "complexChangedOutput.txt")] - [InlineData("complexAdded1.json", "complexAdded2.json", "complexAddedOutput.txt")] - [InlineData("complexRemoved1.json", "complexRemoved2.json", "complexRemovedOutput.txt")] - [InlineData("simpleChanged1.json", "simpleChanged2.json", "simpleChangedOutput.txt")] - [InlineData("simpleAdded1.json", "simpleAdded2.json", "simpleAddedOutput.txt")] - [InlineData("simpleRemoved1.json", "simpleRemoved2.json", "simpleRemovedOutput.txt")] - [InlineData("emptyNestedArray1.json", "emptyNestedArray2.json", "emptyNestedArrayOutput.txt")] - [InlineData("simpleArray1.json", "simpleArray2.json", "simpleArrayOutput.txt")] - - public void TestJSONValueChanged(string file1, string file2, string file3) - { - - string path1 = Path.Combine(Environment.CurrentDirectory, "TestData", file1); - string path2 = Path.Combine(Environment.CurrentDirectory, "TestData", file2); - string path3 = Path.Combine(Environment.CurrentDirectory, "TestData", file3); - - ErrorContainer errorContainer = new ErrorContainer(); - - byte[] jsonString1 = File.ReadAllBytes(path1); - byte[] jsonString2 = File.ReadAllBytes(path2); - - var jsonDictionary1 = MsAppTest.FlattenJson(jsonString1); - var jsonDictionary2 = MsAppTest.FlattenJson(jsonString2); - - // IsMismatched on mismatched files - MsAppTest.CheckPropertyChangedRemoved(jsonDictionary1, jsonDictionary2, errorContainer, ""); - MsAppTest.CheckPropertyAdded(jsonDictionary1, jsonDictionary2, errorContainer, ""); - - // Confirm that the unit tests have the expected output - Assert.Equal(File.ReadAllText(path3), errorContainer.ToString()); - } - - [Theory] - [InlineData("ImageApp_SwitchNames.msapp", "ImageApp.msapp")] - [InlineData("ImageApp.msapp", "ImageApp_SwitchNames.msapp")] - public void CompareChecksum_ImageNotReadAsJSONTest(string app1, string app2) - { - var pathToZip1 = Path.Combine(Environment.CurrentDirectory, "Apps", app1); - var pathToZip2 = Path.Combine(Environment.CurrentDirectory, "Apps", app2); - - // When there's a file content mismatch on non-JSON files, - // we must throw an error and not use JSON to compare non JSON-files - var exception = Assert.Throws<ArgumentException>(() => MsAppTest.Compare(pathToZip1, pathToZip2, Console.Out)); - Assert.Equal("Mismatch detected in non-Json properties: Assets\\Images\\1556681b-11bd-4d72-9b17-4f884fb4b465.png", exception.Message); - } + (var doc, var errors) = CanvasDocument.LoadFromMsapp(PathMissingMsapp); + Assert.True(errors.HasErrors); + Assert.Null(doc); + } + + [Fact] + public void OpenBadSources() + { + // Common error can be mixing up arguments. Ensure that we detect passing a valid msapp as a source param. + Assert.True(File.Exists(PathToValidMsapp)); + + (var doc, var errors) = CanvasDocument.LoadFromSources(PathToValidMsapp); + Assert.True(errors.HasErrors); + Assert.Null(doc); + } + + [Fact] + public void OpenMissingSources() + { + (var doc, var errors) = CanvasDocument.LoadFromSources(PathMissingDir); + Assert.True(errors.HasErrors); + Assert.Null(doc); + } + + [Fact] + public void BadWriteDir() + { + string path = null; + Assert.Throws<DocumentException>(() => DirectoryWriter.EnsureFileDirExists(path)); + } + + [Theory] + [InlineData("complexChanged1.json", "complexChanged2.json", "complexChangedOutput.txt")] + [InlineData("complexAdded1.json", "complexAdded2.json", "complexAddedOutput.txt")] + [InlineData("complexRemoved1.json", "complexRemoved2.json", "complexRemovedOutput.txt")] + [InlineData("simpleChanged1.json", "simpleChanged2.json", "simpleChangedOutput.txt")] + [InlineData("simpleAdded1.json", "simpleAdded2.json", "simpleAddedOutput.txt")] + [InlineData("simpleRemoved1.json", "simpleRemoved2.json", "simpleRemovedOutput.txt")] + [InlineData("emptyNestedArray1.json", "emptyNestedArray2.json", "emptyNestedArrayOutput.txt")] + [InlineData("simpleArray1.json", "simpleArray2.json", "simpleArrayOutput.txt")] + + public void TestJSONValueChanged(string file1, string file2, string file3) + { + + string path1 = Path.Combine(Environment.CurrentDirectory, "TestData", file1); + string path2 = Path.Combine(Environment.CurrentDirectory, "TestData", file2); + string path3 = Path.Combine(Environment.CurrentDirectory, "TestData", file3); + + ErrorContainer errorContainer = new ErrorContainer(); + + byte[] jsonString1 = File.ReadAllBytes(path1); + byte[] jsonString2 = File.ReadAllBytes(path2); + + var jsonDictionary1 = MsAppTest.FlattenJson(jsonString1); + var jsonDictionary2 = MsAppTest.FlattenJson(jsonString2); + + // IsMismatched on mismatched files + MsAppTest.CheckPropertyChangedRemoved(jsonDictionary1, jsonDictionary2, errorContainer, ""); + MsAppTest.CheckPropertyAdded(jsonDictionary1, jsonDictionary2, errorContainer, ""); + + // Confirm that the unit tests have the expected output + Assert.Equal(File.ReadAllText(path3), errorContainer.ToString()); + } + + [Theory] + [InlineData("ImageApp_SwitchNames.msapp", "ImageApp.msapp")] + [InlineData("ImageApp.msapp", "ImageApp_SwitchNames.msapp")] + public void CompareChecksum_ImageNotReadAsJSONTest(string app1, string app2) + { + var pathToZip1 = Path.Combine(Environment.CurrentDirectory, "Apps", app1); + var pathToZip2 = Path.Combine(Environment.CurrentDirectory, "Apps", app2); + + // When there's a file content mismatch on non-JSON files, + // we must throw an error and not use JSON to compare non JSON-files + var exception = Assert.Throws<ArgumentException>(() => MsAppTest.Compare(pathToZip1, pathToZip2, Console.Out)); + Assert.Equal("Mismatch detected in non-Json properties: Assets\\Images\\1556681b-11bd-4d72-9b17-4f884fb4b465.png", exception.Message); } } diff --git a/src/PAModelTests/GroupControlRecreationTest.cs b/src/PAModelTests/GroupControlRecreationTest.cs index 8ca8ea70..98930c4e 100644 --- a/src/PAModelTests/GroupControlRecreationTest.cs +++ b/src/PAModelTests/GroupControlRecreationTest.cs @@ -4,39 +4,37 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.PowerPlatform.Formulas.Tools.IR; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; using System.Linq; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class GroupControlRecreationTest { - [TestClass] - public class GroupControlRecreationTest + // Test that group controls are moved to the end of the list when the editorstate is removed. + [TestMethod] + public void TestGroupControlRecreation() { - // Test that group controls are moved to the end of the list when the editorstate is removed. - [TestMethod] - public void TestGroupControlRecreation() - { - // Pull both the msapp and the baseline from our embedded resources. - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "GroupControlTest.msapp"); - Assert.IsTrue(File.Exists(root)); + // Pull both the msapp and the baseline from our embedded resources. + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "GroupControlTest.msapp"); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - msapp._editorStateStore.Remove("Group1"); + msapp._editorStateStore.Remove("Group1"); - msapp.ApplyBeforeMsAppWriteTransforms(errors); - errors.ThrowOnErrors(); + msapp.ApplyBeforeMsAppWriteTransforms(errors); + errors.ThrowOnErrors(); - Assert.IsTrue(msapp._screens.TryGetValue("Screen1", out var screen)); + Assert.IsTrue(msapp._screens.TryGetValue("Screen1", out var screen)); - var sourceFile = IRStateHelpers.CombineIRAndState(screen, errors, msapp._editorStateStore, msapp._templateStore, new UniqueIdRestorer(msapp._entropy), msapp._entropy); + var sourceFile = IRStateHelpers.CombineIRAndState(screen, errors, msapp._editorStateStore, msapp._templateStore, new UniqueIdRestorer(msapp._entropy), msapp._entropy); - Assert.AreEqual("Screen1", sourceFile.ControlName); + Assert.AreEqual("Screen1", sourceFile.ControlName); - // Check that the group control was still moved to the end of the children list - Assert.AreEqual("Group1", sourceFile.Value.TopParent.Children.Last().Name); - } + // Check that the group control was still moved to the end of the children list + Assert.AreEqual("Group1", sourceFile.Value.TopParent.Children.Last().Name); } } diff --git a/src/PAModelTests/JsonNormalizerTests.cs b/src/PAModelTests/JsonNormalizerTests.cs index e5561dd1..d71c509b 100644 --- a/src/PAModelTests/JsonNormalizerTests.cs +++ b/src/PAModelTests/JsonNormalizerTests.cs @@ -1,36 +1,34 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class JsonNormalizerTests { - [TestClass] - public class JsonNormalizerTests + [TestMethod] + public void Test() { - [TestMethod] - public void Test() - { - // - Property ordering - // - Canonical whitespace - // - number encoding - var str1 = JsonNormalizer.Normalize("{ \"A\" : 12.0, \"B\" \r\n: 34} "); - var str2 = JsonNormalizer.Normalize("{ \"B\" : 34, \"A\" : 12} "); + // - Property ordering + // - Canonical whitespace + // - number encoding + var str1 = JsonNormalizer.Normalize("{ \"A\" : 12.0, \"B\" \r\n: 34} "); + var str2 = JsonNormalizer.Normalize("{ \"B\" : 34, \"A\" : 12} "); - Assert.AreEqual(str1, str2); - } + Assert.AreEqual(str1, str2); + } - // String escaping normalizing. \u is an escape, Multiple ways to encode the same char. - [DataTestMethod] - [DataRow("\"a\\\"bc\"")] - [DataRow("\"a\\u0022bc\"")] - public void StringEncoding(string unescaped) - { - var norm = JsonNormalizer.Normalize(unescaped); - var expected = "\"a\\\"bc\""; - Assert.AreEqual(expected, norm); - } + // String escaping normalizing. \u is an escape, Multiple ways to encode the same char. + [DataTestMethod] + [DataRow("\"a\\\"bc\"")] + [DataRow("\"a\\u0022bc\"")] + public void StringEncoding(string unescaped) + { + var norm = JsonNormalizer.Normalize(unescaped); + var expected = "\"a\\\"bc\""; + Assert.AreEqual(expected, norm); } } diff --git a/src/PAModelTests/NameCollisionTests.cs b/src/PAModelTests/NameCollisionTests.cs index fc43b83c..5d9350fd 100644 --- a/src/PAModelTests/NameCollisionTests.cs +++ b/src/PAModelTests/NameCollisionTests.cs @@ -5,268 +5,265 @@ using Microsoft.PowerPlatform.Formulas.Tools.Schemas; using Microsoft.PowerPlatform.Formulas.Tools.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class NameCollisionTests { - [TestClass] - public class NameCollisionTests + + [TestMethod] + public void TestAssetFileRename() { + var doc = new CanvasDocument(); + var resource1 = new ResourceJson() + { + Name = "Image", // Capital + Path = "Assets\\Images\\Image.png", + FileName = "Image.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; + + doc._assetFiles.Add(new FilePath("Images", "Image.png"), new FileEntry()); - [TestMethod] - public void TestAssetFileRename() + var resource2 = new ResourceJson() { - var doc = new CanvasDocument(); - var resource1 = new ResourceJson() - { - Name = "Image", // Capital - Path = "Assets\\Images\\Image.png", - FileName = "Image.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; + Name = "image", // Lowercase + Path = "Assets\\Images\\image.png", + FileName = "image.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; - doc._assetFiles.Add(new FilePath("Images", "Image.png"), new FileEntry()); + doc._assetFiles.Add(new FilePath("Images", "image.png"), new FileEntry()); - var resource2 = new ResourceJson() - { - Name = "image", // Lowercase - Path = "Assets\\Images\\image.png", - FileName = "image.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; + var resource3 = new ResourceJson() + { + Name = "image_1", + Path = "Assets\\Images\\image_1.png", + FileName = "image_1.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; - doc._assetFiles.Add(new FilePath("Images", "image.png"), new FileEntry()); + doc._assetFiles.Add(new FilePath("Images", "image_1.png"), new FileEntry()); - var resource3 = new ResourceJson() - { - Name = "image_1", - Path = "Assets\\Images\\image_1.png", - FileName = "image_1.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; + doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, resource2, resource3 } }; - doc._assetFiles.Add(new FilePath("Images", "image_1.png"), new FileEntry()); + var errorContainer = new ErrorContainer(); + doc.StabilizeAssetFilePaths(errorContainer); - doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, resource2, resource3 } }; + var newFileNames = doc._resourcesJson.Resources.Select(resource => resource.Name); + Assert.IsTrue(newFileNames.Contains("Image")); + Assert.IsTrue(newFileNames.Contains("image_1")); + Assert.IsTrue(newFileNames.Contains("image_2")); + } - var errorContainer = new ErrorContainer(); - doc.StabilizeAssetFilePaths(errorContainer); + [DataTestMethod] + [DataRow("AppWithLabel.msapp")] + [DataRow("DuplicateScreen.msapp")] + public void TestScreenRename(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - var newFileNames = doc._resourcesJson.Resources.Select(resource => resource.Name); - Assert.IsTrue(newFileNames.Contains("Image")); - Assert.IsTrue(newFileNames.Contains("image_1")); - Assert.IsTrue(newFileNames.Contains("image_2")); - } + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - [DataTestMethod] - [DataRow("AppWithLabel.msapp")] - [DataRow("DuplicateScreen.msapp")] - public void TestScreenRename(string appName) + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; + + // Create a list of screens expected to be seen in the output + List<string> expectedScreens = msapp._screens.Keys.ToList(); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Save to sources + msapp.SaveToSources(outSrcDir); - using (var tempDir = new TempDir()) + // Look for the expected screens in the YAML files + string srcPath = Path.Combine(outSrcDir, "Src"); + foreach (string yamlFile in Directory.GetFiles(srcPath, "*.fx.yaml", SearchOption.TopDirectoryOnly)) { - string outSrcDir = tempDir.Dir; + string fileName = Path.GetFileName(yamlFile).Replace(".fx.yaml", string.Empty); - // Create a list of screens expected to be seen in the output - List<string> expectedScreens = msapp._screens.Keys.ToList(); + // Check for an exact match between the screen name and the file. + if (expectedScreens.Contains(fileName)) + { + expectedScreens.Remove(fileName); + continue; + } - // Save to sources - msapp.SaveToSources(outSrcDir); + // Replace any appended suffixes on Windows to see if there was a file collision. + fileName = Regex.Replace(fileName, "(?:_\\d+)?$", string.Empty); - // Look for the expected screens in the YAML files - string srcPath = Path.Combine(outSrcDir, "Src"); - foreach (string yamlFile in Directory.GetFiles(srcPath, "*.fx.yaml", SearchOption.TopDirectoryOnly)) + // Check if the new file name without a suffix matches, otherwise fail the test + if (expectedScreens.Contains(fileName)) { - string fileName = Path.GetFileName(yamlFile).Replace(".fx.yaml", string.Empty); - - // Check for an exact match between the screen name and the file. - if (expectedScreens.Contains(fileName)) - { - expectedScreens.Remove(fileName); - continue; - } - - // Replace any appended suffixes on Windows to see if there was a file collision. - fileName = Regex.Replace(fileName, "(?:_\\d+)?$", string.Empty); - - // Check if the new file name without a suffix matches, otherwise fail the test - if (expectedScreens.Contains(fileName)) - { - expectedScreens.Remove(fileName); - } - else - { - Assert.Fail($"Unexpected file {yamlFile} in Src folder."); - } + expectedScreens.Remove(fileName); + } + else + { + Assert.Fail($"Unexpected file {yamlFile} in Src folder."); } - - // There should be no expected files that were not found - Assert.AreEqual<int>(expectedScreens.Count, 0, $"{expectedScreens.Count} screens not found in Src directory."); } + + // There should be no expected files that were not found + Assert.AreEqual<int>(expectedScreens.Count, 0, $"{expectedScreens.Count} screens not found in Src directory."); } + } - [DataTestMethod] - [DataRow("AppWithLabel.msapp")] - [DataRow("DuplicateScreen.msapp")] - [DataRow("ComponentNameCollision.msapp")] - public void TestEditorStateRename(string appName) + [DataTestMethod] + [DataRow("AppWithLabel.msapp")] + [DataRow("DuplicateScreen.msapp")] + [DataRow("ComponentNameCollision.msapp")] + public void TestEditorStateRename(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using (var tempDir = new TempDir()) { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + string outSrcDir = tempDir.Dir; + + // Create a list of expected controles with an EditorState file + List<string> expectedControlsWithEditorState = new List<string>(); + expectedControlsWithEditorState.AddRange(msapp._screens.Keys); + expectedControlsWithEditorState.AddRange(msapp._components.Keys); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + // Save to sources + msapp.SaveToSources(outSrcDir); - using (var tempDir = new TempDir()) + // Look for the expected controls in the EditorState files + string srcPath = Path.Combine(outSrcDir, "Src", "EditorState"); + foreach (string editorStateFile in Directory.GetFiles(srcPath, "*.editorstate.json", SearchOption.TopDirectoryOnly)) { - string outSrcDir = tempDir.Dir; + string fileName = Path.GetFileName(editorStateFile).Replace(".editorstate.json", string.Empty); - // Create a list of expected controles with an EditorState file - List<string> expectedControlsWithEditorState = new List<string>(); - expectedControlsWithEditorState.AddRange(msapp._screens.Keys); - expectedControlsWithEditorState.AddRange(msapp._components.Keys); + // Check for an exact match between the control and the file. + if (expectedControlsWithEditorState.Contains(fileName)) + { + expectedControlsWithEditorState.Remove(fileName); + continue; + } - // Save to sources - msapp.SaveToSources(outSrcDir); + // Replace any appended suffixes on Windows to see if there was a file collision. + fileName = Regex.Replace(fileName, "(?:_\\d+)?$", string.Empty); - // Look for the expected controls in the EditorState files - string srcPath = Path.Combine(outSrcDir, "Src", "EditorState"); - foreach (string editorStateFile in Directory.GetFiles(srcPath, "*.editorstate.json", SearchOption.TopDirectoryOnly)) + // Check if the new file name without a suffix matches, otherwise fail the test + if (expectedControlsWithEditorState.Contains(fileName)) { - string fileName = Path.GetFileName(editorStateFile).Replace(".editorstate.json", string.Empty); - - // Check for an exact match between the control and the file. - if (expectedControlsWithEditorState.Contains(fileName)) - { - expectedControlsWithEditorState.Remove(fileName); - continue; - } - - // Replace any appended suffixes on Windows to see if there was a file collision. - fileName = Regex.Replace(fileName, "(?:_\\d+)?$", string.Empty); - - // Check if the new file name without a suffix matches, otherwise fail the test - if (expectedControlsWithEditorState.Contains(fileName)) - { - expectedControlsWithEditorState.Remove(fileName); - } - else - { - Assert.Fail($"Unexpected file {editorStateFile} in EditorState folder."); - } + expectedControlsWithEditorState.Remove(fileName); + } + else + { + Assert.Fail($"Unexpected file {editorStateFile} in EditorState folder."); } - - // There should be no expected files that were not found - Assert.AreEqual<int>(expectedControlsWithEditorState.Count, 0, $"{expectedControlsWithEditorState.Count} editor state files not found in EditorState directory."); } - } - [TestMethod] - public void TestAssetPathCollision() - { - var doc = new CanvasDocument(); + // There should be no expected files that were not found + Assert.AreEqual<int>(expectedControlsWithEditorState.Count, 0, $"{expectedControlsWithEditorState.Count} editor state files not found in EditorState directory."); + } + } - var resource1 = new ResourceJson() - { - Name = "Image", - Path = "Assets\\Images\\Image.png", - FileName = "Image.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; - doc._assetFiles.Add(new FilePath("Images", "Image.png"), new FileEntry()); - - // Adding another resource pointing to the same path - var resource2 = new ResourceJson() - { - Name = "Image2", - Path = "Assets\\Images\\Image.png", - FileName = "Image.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; - doc._assetFiles.Add(new FilePath("Images", "Image2.png"), new FileEntry()); + [TestMethod] + public void TestAssetPathCollision() + { + var doc = new CanvasDocument(); - doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, resource2 } }; + var resource1 = new ResourceJson() + { + Name = "Image", + Path = "Assets\\Images\\Image.png", + FileName = "Image.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; + doc._assetFiles.Add(new FilePath("Images", "Image.png"), new FileEntry()); + + // Adding another resource pointing to the same path + var resource2 = new ResourceJson() + { + Name = "Image2", + Path = "Assets\\Images\\Image.png", + FileName = "Image.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; + doc._assetFiles.Add(new FilePath("Images", "Image2.png"), new FileEntry()); - var errorContainer = new ErrorContainer(); - doc.StabilizeAssetFilePaths(errorContainer); + doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, resource2 } }; - Assert.IsFalse(errorContainer.HasErrors); - } + var errorContainer = new ErrorContainer(); + doc.StabilizeAssetFilePaths(errorContainer); - [DataTestMethod] - [DataRow("CollidingFilenames.msapp")] - public void TestDataSourceNameCollision(string appName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + Assert.IsFalse(errorContainer.HasErrors); + } - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + [DataTestMethod] + [DataRow("CollidingFilenames.msapp")] + public void TestDataSourceNameCollision(string appName) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - using (var tempDir = new TempDir()) - { - string outSrcDir = tempDir.Dir; - msapp.SaveToSources(outSrcDir); - } + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - Assert.IsFalse(errors.HasErrors); + using (var tempDir = new TempDir()) + { + string outSrcDir = tempDir.Dir; + msapp.SaveToSources(outSrcDir); } - [TestMethod] - public void TestAssetFileCollision() + Assert.IsFalse(errors.HasErrors); + } + + [TestMethod] + public void TestAssetFileCollision() + { + var doc = new CanvasDocument(); + var resource1 = new ResourceJson() { - var doc = new CanvasDocument(); - var resource1 = new ResourceJson() - { - Name = "0012", - Path = "Assets\\Images\\0002.png", - FileName = "0002.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; + Name = "0012", + Path = "Assets\\Images\\0002.png", + FileName = "0002.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; - FileEntry f1 = new FileEntry(); - f1.Name = new FilePath("Images", "0002.png"); + FileEntry f1 = new FileEntry(); + f1.Name = new FilePath("Images", "0002.png"); - // First Asset file - doc._assetFiles.Add(new FilePath("Images", "0002.png"), f1); + // First Asset file + doc._assetFiles.Add(new FilePath("Images", "0002.png"), f1); - var resource2 = new ResourceJson() - { - Name = "0038", - Path = "Assets\\Images\\0012.png", - FileName = "0012.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; + var resource2 = new ResourceJson() + { + Name = "0038", + Path = "Assets\\Images\\0012.png", + FileName = "0012.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; - FileEntry f2 = new FileEntry(); - f2.Name = new FilePath("Images", "0012.png"); + FileEntry f2 = new FileEntry(); + f2.Name = new FilePath("Images", "0012.png"); - // Second Asset file - doc._assetFiles.Add(new FilePath("Images", "0012.png"), f2); + // Second Asset file + doc._assetFiles.Add(new FilePath("Images", "0012.png"), f2); - doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, resource2 } }; + doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, resource2 } }; - var errorContainer = new ErrorContainer(); - doc.StabilizeAssetFilePaths(errorContainer); - - Assert.AreEqual(doc._assetFiles.Count(), 2); - } + var errorContainer = new ErrorContainer(); + doc.StabilizeAssetFilePaths(errorContainer); + + Assert.AreEqual(doc._assetFiles.Count(), 2); } } diff --git a/src/PAModelTests/ParserTests.cs b/src/PAModelTests/ParserTests.cs index 326481fe..7f262992 100644 --- a/src/PAModelTests/ParserTests.cs +++ b/src/PAModelTests/ParserTests.cs @@ -3,46 +3,42 @@ using Microsoft.PowerPlatform.Formulas.Tools.Parser; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.Text; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class ParserTests { - [TestClass] - public class ParserTests + [DataTestMethod] + [DataRow("Foo", true, "Foo", 3)] + [DataRow("'Foo'", true, "Foo", 5)] + [DataRow("'Foo Bar'", true, "Foo Bar", 9)] + [DataRow("'Foo B''ar'", true, "Foo B'ar", 11)] + [DataRow("'F''o''o B''ar'", true, "F'o'o B'ar", 15)] + [DataRow("Foo Bar", true, "Foo", 3)] + [DataRow("'Foo' Bar", true, "Foo", 5)] + [DataRow("''", false, null, 0)] + [DataRow("'Foo Bar", false, null, 0)] + [DataRow("'Foo ''Bar", false, null, 0)] + public void TestParseIdent(string input, bool shouldParse, string output, int expectedLength) { - [DataTestMethod] - [DataRow("Foo", true, "Foo", 3)] - [DataRow("'Foo'", true, "Foo", 5)] - [DataRow("'Foo Bar'", true, "Foo Bar", 9)] - [DataRow("'Foo B''ar'", true, "Foo B'ar", 11)] - [DataRow("'F''o''o B''ar'", true, "F'o'o B'ar", 15)] - [DataRow("Foo Bar", true, "Foo", 3)] - [DataRow("'Foo' Bar", true, "Foo", 5)] - [DataRow("''", false, null, 0)] - [DataRow("'Foo Bar", false, null, 0)] - [DataRow("'Foo ''Bar", false, null, 0)] - public void TestParseIdent(string input, bool shouldParse, string output, int expectedLength) - { - Assert.AreEqual(shouldParse, Parser.TryParseIdent(input, out var ident, out var length)); - Assert.AreEqual(output, ident); - Assert.AreEqual(expectedLength, length); - } + Assert.AreEqual(shouldParse, Parser.TryParseIdent(input, out var ident, out var length)); + Assert.AreEqual(output, ident); + Assert.AreEqual(expectedLength, length); + } - [DataTestMethod] - [DataRow("Foo As Bar", true, "Foo", "Bar", null)] - [DataRow("Foo As Bar.Baz", true, "Foo", "Bar", "Baz")] - [DataRow("'escaped foo' As Bar", true, "escaped foo", "Bar", null)] - [DataRow("'escaped foo' As Bar.'escaped'", true, "escaped foo", "Bar", "escaped")] - [DataRow("'es''caped f''oo' As 'Escaped'.foo", true, "es'caped f'oo", "Escaped", "foo")] - public void TestParseControlDef(string input, bool shouldParse, string control, string type, string variant) - { - Assert.AreEqual(shouldParse, Parser.TryParseControlDefCore(input, out var cActual, out var tActual, out var vActual)); - Assert.AreEqual(control, cActual); - Assert.AreEqual(type, tActual); - Assert.AreEqual(variant, vActual); + [DataTestMethod] + [DataRow("Foo As Bar", true, "Foo", "Bar", null)] + [DataRow("Foo As Bar.Baz", true, "Foo", "Bar", "Baz")] + [DataRow("'escaped foo' As Bar", true, "escaped foo", "Bar", null)] + [DataRow("'escaped foo' As Bar.'escaped'", true, "escaped foo", "Bar", "escaped")] + [DataRow("'es''caped f''oo' As 'Escaped'.foo", true, "es'caped f'oo", "Escaped", "foo")] + public void TestParseControlDef(string input, bool shouldParse, string control, string type, string variant) + { + Assert.AreEqual(shouldParse, Parser.TryParseControlDefCore(input, out var cActual, out var tActual, out var vActual)); + Assert.AreEqual(control, cActual); + Assert.AreEqual(type, tActual); + Assert.AreEqual(variant, vActual); - } } } diff --git a/src/PAModelTests/PublicSurfaceTests.cs b/src/PAModelTests/PublicSurfaceTests.cs index 106c77b2..029bde7d 100644 --- a/src/PAModelTests/PublicSurfaceTests.cs +++ b/src/PAModelTests/PublicSurfaceTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using FluentAssertions; using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; @@ -12,40 +13,16 @@ namespace PAModelTests; public class PublicSurfaceTests { [TestMethod] - public void Test() + public void TestNamespace() { var asm = typeof(CanvasDocument).Assembly; - - var ns = "Microsoft.PowerPlatform.Formulas.Tools"; - var allowed = new HashSet<string>() - { - $"{ns}.{nameof(CanvasDocument)}", - $"{ns}.{nameof(CanvasMerger)}", - $"{ns}.{nameof(ChecksumMaker)}", - $"{ns}.{nameof(ErrorContainer)}", - $"{ns}.{nameof(Error)}", - $"{ns}.Yaml.YamlConverter", - $"{ns}.Yaml.YamlPocoSerializer", - $"{ns}.Yaml.YamlWriter", - }; - + var publicNamespace = "Microsoft.PowerPlatform.Formulas.Tools"; var sb = new StringBuilder(); foreach (var type in asm.GetTypes().Where(t => t.IsPublic)) { var name = type.FullName; - if (!allowed.Contains(name)) - { - sb.Append(name); - sb.Append("; "); - } - - allowed.Remove(name); + name.Should().StartWith(publicNamespace, $"Type {name} is not in the public namespace {publicNamespace}"); } - - Assert.AreEqual(0, sb.Length, $"Unexpected public types: {sb}"); - - // Types we expect to be in the assembly aren't there. - Assert.AreEqual(0, allowed.Count); } } diff --git a/src/PAModelTests/ReadTransformTests.cs b/src/PAModelTests/ReadTransformTests.cs index b25f7348..d67e288f 100644 --- a/src/PAModelTests/ReadTransformTests.cs +++ b/src/PAModelTests/ReadTransformTests.cs @@ -5,55 +5,52 @@ using Microsoft.PowerPlatform.Formulas.Tools.Schemas; using Microsoft.PowerPlatform.Formulas.Tools.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -using System.Linq; - -namespace PAModelTests -{ - [TestClass] - public class ReadTransformTests - { - [DataTestMethod] - [DataRow("GalleryTemplateNullChildren.msapp", false, false)] - [DataRow("TestStepWithInvalidScreen.msapp", false, true)] - [DataRow("GroupControlStateEmpty.msapp", false, true)] - public void ApplyAfterMsAppLoadTransforms_Test(string filename, bool hasErrors, bool hasWarnings) - { - var path = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(path)); - // ApplyAfterMsAppLoadTransforms is called in LoadFromMsapp - (var msapp, var errorContainer) = CanvasDocument.LoadFromMsapp(path); - errorContainer.ThrowOnErrors(); +namespace PAModelTests; + +[TestClass] +public class ReadTransformTests +{ + [DataTestMethod] + [DataRow("GalleryTemplateNullChildren.msapp", false, false)] + [DataRow("TestStepWithInvalidScreen.msapp", false, true)] + [DataRow("GroupControlStateEmpty.msapp", false, true)] + public void ApplyAfterMsAppLoadTransforms_Test(string filename, bool hasErrors, bool hasWarnings) + { + var path = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(path)); + + // ApplyAfterMsAppLoadTransforms is called in LoadFromMsapp + (var msapp, var errorContainer) = CanvasDocument.LoadFromMsapp(path); + errorContainer.ThrowOnErrors(); + + Assert.AreEqual(errorContainer.HasErrors, hasErrors); + Assert.AreEqual(errorContainer.HasWarnings, hasWarnings); + } - Assert.AreEqual(errorContainer.HasErrors, hasErrors); - Assert.AreEqual(errorContainer.HasWarnings, hasWarnings); - } + [TestMethod] + public void TestNullResource() + { + var doc = new CanvasDocument(); - [TestMethod] - public void TestNullResource() + // resource name null case + var resource1 = new ResourceJson() { - var doc = new CanvasDocument(); - - // resource name null case - var resource1 = new ResourceJson() - { - Name = null, - Path = "Assets\\Images\\Image.png", - FileName = "Image.png", - ResourceKind = ResourceKind.LocalFile, - Content = ContentKind.Image, - }; - doc._assetFiles.Add(new FilePath("Images", "Image.png"), new FileEntry()); - - // passing null resource in resourcesJson - doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, null } }; - - var errorContainer = new ErrorContainer(); - doc.StabilizeAssetFilePaths(errorContainer); - - Assert.AreEqual(errorContainer.HasErrors, false); - } + Name = null, + Path = "Assets\\Images\\Image.png", + FileName = "Image.png", + ResourceKind = ResourceKind.LocalFile, + Content = ContentKind.Image, + }; + doc._assetFiles.Add(new FilePath("Images", "Image.png"), new FileEntry()); + + // passing null resource in resourcesJson + doc._resourcesJson = new ResourcesJson() { Resources = new ResourceJson[] { resource1, null } }; + + var errorContainer = new ErrorContainer(); + doc.StabilizeAssetFilePaths(errorContainer); + + Assert.AreEqual(errorContainer.HasErrors, false); } } diff --git a/src/PAModelTests/RoundtripTests.cs b/src/PAModelTests/RoundtripTests.cs index f6c5743e..9586f410 100644 --- a/src/PAModelTests/RoundtripTests.cs +++ b/src/PAModelTests/RoundtripTests.cs @@ -3,37 +3,35 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -namespace PAModelTests +namespace PAModelTests; + +// Test that a series of .msapps can successfully roundtrip. +[TestClass] +public class RoundtripTests { - // Test that a series of .msapps can succeesfully roundtrip. - [TestClass] - public class RoundtripTests + // Apps live in the "Apps" folder, and should have a build action of "Copy to output" + [TestMethod] + public void StressTestApps() { - // Apps live in the "Apps" folder, and should have a build action of "Copy to output" - [TestMethod] - public void StressTestApps() + var directory = Path.Combine(Environment.CurrentDirectory, "Apps"); + + foreach (var root in Directory.GetFiles(directory)) { - var directory = Path.Combine(Environment.CurrentDirectory, "Apps"); - - foreach (var root in Directory.GetFiles(directory)) + try { - try - { - bool ok = MsAppTest.StressTest(root); - Assert.IsTrue(ok); + bool ok = MsAppTest.StressTest(root); + Assert.IsTrue(ok); - var cloneOk = MsAppTest.TestClone(root); - // If this fails, to debug it, rerun and set a breakpoint in DebugChecksum(). - Assert.IsTrue(cloneOk, $"Clone failed: " + root); - } - catch (Exception ex) - { - Assert.Fail(ex.ToString()); - } - } - } + var cloneOk = MsAppTest.TestClone(root); + // If this fails, to debug it, rerun and set a breakpoint in DebugChecksum(). + Assert.IsTrue(cloneOk, $"Clone failed: " + root); + } + catch (Exception ex) + { + Assert.Fail(ex.ToString()); + } + } } } diff --git a/src/PAModelTests/SmartMergeTests.cs b/src/PAModelTests/SmartMergeTests.cs index 3d0a3104..4d1cabe3 100644 --- a/src/PAModelTests/SmartMergeTests.cs +++ b/src/PAModelTests/SmartMergeTests.cs @@ -3,527 +3,523 @@ using Microsoft.PowerPlatform.Formulas.Tools.IR; using Microsoft.PowerPlatform.Formulas.Tools.Schemas.PcfControl; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class SmartMergeTests { - [TestClass] - public class SmartMergeTests + private delegate void BranchChange(CanvasDocument canvasDoc); + private delegate void ResultValidator(CanvasDocument canvasDoc); + + private void MergeTester(CanvasDocument baseDoc, BranchChange branchAChange, BranchChange branchBChange, ResultValidator resultValidator) { - private delegate void BranchChange(CanvasDocument canvasDoc); - private delegate void ResultValidator(CanvasDocument canvasDoc); + var branchADoc = new CanvasDocument(baseDoc); + var branchBDoc = new CanvasDocument(baseDoc); - private void MergeTester(CanvasDocument baseDoc, BranchChange branchAChange, BranchChange branchBChange, ResultValidator resultValidator) - { - var branchADoc = new CanvasDocument(baseDoc); - var branchBDoc = new CanvasDocument(baseDoc); + branchAChange(branchADoc); + branchBChange(branchBDoc); - branchAChange(branchADoc); - branchBChange(branchBDoc); + var mergeResult = CanvasMerger.Merge(branchADoc, branchBDoc, baseDoc); - var mergeResult = CanvasMerger.Merge(branchADoc, branchBDoc, baseDoc); + resultValidator(mergeResult); + } - resultValidator(mergeResult); - } + [TestMethod] + public void NoOpMergeTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void NoOpMergeTest() + MergeTester( + msapp, + (branchADoc) => + { + // Nothing + }, + (branchBDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + // Nothing + }, + (resultDoc) => + { + Assert.AreEqual(4, resultDoc._editorStateStore.Contents.Count()); + }); + } - MergeTester( - msapp, - (branchADoc) => - { - // Nothing - }, - (branchBDoc) => - { - // Nothing - }, - (resultDoc) => - { - Assert.AreEqual(4, resultDoc._editorStateStore.Contents.Count()); - }); - } + [TestMethod] + public void SimpleControlAddTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void SimpleControlAddTest() + MergeTester( + msapp, + (branchADoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); - - MergeTester( - msapp, - (branchADoc) => + branchADoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - branchADoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }); - }, - (branchBDoc) => + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } + }); + }, + (branchBDoc) => + { + branchBDoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - branchBDoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Bar", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }); - - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen1", out var control); - Assert.IsTrue(control.Children.Any(item => item.Name.Identifier == "Foo")); - Assert.IsTrue(control.Children.Any(item => item.Name.Identifier == "Bar")); + Identifier = "Bar", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "Expr" } } } }); - } - [TestMethod] - public void ControlCollisionTest() + }, + (resultDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + resultDoc._screens.TryGetValue("Screen1", out var control); + Assert.IsTrue(control.Children.Any(item => item.Name.Identifier == "Foo")); + Assert.IsTrue(control.Children.Any(item => item.Name.Identifier == "Bar")); + }); + } + + [TestMethod] + public void ControlCollisionTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - MergeTester( - msapp, - (branchADoc) => + MergeTester( + msapp, + (branchADoc) => + { + branchADoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - branchADoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }); - }, - (branchBDoc) => + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } + }); + }, + (branchBDoc) => + { + branchBDoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - branchBDoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }); - - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen1", out var control); - Assert.AreEqual(1, control.Children.Count(item => item.Name.Identifier == "Foo")); + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "Expr" } } } }); - } - [TestMethod] - public void SimplePropertyEditTest() + }, + (resultDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + resultDoc._screens.TryGetValue("Screen1", out var control); + Assert.AreEqual(1, control.Children.Count(item => item.Name.Identifier == "Foo")); + }); + } - MergeTester( - msapp, - (branchADoc) => - { - // Nothing - }, - (branchBDoc) => - { - branchBDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.First(prop => prop.Identifier == "Text"); - textProp.Expression.Expression = "UpdatedBranchB"; - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + [TestMethod] + public void SimplePropertyEditTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - Assert.AreEqual("UpdatedBranchB", textProp.Expression.Expression); - }); - } + MergeTester( + msapp, + (branchADoc) => + { + // Nothing + }, + (branchBDoc) => + { + branchBDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + textProp.Expression.Expression = "UpdatedBranchB"; + }, + (resultDoc) => + { + resultDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + + Assert.AreEqual("UpdatedBranchB", textProp.Expression.Expression); + }); + } + + [TestMethod] + public void ScreenDeletionTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "Chess_for_Power_Apps_v1.03.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void ScreenDeletionTest() + MergeTester( + msapp, + (branchADoc) => + { + // Nothing + }, + (branchBDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "Chess_for_Power_Apps_v1.03.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + branchBDoc._screens.Remove("Home Screen", out var control); + }, + (resultDoc) => + { + Assert.IsFalse(resultDoc._screens.ContainsKey("Home Screen")); + }); + } - MergeTester( - msapp, - (branchADoc) => - { - // Nothing - }, - (branchBDoc) => - { - branchBDoc._screens.Remove("Home Screen", out var control); - }, - (resultDoc) => - { - Assert.IsFalse(resultDoc._screens.ContainsKey("Home Screen")); - }); - } + [TestMethod] + public void DefaultPropertyEditTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void DefaultPropertyEditTest() + MergeTester( + msapp, + (branchADoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + // Nothing + }, + (branchBDoc) => + { + branchBDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + label.Properties.Add(new PropertyNode() { Identifier = "Fill", Expression = new ExpressionNode() { Expression = "Color.Blue" } }); + }, + (resultDoc) => + { + resultDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var fillProp = label.Properties.First(prop => prop.Identifier == "Fill"); - MergeTester( - msapp, - (branchADoc) => - { - // Nothing - }, - (branchBDoc) => - { - branchBDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - label.Properties.Add(new PropertyNode() { Identifier = "Fill", Expression = new ExpressionNode() { Expression = "Color.Blue" } }); - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var fillProp = label.Properties.First(prop => prop.Identifier == "Fill"); + Assert.AreEqual("Color.Blue", fillProp.Expression.Expression); + }); + } - Assert.AreEqual("Color.Blue", fillProp.Expression.Expression); - }); - } + [TestMethod] + public void PropertyEditCollisonTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void PropertyEditCollisonTest() + MergeTester( + msapp, + (branchADoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + branchADoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + textProp.Expression.Expression = "UpdatedBranchA"; + }, + (branchBDoc) => + { + branchBDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + textProp.Expression.Expression = "UpdatedBranchB"; + }, + (resultDoc) => + { + resultDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.First(prop => prop.Identifier == "Text"); - MergeTester( - msapp, - (branchADoc) => - { - branchADoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.First(prop => prop.Identifier == "Text"); - textProp.Expression.Expression = "UpdatedBranchA"; - }, - (branchBDoc) => - { - branchBDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.First(prop => prop.Identifier == "Text"); - textProp.Expression.Expression = "UpdatedBranchB"; - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + Assert.AreEqual("UpdatedBranchA", textProp.Expression.Expression); + }); + } - Assert.AreEqual("UpdatedBranchA", textProp.Expression.Expression); - }); - } + [TestMethod] + public void PropertyRemoveDefaultTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void PropertyRemoveDefaultTest() + MergeTester( + msapp, + (branchADoc) => + { + // Nothing + }, + (branchBDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + branchBDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.First(prop => prop.Identifier == "Text"); + label.Properties.Remove(textProp); + }, + (resultDoc) => + { + resultDoc._screens.TryGetValue("Screen1", out var control); + var label = control.Children.First(child => child.Name.Identifier == "Label1"); + var textProp = label.Properties.FirstOrDefault(prop => prop.Identifier == "Text"); - MergeTester( - msapp, - (branchADoc) => - { - // Nothing - }, - (branchBDoc) => - { - branchBDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.First(prop => prop.Identifier == "Text"); - label.Properties.Remove(textProp); - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen1", out var control); - var label = control.Children.First(child => child.Name.Identifier == "Label1"); - var textProp = label.Properties.FirstOrDefault(prop => prop.Identifier == "Text"); + Assert.AreEqual(default, textProp); + }); + } - Assert.AreEqual(default, textProp); - }); - } + [TestMethod] + public void ScreenAddTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void ScreenAddTest() + MergeTester( + msapp, + (branchADoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); - - MergeTester( - msapp, - (branchADoc) => + branchADoc._screens.Add("Screen32", new BlockNode() { - branchADoc._screens.Add("Screen32", new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Screen32", - Kind = new TypeNode() { TypeName = "screen" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }); - }, - (branchBDoc) => - { - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen32", out var control); - Assert.AreEqual(1, control.Properties.Count(item => item.Identifier == "SomeProp")); - Assert.IsTrue(resultDoc._screenOrder.Contains("Screen32")); + Identifier = "Screen32", + Kind = new TypeNode() { TypeName = "screen" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } }); - } - - [TestMethod] - public void ScreenAddWithChildCollisionTest_InLocal() + }, + (branchBDoc) => + { + }, + (resultDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + resultDoc._screens.TryGetValue("Screen32", out var control); + Assert.AreEqual(1, control.Properties.Count(item => item.Identifier == "SomeProp")); + Assert.IsTrue(resultDoc._screenOrder.Contains("Screen32")); + }); + } - MergeTester( - msapp, - (branchADoc) => + [TestMethod] + public void ScreenAddWithChildCollisionTest_InLocal() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); + + MergeTester( + msapp, + (branchADoc) => + { + var newScreen = new BlockNode() { - var newScreen = new BlockNode() - { - Name = new TypedNameNode() - { - Identifier = "Screen32", - Kind = new TypeNode() { TypeName = "screen" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }; - - newScreen.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromA" } } } - }); - - branchADoc._screens.Add("Screen32", newScreen); - branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Screen32", TopParentName = "Screen32" }); - branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen32" }); - }, - (branchBDoc) => + Identifier = "Screen32", + Kind = new TypeNode() { TypeName = "screen" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } + }; + + newScreen.Children.Add(new BlockNode() { - branchBDoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromB" } } } - }); - branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen1" }); - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen32", out var control); - Assert.AreEqual(1, control.Children.Count()); - resultDoc._screens.TryGetValue("Screen1", out control); - Assert.IsFalse(control.Children.Any(child => child.Name.Identifier == "Foo")); + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromA" } } } }); - } - [TestMethod] - public void ScreenAddWithChildCollisionTest_InRemote() + branchADoc._screens.Add("Screen32", newScreen); + branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Screen32", TopParentName = "Screen32" }); + branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen32" }); + }, + (branchBDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); - - MergeTester( - msapp, - (branchADoc) => + branchBDoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - branchADoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromB" } } } - }); - branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen1" }); - }, - (branchBDoc) => - { - var newScreen = new BlockNode() - { - Name = new TypedNameNode() - { - Identifier = "Screen32", - Kind = new TypeNode() { TypeName = "screen" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }; - - newScreen.Children.Add(new BlockNode() - { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "label" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromA" } } } - }); - - branchBDoc._screens.Add("Screen32", newScreen); - branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Screen32", TopParentName = "Screen32" }); - branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen32" }); - }, - (resultDoc) => - { - resultDoc._screens.TryGetValue("Screen32", out var control); - Assert.AreEqual(0, control.Children.Count()); - resultDoc._screens.TryGetValue("Screen1", out control); - Assert.IsTrue(control.Children.Any(child => child.Name.Identifier == "Foo")); + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromB" } } } }); - } - - [TestMethod] - public void AddedPCFTest() + branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen1" }); + }, + (resultDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + resultDoc._screens.TryGetValue("Screen32", out var control); + Assert.AreEqual(1, control.Children.Count()); + resultDoc._screens.TryGetValue("Screen1", out control); + Assert.IsFalse(control.Children.Any(child => child.Name.Identifier == "Foo")); + }); + } + + [TestMethod] + public void ScreenAddWithChildCollisionTest_InRemote() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - MergeTester( - msapp, - (branchADoc) => + MergeTester( + msapp, + (branchADoc) => + { + branchADoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - branchADoc._screens.TryGetValue("Screen1", out var control); - control.Children.Add(new BlockNode() + Name = new TypedNameNode() { - Name = new TypedNameNode() - { - Identifier = "Foo", - Kind = new TypeNode() { TypeName = "PCFTemplate" } - }, - Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } - }); - - // These are mocks, feel free to improve if needed to make this test more accurate - branchADoc._templateStore.AddTemplate("PCFTemplate", new CombinedTemplateState() { Name = "PCFTemplate", IsPcfControl = true }); - branchADoc._pcfControls.Add("PCFTemplate", new PcfControl() { Name = "PCFTemplate" }); - }, - (branchBDoc) => + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromB" } } } + }); + branchADoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen1" }); + }, + (branchBDoc) => + { + var newScreen = new BlockNode() { - }, - (resultDoc) => + Name = new TypedNameNode() + { + Identifier = "Screen32", + Kind = new TypeNode() { TypeName = "screen" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } + }; + + newScreen.Children.Add(new BlockNode() { - resultDoc._screens.TryGetValue("Screen1", out var control); - Assert.AreEqual(1, control.Children.Count(item => item.Name.Identifier == "Foo")); - Assert.IsTrue(resultDoc._templateStore.TryGetTemplate("PCFTemplate", out _)); - Assert.IsTrue(resultDoc._pcfControls.ContainsKey("PCFTemplate")); + Name = new TypedNameNode() + { + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "label" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeOtherProp", Expression = new ExpressionNode() { Expression = "FromA" } } } }); - } - [TestMethod] - public void PropertiesTest() + branchBDoc._screens.Add("Screen32", newScreen); + branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Screen32", TopParentName = "Screen32" }); + branchBDoc._editorStateStore.TryAddControl(new ControlState() { Name = "Foo", TopParentName = "Screen32" }); + }, + (resultDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); + resultDoc._screens.TryGetValue("Screen32", out var control); + Assert.AreEqual(0, control.Children.Count()); + resultDoc._screens.TryGetValue("Screen1", out control); + Assert.IsTrue(control.Children.Any(child => child.Name.Identifier == "Foo")); + }); + } - MergeTester( - msapp, - (branchADoc) => - { - branchADoc._properties.DocumentLayoutWidth = 1111; - branchADoc._properties.ExtensionData.Add("Foo", branchADoc._properties.ExtensionData.Values.First()); - }, - (branchBDoc) => - { - branchBDoc._properties.DocumentLayoutWidth = 2222; - branchBDoc._properties.AppPreviewFlagsKey = branchBDoc._properties.AppPreviewFlagsKey.Concat(new List<string>() { "NewFlag" }).ToArray(); - }, - (resultDoc) => + [TestMethod] + public void AddedPCFTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); + + MergeTester( + msapp, + (branchADoc) => + { + branchADoc._screens.TryGetValue("Screen1", out var control); + control.Children.Add(new BlockNode() { - Assert.AreEqual(1111, resultDoc._properties.DocumentLayoutWidth); - Assert.AreEqual(msapp._properties.ExtensionData.Values.First().ToString(), resultDoc._properties.ExtensionData["Foo"].ToString()); - Assert.IsTrue(resultDoc._properties.AppPreviewFlagsKey.Contains("NewFlag")); + Name = new TypedNameNode() + { + Identifier = "Foo", + Kind = new TypeNode() { TypeName = "PCFTemplate" } + }, + Properties = new List<PropertyNode>() { new PropertyNode { Identifier = "SomeProp", Expression = new ExpressionNode() { Expression = "Expr" } } } }); - } + // These are mocks, feel free to improve if needed to make this test more accurate + branchADoc._templateStore.AddTemplate("PCFTemplate", new CombinedTemplateState() { Name = "PCFTemplate", IsPcfControl = true }); + branchADoc._pcfControls.Add("PCFTemplate", new PcfControl() { Name = "PCFTemplate" }); + }, + (branchBDoc) => + { + }, + (resultDoc) => + { + resultDoc._screens.TryGetValue("Screen1", out var control); + Assert.AreEqual(1, control.Children.Count(item => item.Name.Identifier == "Foo")); + Assert.IsTrue(resultDoc._templateStore.TryGetTemplate("PCFTemplate", out _)); + Assert.IsTrue(resultDoc._pcfControls.ContainsKey("PCFTemplate")); + }); + } + [TestMethod] + public void PropertiesTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); - [TestMethod] - public void ScreenOrderTest() + MergeTester( + msapp, + (branchADoc) => + { + branchADoc._properties.DocumentLayoutWidth = 1111; + branchADoc._properties.ExtensionData.Add("Foo", branchADoc._properties.ExtensionData.Values.First()); + }, + (branchBDoc) => + { + branchBDoc._properties.DocumentLayoutWidth = 2222; + branchBDoc._properties.AppPreviewFlagsKey = branchBDoc._properties.AppPreviewFlagsKey.Concat(new List<string>() { "NewFlag" }).ToArray(); + }, + (resultDoc) => { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - Assert.IsFalse(errors.HasErrors); - msapp._screenOrder = new List<string>() { "A", "B", "C" }; + Assert.AreEqual(1111, resultDoc._properties.DocumentLayoutWidth); + Assert.AreEqual(msapp._properties.ExtensionData.Values.First().ToString(), resultDoc._properties.ExtensionData["Foo"].ToString()); + Assert.IsTrue(resultDoc._properties.AppPreviewFlagsKey.Contains("NewFlag")); + }); + } - MergeTester( - msapp, - (branchADoc) => - { - branchADoc._screenOrder = new List<string>() { "B", "C", "A" }; - }, - (branchBDoc) => - { - branchBDoc._screenOrder = new List<string>() { "C", "A", "B" }; - }, - (resultDoc) => - { - Assert.IsTrue(new List<string>() { "B", "C", "A" }.SequenceEqual(resultDoc._screenOrder)); - }); - } + + + [TestMethod] + public void ScreenOrderTest() + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", "MyWeather.msapp"); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + Assert.IsFalse(errors.HasErrors); + msapp._screenOrder = new List<string>() { "A", "B", "C" }; + + MergeTester( + msapp, + (branchADoc) => + { + branchADoc._screenOrder = new List<string>() { "B", "C", "A" }; + }, + (branchBDoc) => + { + branchBDoc._screenOrder = new List<string>() { "C", "A", "B" }; + }, + (resultDoc) => + { + Assert.IsTrue(new List<string>() { "B", "C", "A" }.SequenceEqual(resultDoc._screenOrder)); + }); } } diff --git a/src/PAModelTests/SourceDecoderTests.cs b/src/PAModelTests/SourceDecoderTests.cs index 87b14753..2002169e 100644 --- a/src/PAModelTests/SourceDecoderTests.cs +++ b/src/PAModelTests/SourceDecoderTests.cs @@ -3,56 +3,54 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; -namespace PAModelTests +namespace PAModelTests; + +// Test that a series of .msapps can successfully roundtrip. +[TestClass] +public class SourceDecoderTests { - // Test that a series of .msapps can succeesfully roundtrip. - [TestClass] - public class SourceDecoderTests + // Compare actual source output. This catches things like: + // - are we removing default properties, from both Theme Json and Template xmL? + // - canonical ordering and stable output + [DataTestMethod] + [DataRow("MyWeather.msapp", "", "Screen1.fx.yaml", "Weather_Screen1.fx.yaml")] + [DataRow("GroupControlTest.msapp", "", "Screen1.fx.yaml", "GroupControl_Test.fx.yaml")] + [DataRow("GalleryTestApp.msapp", "", "Screen1.fx.yaml", "Gallery_ScreenTest.fx.yaml")] + [DataRow("SimpleScopeVariables.msapp", "Components", "Component1.fx.yaml", "ComponentFunction_Test.fx.yaml")] + [DataRow("TestStudio_Test.msapp", "Tests", "Suite.fx.yaml", "TestStudio_Test_Suite.fx.yaml")] + [DataRow("TestStudio_Test.msapp", "Tests", "Test_7F478737223C4B69.fx.yaml", "TestStudio_Test.fx.yaml")] + [DataRow("autolayouttest.msapp", "", "Screen1.fx.yaml", "AutoLayout_test.fx.yaml")] + public void TestScreenBaselines(string appName, string basePath, string sourceFileName, string screenBaselineName) { - // Compare actual source output. This catches things like: - // - are we removing default properties, from both Theme Json and Template xmL? - // - canonical ordering and stable output - [DataTestMethod] - [DataRow("MyWeather.msapp", "", "Screen1.fx.yaml", "Weather_Screen1.fx.yaml")] - [DataRow("GroupControlTest.msapp", "", "Screen1.fx.yaml", "GroupControl_Test.fx.yaml")] - [DataRow("GalleryTestApp.msapp", "", "Screen1.fx.yaml", "Gallery_ScreenTest.fx.yaml")] - [DataRow("SimpleScopeVariables.msapp", "Components", "Component1.fx.yaml", "ComponentFunction_Test.fx.yaml")] - [DataRow("TestStudio_Test.msapp", "Tests", "Suite.fx.yaml", "TestStudio_Test_Suite.fx.yaml")] - [DataRow("TestStudio_Test.msapp", "Tests", "Test_7F478737223C4B69.fx.yaml", "TestStudio_Test.fx.yaml")] - [DataRow("autolayouttest.msapp", "", "Screen1.fx.yaml", "AutoLayout_test.fx.yaml")] - public void TestScreenBaselines(string appName, string basePath, string sourceFileName, string screenBaselineName) - { - // Pull both the msapp and the baseline from our embedded resources. - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + // Pull both the msapp and the baseline from our embedded resources. + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - // validate the source - using (var tempDir = new TempDir()) - { - string outSrcDir = tempDir.Dir; - msapp.SaveToSources(outSrcDir); + // validate the source + using (var tempDir = new TempDir()) + { + string outSrcDir = tempDir.Dir; + msapp.SaveToSources(outSrcDir); - var pathActual = Path.Combine(outSrcDir, "Src", basePath, sourceFileName); - var pathExpected = Path.Combine(Environment.CurrentDirectory, "SrcBaseline", screenBaselineName); + var pathActual = Path.Combine(outSrcDir, "Src", basePath, sourceFileName); + var pathExpected = Path.Combine(Environment.CurrentDirectory, "SrcBaseline", screenBaselineName); - // TODO - as the format stabalizes, we can compare more aggressively. - AssertFilesEqual(pathExpected, pathActual); - } + // TODO - as the format stabalizes, we can compare more aggressively. + AssertFilesEqual(pathExpected, pathActual); } + } - static void AssertFilesEqual(string pathExpected, string pathActual) - { - var expected = File.ReadAllText(pathExpected).Replace("\r\n", "\n").Trim(); - var actual = File.ReadAllText(pathActual).Replace("\r\n", "\n").Trim(); + static void AssertFilesEqual(string pathExpected, string pathActual) + { + var expected = File.ReadAllText(pathExpected).Replace("\r\n", "\n").Trim(); + var actual = File.ReadAllText(pathActual).Replace("\r\n", "\n").Trim(); - Assert.AreEqual(expected, actual); - } + Assert.AreEqual(expected, actual); } } diff --git a/src/PAModelTests/TemplateParserTests.cs b/src/PAModelTests/TemplateParserTests.cs index eeed46b0..4cee7bb5 100644 --- a/src/PAModelTests/TemplateParserTests.cs +++ b/src/PAModelTests/TemplateParserTests.cs @@ -5,44 +5,40 @@ using Microsoft.PowerPlatform.Formulas.Tools.ControlTemplates; using Microsoft.PowerPlatform.Formulas.Tools.EditorState; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; using System.IO; -using System.Text; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class TemplateParserTests { - [TestClass] - public class TemplateParserTests + // This test is validating that the control template parser correctly parses default values for gallery control's nested template + // This will likely change after the preprocessor logic to combine nested templates with the parent is added. + [TestMethod] + public void TestGalleryNestedTemplateParse() { - // This test is validating that the control template parser correctly parses default values for gallery control's nested template - // This will likely change after the preprocessor logic to combine nested templates with the parent is added. - [TestMethod] - public void TestGalleryNestedTemplateParse() - { - var galleryTemplatePath = Path.Combine(Environment.CurrentDirectory, "Templates", "gallery_2.10.0.xml"); - Assert.IsTrue(File.Exists(galleryTemplatePath)); + var galleryTemplatePath = Path.Combine(Environment.CurrentDirectory, "Templates", "gallery_2.10.0.xml"); + Assert.IsTrue(File.Exists(galleryTemplatePath)); - using var galleryTemplateStream = File.OpenRead(galleryTemplatePath); - using var galleryTemplateReader = new StreamReader(galleryTemplateStream); + using var galleryTemplateStream = File.OpenRead(galleryTemplatePath); + using var galleryTemplateReader = new StreamReader(galleryTemplateStream); - var galleryTemplateContents = galleryTemplateReader.ReadToEnd(); + var galleryTemplateContents = galleryTemplateReader.ReadToEnd(); - var parsedTemplates = new Dictionary<string, ControlTemplate>(); - var templateStore = new TemplateStore(); - Assert.IsTrue(ControlTemplateParser.TryParseTemplate(templateStore, galleryTemplateContents, AppType.DesktopOrTablet, parsedTemplates, out var topTemplate, out var name)); + var parsedTemplates = new Dictionary<string, ControlTemplate>(); + var templateStore = new TemplateStore(); + Assert.IsTrue(ControlTemplateParser.TryParseTemplate(templateStore, galleryTemplateContents, AppType.DesktopOrTablet, parsedTemplates, out var topTemplate, out var name)); - Assert.AreEqual(2, parsedTemplates.Count); - Assert.AreEqual("gallery", name); - Assert.AreEqual("http://microsoft.com/appmagic/gallery", topTemplate.Id); + Assert.AreEqual(2, parsedTemplates.Count); + Assert.AreEqual("gallery", name); + Assert.AreEqual("http://microsoft.com/appmagic/gallery", topTemplate.Id); - Assert.IsTrue(templateStore.TryGetTemplate("gallery", out _)); + Assert.IsTrue(templateStore.TryGetTemplate("gallery", out _)); - Assert.IsTrue(parsedTemplates.TryGetValue("galleryTemplate", out var innerTemplate)); - Assert.AreEqual("http://microsoft.com/appmagic/galleryTemplate", innerTemplate.Id); - Assert.AreEqual("RGBA(0, 0, 0, 0)", innerTemplate.InputDefaults["TemplateFill"]); + Assert.IsTrue(parsedTemplates.TryGetValue("galleryTemplate", out var innerTemplate)); + Assert.AreEqual("http://microsoft.com/appmagic/galleryTemplate", innerTemplate.Id); + Assert.AreEqual("RGBA(0, 0, 0, 0)", innerTemplate.InputDefaults["TemplateFill"]); - Assert.IsTrue(templateStore.TryGetTemplate("galleryTemplate", out _)); - } + Assert.IsTrue(templateStore.TryGetTemplate("galleryTemplate", out _)); } } diff --git a/src/PAModelTests/TemplateStoreTests.cs b/src/PAModelTests/TemplateStoreTests.cs index b9090b3c..6541799d 100644 --- a/src/PAModelTests/TemplateStoreTests.cs +++ b/src/PAModelTests/TemplateStoreTests.cs @@ -4,46 +4,44 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.PowerPlatform.Formulas.Tools.IR; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; using System.Linq; -namespace PAModelTests +namespace PAModelTests; + +// Control Template Tests +[TestClass] +public class TemplateStoreTests { - // Control Template Tests - [TestClass] - public class TemplateStoreTests + // Validate that the host control template hostType value is stored in entropy while unpacking + // This example app has different host control instances with different template values like HostType + [DataTestMethod] + [DataRow("SharepointAppWithHostControls.msapp")] + public void TestHostControlInstancesWithHostType(string appName) { - // Validate that the host control template hostType value is stored in entropy while unpacking - // This example app has different host control instances with different template values like HostType - [DataTestMethod] - [DataRow("SharepointAppWithHostControls.msapp")] - public void TestHostControlInstancesWithHostType(string appName) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); - Assert.IsTrue(File.Exists(root)); + var root = Path.Combine(Environment.CurrentDirectory, "Apps", appName); + Assert.IsTrue(File.Exists(root)); - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); - Assert.IsTrue(msapp._templateStore.Contents.ContainsKey("Host")); - Assert.IsTrue(msapp._templateStore.Contents.ContainsKey("SharePointIntegration")); + Assert.IsTrue(msapp._templateStore.Contents.ContainsKey("Host")); + Assert.IsTrue(msapp._templateStore.Contents.ContainsKey("SharePointIntegration")); - Assert.IsTrue(msapp._screens.TryGetValue("App", out var app)); - var sourceFile = IRStateHelpers.CombineIRAndState(app, errors, msapp._editorStateStore, msapp._templateStore, new UniqueIdRestorer(msapp._entropy), msapp._entropy); + Assert.IsTrue(msapp._screens.TryGetValue("App", out var app)); + var sourceFile = IRStateHelpers.CombineIRAndState(app, errors, msapp._editorStateStore, msapp._templateStore, new UniqueIdRestorer(msapp._entropy), msapp._entropy); - Assert.AreEqual("Host", sourceFile.Value.TopParent.Children.First().Name); - Assert.AreEqual("SharePointIntegration", sourceFile.Value.TopParent.Children.Last().Name); + Assert.AreEqual("Host", sourceFile.Value.TopParent.Children.First().Name); + Assert.AreEqual("SharePointIntegration", sourceFile.Value.TopParent.Children.Last().Name); - // Checking if the HostType entry added - Assert.IsTrue(sourceFile.Value.TopParent.Children.Last().Template.ExtensionData.ContainsKey("HostType")); + // Checking if the HostType entry added + Assert.IsTrue(sourceFile.Value.TopParent.Children.Last().Template.ExtensionData.ContainsKey("HostType")); - // Repack the app and validate it matches the initial msapp - using (var tempFile = new TempFile()) - { - MsAppSerializer.SaveAsMsApp(msapp, tempFile.FullPath, new ErrorContainer()); - Assert.IsTrue(MsAppTest.Compare(root, tempFile.FullPath, Console.Out)); - } + // Repack the app and validate it matches the initial msapp + using (var tempFile = new TempFile()) + { + MsAppSerializer.SaveAsMsApp(msapp, tempFile.FullPath, new ErrorContainer()); + Assert.IsTrue(MsAppTest.Compare(root, tempFile.FullPath, Console.Out)); } } } diff --git a/src/PAModelTests/UtilityTests.cs b/src/PAModelTests/UtilityTests.cs index 2c6a5bda..21c48198 100644 --- a/src/PAModelTests/UtilityTests.cs +++ b/src/PAModelTests/UtilityTests.cs @@ -5,84 +5,83 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class UtilityTests { - [TestClass] - public class UtilityTests + [DataTestMethod] + [DataRow("\r\t!$^%/\\", "%0d%09%21%24%5e%25%2f%5c")] + [DataRow("одиндваодиндваодиндваодиндваодиндваодинд", "одиндваодиндваодиндваодиндваодиндваодинд")] + [DataRow("İkşzlerAçık芲偁ABC巢für नमस्ते กุ้งจิ้яЧчŠš������ - Copy (2).jpg", "İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg")] + public void TestEscaping(string unescaped, string escaped) { - [DataTestMethod] - [DataRow("\r\t!$^%/\\", "%0d%09%21%24%5e%25%2f%5c")] - [DataRow("одиндваодиндваодиндваодиндваодиндваодинд", "одиндваодиндваодиндваодиндваодиндваодинд")] - [DataRow("İkşzlerAçık芲偁ABC巢für नमस्ते กุ้งจิ้яЧчŠš������ - Copy (2).jpg", "İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg")] - public void TestEscaping(string unescaped, string escaped) - { - Assert.AreEqual(Utilities.EscapeFilename(unescaped), escaped); - Assert.AreEqual(Utilities.UnEscapeFilename(escaped), unescaped); - } + Assert.AreEqual(Utilities.EscapeFilename(unescaped), escaped); + Assert.AreEqual(Utilities.UnEscapeFilename(escaped), unescaped); + } - [DataTestMethod] - [DataRow("foo-%41", "foo-A")] - [DataRow("[]_' ", "[]_' ")] // unescape only touches % character. - [DataRow("İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg", "İkşzlerAçık芲偁ABC巢für नमस्ते กุ้งจิ้яЧчŠš������ - Copy (2).jpg")] - public void TestUnescape(string escaped, string unescaped) - { - Assert.AreEqual(Utilities.UnEscapeFilename(escaped), unescaped); - } + [DataTestMethod] + [DataRow("foo-%41", "foo-A")] + [DataRow("[]_' ", "[]_' ")] // unescape only touches % character. + [DataRow("İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg", "İkşzlerAçık芲偁ABC巢für नमस्ते กุ้งจิ้яЧчŠš������ - Copy (2).jpg")] + public void TestUnescape(string escaped, string unescaped) + { + Assert.AreEqual(Utilities.UnEscapeFilename(escaped), unescaped); + } - [TestMethod] - public void TestNotEscaped() - { - // Not escaped. - var a = "0123456789AZaz[]_. "; - Assert.AreEqual(Utilities.EscapeFilename(a), a); - } + [TestMethod] + public void TestNotEscaped() + { + // Not escaped. + var a = "0123456789AZaz[]_. "; + Assert.AreEqual(Utilities.EscapeFilename(a), a); + } - [DataTestMethod] - [DataRow("C:\\Foo\\Bar\\file", "C:\\Foo", "Bar\\file")] - [DataRow("C:\\Foo\\Bar\\file", "C:\\Foo\\", "Bar\\file")] - [DataRow("C:\\Foo\\Bar.msapp", "C:\\Foo", "Bar.msapp")] - [DataRow("C:\\Foo\\Bar.msapp", "C:\\Foo\\", "Bar.msapp")] - [DataRow("C:\\Foo\\Bar.msapp", "C:\\", "Foo\\Bar.msapp")] - [DataRow(@"C:\DataSources\JourneyPlanner|Sendforapproval.json", "C:\\", @"DataSources\JourneyPlanner|Sendforapproval.json")] - [DataRow(@"C:\DataSources\JourneyPlanner%7cSendforapproval.json", "C:\\", @"DataSources\JourneyPlanner%7cSendforapproval.json")] - [DataRow(@"d:\app\Src\EditorState\Screen%252.editorstate.json", @"d:\app", @"Src\EditorState\Screen%252.editorstate.json")] - [DataRow(@"C:\Temp\MySolution\MySolution.Project\DataSources\JourneyPlanner%7cSendforapproval.json", @"C:\Temp\MySolution\MySolution.Project", @"DataSources\JourneyPlanner%7cSendforapproval.json")] - [DataRow(@"D:\Testing\Power-fx\LocalTest\Unpack\Multi-Lang-App-MX\Assets\Images\İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg", @"D:\Testing\Power-fx\LocalTest\Unpack\Multi-Lang-App-MX\Assets", @"Images\İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg")] - public void TestRelativePath(string fullPath, string basePath, string expectedRelativePath) + [DataTestMethod] + [DataRow("C:\\Foo\\Bar\\file", "C:\\Foo", "Bar\\file")] + [DataRow("C:\\Foo\\Bar\\file", "C:\\Foo\\", "Bar\\file")] + [DataRow("C:\\Foo\\Bar.msapp", "C:\\Foo", "Bar.msapp")] + [DataRow("C:\\Foo\\Bar.msapp", "C:\\Foo\\", "Bar.msapp")] + [DataRow("C:\\Foo\\Bar.msapp", "C:\\", "Foo\\Bar.msapp")] + [DataRow(@"C:\DataSources\JourneyPlanner|Sendforapproval.json", "C:\\", @"DataSources\JourneyPlanner|Sendforapproval.json")] + [DataRow(@"C:\DataSources\JourneyPlanner%7cSendforapproval.json", "C:\\", @"DataSources\JourneyPlanner%7cSendforapproval.json")] + [DataRow(@"d:\app\Src\EditorState\Screen%252.editorstate.json", @"d:\app", @"Src\EditorState\Screen%252.editorstate.json")] + [DataRow(@"C:\Temp\MySolution\MySolution.Project\DataSources\JourneyPlanner%7cSendforapproval.json", @"C:\Temp\MySolution\MySolution.Project", @"DataSources\JourneyPlanner%7cSendforapproval.json")] + [DataRow(@"D:\Testing\Power-fx\LocalTest\Unpack\Multi-Lang-App-MX\Assets\Images\İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg", @"D:\Testing\Power-fx\LocalTest\Unpack\Multi-Lang-App-MX\Assets", @"Images\İkşzlerA%e7ık芲偁ABC巢f%fcr नमस्ते กุ้งจิ้яЧчŠš������ - Copy %282%29.jpg")] + public void TestRelativePath(string fullPath, string basePath, string expectedRelativePath) + { + // Test non-windows paths if on other platforms + if (Path.DirectorySeparatorChar != '\\') { - // Test non-windows paths if on other platforms - if (Path.DirectorySeparatorChar != '\\') - { - fullPath = fullPath.Replace('\\', '/'); - basePath = basePath.Replace('\\', '/'); - expectedRelativePath = expectedRelativePath.Replace('\\', '/'); - } - Assert.AreEqual(expectedRelativePath, Utilities.GetRelativePath(basePath, fullPath)); + fullPath = fullPath.Replace('\\', '/'); + basePath = basePath.Replace('\\', '/'); + expectedRelativePath = expectedRelativePath.Replace('\\', '/'); } + Assert.AreEqual(expectedRelativePath, Utilities.GetRelativePath(basePath, fullPath)); + } - // Verify regression from - // https://github.com/microsoft/PowerApps-Language-Tooling/issues/153 - [TestMethod] - public void Regression153() - { - // Not escaped. - var path = @"DataSources\JourneyPlanner|Sendforapproval.json"; - var escape = Utilities.EscapeFilename(path); + // Verify regression from + // https://github.com/microsoft/PowerApps-Language-Tooling/issues/153 + [TestMethod] + public void Regression153() + { + // Not escaped. + var path = @"DataSources\JourneyPlanner|Sendforapproval.json"; + var escape = Utilities.EscapeFilename(path); - var original = Utilities.UnEscapeFilename(escape); + var original = Utilities.UnEscapeFilename(escape); - Assert.AreEqual(path, original); - } + Assert.AreEqual(path, original); + } - [DataTestMethod] - [DataRow("Long*Control*Name*Truncation*Tests***", "Long%2aControl%2aName%2aTruncation%2aTests%2a_959")] - [DataRow("TestReallyLoooooooooooooooooooooooooooooooooooongControlName", "TestReallyLooooooooooooooooooooooooooooooooooo_cad")] - [DataRow("TestControlName", "TestControlName")] - public void TestControlNameTruncation(string originalName, string expectedName) - { - var truncatedName = Utilities.TruncateNameIfTooLong(originalName); - Assert.AreEqual(truncatedName, expectedName); - } + [DataTestMethod] + [DataRow("Long*Control*Name*Truncation*Tests***", "Long%2aControl%2aName%2aTruncation%2aTests%2a_959")] + [DataRow("TestReallyLoooooooooooooooooooooooooooooooooooongControlName", "TestReallyLooooooooooooooooooooooooooooooooooo_cad")] + [DataRow("TestControlName", "TestControlName")] + public void TestControlNameTruncation(string originalName, string expectedName) + { + var truncatedName = Utilities.TruncateNameIfTooLong(originalName); + Assert.AreEqual(truncatedName, expectedName); } } diff --git a/src/PAModelTests/WriteTransformTests.cs b/src/PAModelTests/WriteTransformTests.cs index 312b34d7..cb0dfebb 100644 --- a/src/PAModelTests/WriteTransformTests.cs +++ b/src/PAModelTests/WriteTransformTests.cs @@ -4,109 +4,107 @@ using Microsoft.PowerPlatform.Formulas.Tools; using Microsoft.PowerPlatform.Formulas.Tools.Schemas; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.IO; using System.Linq; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class WriteTransformTests { - [TestClass] - public class WriteTransformTests + [DataTestMethod] + [DataRow("EmptyTestCase.msapp")] + public void TestResourceNullCase(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + // explicitly setting it to null + msapp._resourcesJson = null; + + msapp.ApplyBeforeMsAppWriteTransforms(errors); + Assert.IsFalse(errors.HasErrors); + } + + [DataTestMethod] + [DataRow("AccountPlanReviewerMaster.msapp")] + public void TestAssetFilesNullCase(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + // explicitly setting it to null + msapp._assetFiles = null; + + msapp.ApplyBeforeMsAppWriteTransforms(errors); + Assert.IsFalse(errors.HasErrors); + } + + [DataTestMethod] + [DataRow("AccountPlanReviewerMaster.msapp")] + public void TestResourcesInResourcesJsonIsNullWhenRestoringAssetFilePaths(string filename) { - [DataTestMethod] - [DataRow("EmptyTestCase.msapp")] - public void TestResourceNullCase(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); - - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - // explicitly setting it to null - msapp._resourcesJson = null; - - msapp.ApplyBeforeMsAppWriteTransforms(errors); - Assert.IsFalse(errors.HasErrors); - } - - [DataTestMethod] - [DataRow("AccountPlanReviewerMaster.msapp")] - public void TestAssetFilesNullCase(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); - - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - // explicitly setting it to null - msapp._assetFiles = null; - - msapp.ApplyBeforeMsAppWriteTransforms(errors); - Assert.IsFalse(errors.HasErrors); - } - - [DataTestMethod] - [DataRow("AccountPlanReviewerMaster.msapp")] - public void TestResourcesInResourcesJsonIsNullWhenRestoringAssetFilePaths(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); - - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - msapp._resourcesJson = new ResourcesJson() { Resources = null }; - - msapp.ApplyBeforeMsAppWriteTransforms(errors); - Assert.IsFalse(errors.HasErrors); - } - - [DataTestMethod] - [DataRow("AccountPlanReviewerMaster.msapp")] - public void TestNullExceptionInRestoreAssetsFilePathsIsLoggedAsAnInternalError(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); - - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - // explicitly setting it to null - msapp._localAssetInfoJson[msapp._resourcesJson.Resources.Last().FileName] = null; - - - msapp.ApplyBeforeMsAppWriteTransforms(errors); - Assert.IsTrue(errors.HasErrors); - Assert.IsNotNull(errors.Where(error => error.Code == ErrorCode.InternalError).FirstOrDefault()); - } - - [DataTestMethod] - [DataRow("AccountPlanReviewerMaster.msapp")] - public void TestNullExceptionInGetMsAppFilesIsLoggedAsAnInternalError(string filename) - { - var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); - Assert.IsTrue(File.Exists(root)); - - (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); - errors.ThrowOnErrors(); - - using var sourcesTempDir = new TempDir(); - var sourcesTempDirPath = sourcesTempDir.Dir; - msapp.SaveToSources(sourcesTempDirPath); - - var loadFromSourceErrors = new ErrorContainer(); - var loadedMsApp = SourceSerializer.LoadFromSource(sourcesTempDirPath, loadFromSourceErrors); - loadFromSourceErrors.ThrowOnErrors(); - - // explicitly setting it to null - loadedMsApp._header = null; - using var tempFile = new TempFile(); - var saveToMsAppErrors = loadedMsApp.SaveToMsApp(tempFile.FullPath); - - Assert.IsTrue(saveToMsAppErrors.HasErrors); - Assert.IsNotNull(saveToMsAppErrors.Where(error => error.Code == ErrorCode.InternalError).FirstOrDefault()); - } + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + msapp._resourcesJson = new ResourcesJson() { Resources = null }; + + msapp.ApplyBeforeMsAppWriteTransforms(errors); + Assert.IsFalse(errors.HasErrors); + } + + [DataTestMethod] + [DataRow("AccountPlanReviewerMaster.msapp")] + public void TestNullExceptionInRestoreAssetsFilePathsIsLoggedAsAnInternalError(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + // explicitly setting it to null + msapp._localAssetInfoJson[msapp._resourcesJson.Resources.Last().FileName] = null; + + + msapp.ApplyBeforeMsAppWriteTransforms(errors); + Assert.IsTrue(errors.HasErrors); + Assert.IsNotNull(errors.Where(error => error.Code == ErrorCode.InternalError).FirstOrDefault()); + } + + [DataTestMethod] + [DataRow("AccountPlanReviewerMaster.msapp")] + public void TestNullExceptionInGetMsAppFilesIsLoggedAsAnInternalError(string filename) + { + var root = Path.Combine(Environment.CurrentDirectory, "Apps", filename); + Assert.IsTrue(File.Exists(root)); + + (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); + errors.ThrowOnErrors(); + + using var sourcesTempDir = new TempDir(); + var sourcesTempDirPath = sourcesTempDir.Dir; + msapp.SaveToSources(sourcesTempDirPath); + + var loadFromSourceErrors = new ErrorContainer(); + var loadedMsApp = SourceSerializer.LoadFromSource(sourcesTempDirPath, loadFromSourceErrors); + loadFromSourceErrors.ThrowOnErrors(); + + // explicitly setting it to null + loadedMsApp._header = null; + using var tempFile = new TempFile(); + var saveToMsAppErrors = loadedMsApp.SaveToMsApp(tempFile.FullPath); + + Assert.IsTrue(saveToMsAppErrors.HasErrors); + Assert.IsNotNull(saveToMsAppErrors.Where(error => error.Code == ErrorCode.InternalError).FirstOrDefault()); } } diff --git a/src/PAModelTests/YamlTest.cs b/src/PAModelTests/YamlTest.cs index 7db032f5..deea1b5b 100644 --- a/src/PAModelTests/YamlTest.cs +++ b/src/PAModelTests/YamlTest.cs @@ -3,33 +3,31 @@ using Microsoft.PowerPlatform.Formulas.Tools.Yaml; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; using System.IO; using YamlDotNet.Serialization; -namespace PAModelTests +namespace PAModelTests; + +[TestClass] +public class YamlTest { - [TestClass] - public class YamlTest + [TestMethod] + public void Write1() { - [TestMethod] - public void Write1() - { - var sw = new StringWriter(); - var yw = new YamlWriter(sw); - yw.WriteProperty("P0", "abc"); - yw.WriteStartObject("Obj1"); - yw.WriteProperty("P1a", "A#B"); // induced multiline, |- since no trailing \n - yw.WriteProperty("P1b", "B"); - yw.WriteStartObject("Obj2"); - yw.WriteProperty("P2a", "A"); - yw.WriteEndObject(); - yw.WriteProperty("P1c", "C"); + var sw = new StringWriter(); + var yw = new YamlWriter(sw); + yw.WriteProperty("P0", "abc"); + yw.WriteStartObject("Obj1"); + yw.WriteProperty("P1a", "A#B"); // induced multiline, |- since no trailing \n + yw.WriteProperty("P1b", "B"); + yw.WriteStartObject("Obj2"); + yw.WriteProperty("P2a", "A"); yw.WriteEndObject(); + yw.WriteProperty("P1c", "C"); + yw.WriteEndObject(); - var t = sw.ToString(); - var expected = @"P0: =abc + var t = sw.ToString(); + var expected = @"P0: =abc Obj1: P1a: |- =A#B @@ -38,163 +36,163 @@ public void Write1() P2a: =A P1c: =C "; - Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); - } + Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); + } - // These values should get automatically multiline escaped. - [DataTestMethod] - [DataRow("Hi #There")] // Yaml comments are dangerous - [DataRow("abc\r\ndef")] - [DataRow("Patched({a : b})")] - public void WriteEscapes(string value) - { - var sw = new StringWriter(); - var yw = new YamlWriter(sw); + // These values should get automatically multiline escaped. + [DataTestMethod] + [DataRow("Hi #There")] // Yaml comments are dangerous + [DataRow("abc\r\ndef")] + [DataRow("Patched({a : b})")] + public void WriteEscapes(string value) + { + var sw = new StringWriter(); + var yw = new YamlWriter(sw); - yw.WriteProperty("Foo", value); + yw.WriteProperty("Foo", value); - var text = sw.ToString(); + var text = sw.ToString(); - // We use a | escape. - Assert.IsTrue(text.StartsWith("Foo: |")); - } + // We use a | escape. + Assert.IsTrue(text.StartsWith("Foo: |")); + } - // Different ending newlines will have different escapes. - [DataTestMethod] - [DataRow(" 1")] // leading whitespace - [DataRow(" 1\n2\n3")] // leading whitespace with multiline - [DataRow(" 1\r2\r3")] // leading whitespace with Mac style multiline - [DataRow("12 + \r\n\r\n34")] - [DataRow("abc\r\ndef")] - [DataRow("abc\rdef")] // MacOS style multiline - [DataRow("\"brows_4.0\"")] - [DataRow("a # b")] // Test with yaml comment. - [DataRow("x")] // easy, no newlines. - [DataRow("1\n2")] // multiline - [DataRow("1\n\n2")] // 2 lines linux - [DataRow("1\r\r2")] // 2 lines mac - [DataRow("1\r\n\r\n2")] // 2 lines windows - [DataRow("1\r\n\r2")] // 2 lines mixed - [DataRow("1\n\r\n2")] // 2 lines mixed - [DataRow("1\n\r2")] // 2 lines mixed - [DataRow("1\n2\n")] // multiline, trailing newline - [DataRow("1\n2\r3\r\n")] // Mixed multiline - [DataRow("1 \n2 \n")] - [DataRow("1\n 2 \n ")] - [DataRow("1\n2 \n ")] - [DataRow("Hi #There")] // Yaml comments are dangerous - [DataRow("abc\ndef")] - [DataRow("Patched({a : b})")] - public void NewLinesRoundtrip(string value) - { - var sw = new StringWriter(); - var yw = new YamlWriter(sw); - yw.WriteProperty("Foo", value); + // Different ending newlines will have different escapes. + [DataTestMethod] + [DataRow(" 1")] // leading whitespace + [DataRow(" 1\n2\n3")] // leading whitespace with multiline + [DataRow(" 1\r2\r3")] // leading whitespace with Mac style multiline + [DataRow("12 + \r\n\r\n34")] + [DataRow("abc\r\ndef")] + [DataRow("abc\rdef")] // MacOS style multiline + [DataRow("\"brows_4.0\"")] + [DataRow("a # b")] // Test with yaml comment. + [DataRow("x")] // easy, no newlines. + [DataRow("1\n2")] // multiline + [DataRow("1\n\n2")] // 2 lines linux + [DataRow("1\r\r2")] // 2 lines mac + [DataRow("1\r\n\r\n2")] // 2 lines windows + [DataRow("1\r\n\r2")] // 2 lines mixed + [DataRow("1\n\r\n2")] // 2 lines mixed + [DataRow("1\n\r2")] // 2 lines mixed + [DataRow("1\n2\n")] // multiline, trailing newline + [DataRow("1\n2\r3\r\n")] // Mixed multiline + [DataRow("1 \n2 \n")] + [DataRow("1\n 2 \n ")] + [DataRow("1\n2 \n ")] + [DataRow("Hi #There")] // Yaml comments are dangerous + [DataRow("abc\ndef")] + [DataRow("Patched({a : b})")] + public void NewLinesRoundtrip(string value) + { + var sw = new StringWriter(); + var yw = new YamlWriter(sw); + yw.WriteProperty("Foo", value); - var text = sw.ToString(); + var text = sw.ToString(); - var normalizedValue = NormNewlines(value); + var normalizedValue = NormNewlines(value); - // Validate it passes YamlDotNet - var valueFromYaml = ParseSinglePropertyViaYamlDotNot(text); - Assert.AreEqual(normalizedValue, NormNewlines(valueFromYaml)); + // Validate it passes YamlDotNet + var valueFromYaml = ParseSinglePropertyViaYamlDotNot(text); + Assert.AreEqual(normalizedValue, NormNewlines(valueFromYaml)); - // Validate it passes our subset. - var sr = new StringReader(text); - var y = new YamlLexer(sr); - var p = y.ReadNext(); - Assert.AreEqual(YamlTokenKind.Property, p.Kind); - Assert.AreEqual("Foo", p.Property); + // Validate it passes our subset. + var sr = new StringReader(text); + var y = new YamlLexer(sr); + var p = y.ReadNext(); + Assert.AreEqual(YamlTokenKind.Property, p.Kind); + Assert.AreEqual("Foo", p.Property); - Assert.AreEqual(normalizedValue, NormNewlines(p.Value)); - } + Assert.AreEqual(normalizedValue, NormNewlines(p.Value)); + } - [DataTestMethod] - [DataRow(true)] - [DataRow(false)] - public void WriteBool(bool value) - { - var sw = new StringWriter(); - var yw = new YamlWriter(sw); - yw.WriteProperty("P0", value); + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void WriteBool(bool value) + { + var sw = new StringWriter(); + var yw = new YamlWriter(sw); + yw.WriteProperty("P0", value); - var t = sw.ToString(); - var expected = $@"P0: ={value.ToString().ToLower()} + var t = sw.ToString(); + var expected = $@"P0: ={value.ToString().ToLower()} "; - Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); - } + Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); + } - [TestMethod] - public void WriteInt() - { - var sw = new StringWriter(); - var yw = new YamlWriter(sw); - yw.WriteProperty("P0", 12); + [TestMethod] + public void WriteInt() + { + var sw = new StringWriter(); + var yw = new YamlWriter(sw); + yw.WriteProperty("P0", 12); - var t = sw.ToString(); - var expected = @"P0: =12 + var t = sw.ToString(); + var expected = @"P0: =12 "; - Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); - } + Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); + } - [TestMethod] - public void WriteDouble() - { - var sw = new StringWriter(); - var yw = new YamlWriter(sw); - yw.WriteProperty("P0", 1.2); + [TestMethod] + public void WriteDouble() + { + var sw = new StringWriter(); + var yw = new YamlWriter(sw); + yw.WriteProperty("P0", 1.2); - var t = sw.ToString(); - var expected = @"P0: =1.2 + var t = sw.ToString(); + var expected = @"P0: =1.2 "; - Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); - } + Assert.AreEqual(expected.Replace("\r\n", "\n"), t.Replace("\r\n", "\n")); + } - // Normalize newlines across OSes. - static string NormNewlines(string x) - { - return x.Replace("\r\n", "\n").Replace("\r", "\n"); - } + // Normalize newlines across OSes. + static string NormNewlines(string x) + { + return x.Replace("\r\n", "\n").Replace("\r", "\n"); + } - // Error on 1st token read - [DataTestMethod] - [DataRow("Foo: 12")] // missing = - [DataRow("Foo: |\r\n=12")] // missing = in newline - [DataRow("Foo: =x #comment")] // comments not allowed in single line. - [DataRow("Foo: |x\n =next")] // chars on same line after | - [DataRow("Foo: >\n =next")] // > multiline not supported - [DataRow("Foo: |\nBar: =next")] // empty multiline - [DataRow("'Foo: \n Bar:")] // unclosed \' escape - [DataRow("---")] // multi docs not supported - public void ExpectedError(string expr) - { - var sr = new StringReader(expr); - var y = new YamlLexer(sr); + // Error on 1st token read + [DataTestMethod] + [DataRow("Foo: 12")] // missing = + [DataRow("Foo: |\r\n=12")] // missing = in newline + [DataRow("Foo: =x #comment")] // comments not allowed in single line. + [DataRow("Foo: |x\n =next")] // chars on same line after | + [DataRow("Foo: >\n =next")] // > multiline not supported + [DataRow("Foo: |\nBar: =next")] // empty multiline + [DataRow("'Foo: \n Bar:")] // unclosed \' escape + [DataRow("---")] // multi docs not supported + public void ExpectedError(string expr) + { + var sr = new StringReader(expr); + var y = new YamlLexer(sr); - AssertLexError(y); - } + AssertLexError(y); + } - // Error on 2nd token read. - [DataTestMethod] - [DataRow("Foo:\r\n val\r\n")] // Must have escape if there's a newline - public void ExpectedError2(string expr) - { - var sr = new StringReader(expr); - var y = new YamlLexer(sr); + // Error on 2nd token read. + [DataTestMethod] + [DataRow("Foo:\r\n val\r\n")] // Must have escape if there's a newline + public void ExpectedError2(string expr) + { + var sr = new StringReader(expr); + var y = new YamlLexer(sr); - var tokenOk = y.ReadNext(); - Assert.AreNotEqual(YamlTokenKind.Error, tokenOk.Kind); + var tokenOk = y.ReadNext(); + Assert.AreNotEqual(YamlTokenKind.Error, tokenOk.Kind); - AssertLexError(y); - } + AssertLexError(y); + } - // Yaml ignores duplicate properties. This could lead to data loss! - // The lexer here catches duplicates and errors. - [TestMethod] - public void ErrorOnDuplicate() - { - var text = + // Yaml ignores duplicate properties. This could lead to data loss! + // The lexer here catches duplicates and errors. + [TestMethod] + public void ErrorOnDuplicate() + { + var text = @"P1: =123 Obj1: P1: =Nested object, not duplicate @@ -202,98 +200,98 @@ public void ErrorOnDuplicate() P2: =456 P1: =duplicate! "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); + var sr = new StringReader(text); + var y = new YamlLexer(sr); - AssertLex("P1=123", y); - AssertLex("Obj1:", y); - AssertLex("P1=Nested object, not duplicate", y); - AssertLexEndObj(y); - AssertLex("p1=Casing Different, not duplicate", y); - AssertLex("P2=456", y); + AssertLex("P1=123", y); + AssertLex("Obj1:", y); + AssertLex("P1=Nested object, not duplicate", y); + AssertLexEndObj(y); + AssertLex("p1=Casing Different, not duplicate", y); + AssertLex("P2=456", y); - AssertLexError(y); - } + AssertLexError(y); + } - [TestMethod] - public void ReadBasic() - { - var text = + [TestMethod] + public void ReadBasic() + { + var text = @"P1: =123 P2: =456 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); + var sr = new StringReader(text); + var y = new YamlLexer(sr); - AssertLex("P1=123", y); - AssertLex("P2=456", y); - AssertLexEndFile(y); - AssertLexEndFile(y); - } + AssertLex("P1=123", y); + AssertLex("P2=456", y); + AssertLexEndFile(y); + AssertLexEndFile(y); + } - [TestMethod] - public void ReadBasicSpans() - { - var text = + [TestMethod] + public void ReadBasicSpans() + { + var text = @"Obj1: P1: =456 Obj2: "; - var sr = new StringReader(text); - var y = new YamlLexer(sr, "test.yaml"); - - AssertLex("Obj1:", y, "test.yaml:1,1-1,6"); - AssertLex("P1=456", y, "test.yaml:2,4-2,12"); - AssertLexEndObj(y); - AssertLex("Obj2:", y, "test.yaml:4,1-4,6"); - AssertLexEndObj(y); - AssertLexEndFile(y); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr, "test.yaml"); + + AssertLex("Obj1:", y, "test.yaml:1,1-1,6"); + AssertLex("P1=456", y, "test.yaml:2,4-2,12"); + AssertLexEndObj(y); + AssertLex("Obj2:", y, "test.yaml:4,1-4,6"); + AssertLexEndObj(y); + AssertLexEndFile(y); + } - // Test basic read of multiline - [TestMethod] - public void ReadBasicMultiline() - { - var text = + // Test basic read of multiline + [TestMethod] + public void ReadBasicMultiline() + { + var text = @"M1: | =abc def P1: =123 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - - AssertLex("M1=abc\r\ndef\r\n", y); - AssertLex("P1=123", y); - AssertLexEndFile(y); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr); + + AssertLex("M1=abc\r\ndef\r\n", y); + AssertLex("P1=123", y); + AssertLexEndFile(y); + } - [TestMethod] - public void ReadBasicMultiline2() - { - // subsequent line in multiline (def) doesn't start at the same indentation - // as first line. This means there are leading spaces on the 2nd line. - var text = + [TestMethod] + public void ReadBasicMultiline2() + { + // subsequent line in multiline (def) doesn't start at the same indentation + // as first line. This means there are leading spaces on the 2nd line. + var text = @"M1: | =abc def P1: =123 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); + var sr = new StringReader(text); + var y = new YamlLexer(sr); - AssertLex("M1=abc\r\n def\r\n", y); - AssertLex("P1=123", y); - AssertLexEndFile(y); - } + AssertLex("M1=abc\r\n def\r\n", y); + AssertLex("P1=123", y); + AssertLexEndFile(y); + } - // Ensure we can get multiple EndObj tokens in a row. - [TestMethod] - public void ReadClosing() - { - var text = + // Ensure we can get multiple EndObj tokens in a row. + [TestMethod] + public void ReadClosing() + { + var text = @"P0: =1 Obj1: Obj2: @@ -302,96 +300,96 @@ public void ReadClosing() P2: =2 P3: =3 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - - AssertLex("P0=1", y); - AssertLex("Obj1:", y); - AssertLex("Obj2:", y); - AssertLex("P1=1", y); - AssertLex("P2=2", y); // the newline prior isn't a token. - AssertLexEndObj(y); // Obj2 - AssertLexEndObj(y); // Obj1 - AssertLex("P3=3", y); - AssertLexEndFile(y); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr); + + AssertLex("P0=1", y); + AssertLex("Obj1:", y); + AssertLex("Obj2:", y); + AssertLex("P1=1", y); + AssertLex("P2=2", y); // the newline prior isn't a token. + AssertLexEndObj(y); // Obj2 + AssertLexEndObj(y); // Obj1 + AssertLex("P3=3", y); + AssertLexEndFile(y); + } - // Handle empty objects. - [TestMethod] - public void ReadEmptyObjects2() - { - var text = + // Handle empty objects. + [TestMethod] + public void ReadEmptyObjects2() + { + var text = @"P0: =1 Obj1: Obj1a: Obj1b: Obj2: "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - - AssertLex("P0=1", y); - AssertLex("Obj1:", y); - AssertLex("Obj1a:", y); - AssertLexEndObj(y); - AssertLex("Obj1b:", y); - AssertLexEndObj(y); // Obj1b - - AssertLexEndObj(y); // Obj1 - AssertLex("Obj2:", y); - AssertLexEndObj(y); // Obj4 - AssertLexEndFile(y); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr); + + AssertLex("P0=1", y); + AssertLex("Obj1:", y); + AssertLex("Obj1a:", y); + AssertLexEndObj(y); + AssertLex("Obj1b:", y); + AssertLexEndObj(y); // Obj1b + + AssertLexEndObj(y); // Obj1 + AssertLex("Obj2:", y); + AssertLexEndObj(y); // Obj4 + AssertLexEndFile(y); + } - // Handle empty objects, multiple levels of closing. - [TestMethod] - public void ReadEmptyObjects() - { - var text = + // Handle empty objects, multiple levels of closing. + [TestMethod] + public void ReadEmptyObjects() + { + var text = @"P0: =1 Obj1: Obj2: Obj3: Obj4: "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - - AssertLex("P0=1", y); - AssertLex("Obj1:", y); - AssertLex("Obj2:", y); - AssertLex("Obj3:", y); - AssertLexEndObj(y); // Obj3 - AssertLexEndObj(y); // Obj2 - AssertLexEndObj(y); // Obj1 - AssertLex("Obj4:", y); - AssertLexEndObj(y); // Obj4 - AssertLexEndFile(y); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr); + + AssertLex("P0=1", y); + AssertLex("Obj1:", y); + AssertLex("Obj2:", y); + AssertLex("Obj3:", y); + AssertLexEndObj(y); // Obj3 + AssertLexEndObj(y); // Obj2 + AssertLexEndObj(y); // Obj1 + AssertLex("Obj4:", y); + AssertLexEndObj(y); // Obj4 + AssertLexEndFile(y); + } - // Detect error case. Closing an object must still align to previous indent. - [TestMethod] - public void ReadEmptyObjectsError() - { - var text = + // Detect error case. Closing an object must still align to previous indent. + [TestMethod] + public void ReadEmptyObjectsError() + { + var text = @"P0: =1 Obj1: Obj2: ErrorObj3: "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); + var sr = new StringReader(text); + var y = new YamlLexer(sr); - AssertLex("P0=1", y); - AssertLex("Obj1:", y); - AssertLex("Obj2:", y); - AssertLexError(y); // Obj3 is at a bad indent. - } + AssertLex("P0=1", y); + AssertLex("Obj1:", y); + AssertLex("Obj2:", y); + AssertLexError(y); // Obj3 is at a bad indent. + } - [TestMethod] - public void ReadObject() - { - var text = + [TestMethod] + public void ReadObject() + { + var text = @"P0: =123 Obj1: P1a: =ABC @@ -405,164 +403,164 @@ public void ReadObject() P4a: =X P1b: =DEF "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - - AssertLex("P0=123", y); - AssertLex("Obj1:", y); - AssertLex("P1a=ABC", y); - AssertLex("Obj2:", y); - AssertLex("P2a=X", y); - AssertLex("P2b=Y", y); - AssertLex("P2c=Z", y); - AssertLexEndObj(y); // Obj2 - AssertLex("'Obj3:':", y); - AssertLex("P3a=X", y); - AssertLexEndObj(y); // Obj3 - AssertLex("'Ob\"j4':", y); - AssertLex("P4a=X", y); - AssertLexEndObj(y); // Obj4 - AssertLex("P1b=DEF", y); - AssertLexEndObj(y); // Obj1 - AssertLexEndFile(y); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr); - [TestMethod] - public void ReadComments() - { - var text = + AssertLex("P0=123", y); + AssertLex("Obj1:", y); + AssertLex("P1a=ABC", y); + AssertLex("Obj2:", y); + AssertLex("P2a=X", y); + AssertLex("P2b=Y", y); + AssertLex("P2c=Z", y); + AssertLexEndObj(y); // Obj2 + AssertLex("'Obj3:':", y); + AssertLex("P3a=X", y); + AssertLexEndObj(y); // Obj3 + AssertLex("'Ob\"j4':", y); + AssertLex("P4a=X", y); + AssertLexEndObj(y); // Obj4 + AssertLex("P1b=DEF", y); + AssertLexEndObj(y); // Obj1 + AssertLexEndFile(y); + } + + [TestMethod] + public void ReadComments() + { + var text = @"Obj1: # this starts on line 2, column 4 P1: =123 # comment2 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - - AssertLex("Obj1:", y); - AssertLex("P1=123", y); - AssertLexEndObj(y); // Obj1 - AssertLexEndFile(y); - - // Comments in yaml get stripped, but we get a warning and source pointer. - Assert.IsNotNull(y._commentStrippedWarning); - var loc = y._commentStrippedWarning.Value; - Assert.AreEqual(2, loc.StartLine); - Assert.AreEqual(4, loc.StartChar); - } + var sr = new StringReader(text); + var y = new YamlLexer(sr); + + AssertLex("Obj1:", y); + AssertLex("P1=123", y); + AssertLexEndObj(y); // Obj1 + AssertLexEndFile(y); + + // Comments in yaml get stripped, but we get a warning and source pointer. + Assert.IsNotNull(y._commentStrippedWarning); + var loc = y._commentStrippedWarning.Value; + Assert.AreEqual(2, loc.StartLine); + Assert.AreEqual(4, loc.StartChar); + } - const string expectedYaml = @"P1: =123 + const string expectedYaml = @"P1: =123 P2: = ""hello"" & ""world"" "; - [TestMethod] - public void ReadDict() - { - var reader = new StringReader(expectedYaml); + [TestMethod] + public void ReadDict() + { + var reader = new StringReader(expectedYaml); - var props = YamlConverter.Read(reader); - Assert.AreEqual(props.Count, 2); - Assert.AreEqual(props["P1"], "123"); - Assert.AreEqual(props["P2"], " \"hello\" & \"world\""); + var props = YamlConverter.Read(reader); + Assert.AreEqual(props.Count, 2); + Assert.AreEqual(props["P1"], "123"); + Assert.AreEqual(props["P2"], " \"hello\" & \"world\""); - } + } - [TestMethod] - public void ReadDictError() - { - var reader = new StringReader( + [TestMethod] + public void ReadDictError() + { + var reader = new StringReader( @"P1: sub1: 123"); // error, not supported objects - Assert.ThrowsException<InvalidOperationException>( - () => YamlConverter.Read(reader)); - } + Assert.ThrowsException<InvalidOperationException>( + () => YamlConverter.Read(reader)); + } - [TestMethod] - public void WriteDict() + [TestMethod] + public void WriteDict() + { + var writer = new StringWriter(); + + var d = new Dictionary<string, string> { - var writer = new StringWriter(); + {"P2", " \"hello\" & \"world\""}, + {"P1", "123" } + }; - var d = new Dictionary<string, string> - { - {"P2", " \"hello\" & \"world\""}, - {"P1", "123" } - }; - - YamlConverter.Write(writer, d); - - // order should be alphabetical - Assert.AreEqual(expectedYaml, writer.ToString()); - } + YamlConverter.Write(writer, d); + + // order should be alphabetical + Assert.AreEqual(expectedYaml, writer.ToString()); + } - #region Helpers - static void AssertLexEndFile(YamlLexer y) - { - AssertLex("<EndOfFile>", y); - } + #region Helpers + static void AssertLexEndFile(YamlLexer y) + { + AssertLex("<EndOfFile>", y); + } - static void AssertLexEndObj(YamlLexer y) - { - AssertLex("<EndObj>", y); - } + static void AssertLexEndObj(YamlLexer y) + { + AssertLex("<EndObj>", y); + } - static void AssertLexError(YamlLexer y) - { - var p = y.ReadNext(); - Assert.AreEqual(YamlTokenKind.Error, p.Kind); - } + static void AssertLexError(YamlLexer y) + { + var p = y.ReadNext(); + Assert.AreEqual(YamlTokenKind.Error, p.Kind); + } - static void AssertLex(string expected, YamlLexer y) - { - var p = y.ReadNext(); - Assert.AreEqual(NormNewlines(expected), p.ToString()); - } + static void AssertLex(string expected, YamlLexer y) + { + var p = y.ReadNext(); + Assert.AreEqual(NormNewlines(expected), p.ToString()); + } - static void AssertLex(string expected, YamlLexer y, string sourceSpan) - { - var p = y.ReadNext(); - Assert.AreEqual(expected, p.ToString()); - Assert.AreEqual(sourceSpan, p.Span.ToString()); - } + static void AssertLex(string expected, YamlLexer y, string sourceSpan) + { + var p = y.ReadNext(); + Assert.AreEqual(expected, p.ToString()); + Assert.AreEqual(sourceSpan, p.Span.ToString()); + } - private static YamlToken[] ReadAllTokens(string text) + private static YamlToken[] ReadAllTokens(string text) + { + var sr = new StringReader(text); + var y = new YamlLexer(sr); + + List<YamlToken> tokens = new List<YamlToken>(); + YamlToken token; + do { - var sr = new StringReader(text); - var y = new YamlLexer(sr); + token = y.ReadNext(); + tokens.Add(token); + Assert.AreNotEqual(YamlTokenKind.Error, token.Kind); - List<YamlToken> tokens = new List<YamlToken>(); - YamlToken token; - do - { - token = y.ReadNext(); - tokens.Add(token); - Assert.AreNotEqual(YamlTokenKind.Error, token.Kind); - - // Fragments are small. If we don't terminate, there's a parser bug. - Assert.IsTrue(tokens.Count < 100, "fragment failed to parse to EOF"); - } while (token.Kind != YamlTokenKind.EndOfFile); - return tokens.ToArray(); - } + // Fragments are small. If we don't terminate, there's a parser bug. + Assert.IsTrue(tokens.Count < 100, "fragment failed to parse to EOF"); + } while (token.Kind != YamlTokenKind.EndOfFile); + return tokens.ToArray(); + } - // Parse a single property "Foo" expression with YamlDotNet. - private static string ParseSinglePropertyViaYamlDotNot(string text) + // Parse a single property "Foo" expression with YamlDotNet. + private static string ParseSinglePropertyViaYamlDotNot(string text) + { + var deserializer = new DeserializerBuilder().Build(); + var o2 = (Dictionary<object, object>)deserializer.Deserialize(new StringReader(text)); + var val = (string)o2["Foo"]; + // Strip the '=' that we add. + if (val[0] == '=') { - var deserializer = new DeserializerBuilder().Build(); - var o2 = (Dictionary<object, object>)deserializer.Deserialize(new StringReader(text)); - var val = (string)o2["Foo"]; - // Strip the '=' that we add. - if (val[0] == '=') - { - val = val.Substring(1); - } - return val; + val = val.Substring(1); } + return val; + } - [TestMethod] - public void MissingClosingQuoteComponentError() - { - var text = + [TestMethod] + public void MissingClosingQuoteComponentError() + { + var text = @"Co'mp'1 As CanvasComp'onent: Fill: =RGBA(0, 0, 0, 0) Height: =640 @@ -572,17 +570,17 @@ public void MissingClosingQuoteComponentError() Y: =0 ZIndex: =1 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - y.IsComponent = true; + var sr = new StringReader(text); + var y = new YamlLexer(sr); + y.IsComponent = true; - AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteComponent); - } + AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteComponent); + } - [TestMethod] - public void MissingClosingQuoteFunctionNodeError() - { - var text = + [TestMethod] + public void MissingClosingQuoteFunctionNodeError() + { + var text = @"Comp1 As CanvasComponent: fun'c1(Parameter1 As String, Parameter2 As String): Parameter1: @@ -597,17 +595,17 @@ public void MissingClosingQuoteFunctionNodeError() Y: =0 ZIndex: =1 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); - y.IsComponent = true; + var sr = new StringReader(text); + var y = new YamlLexer(sr); + y.IsComponent = true; - AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteFunctionNode); - } + AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteFunctionNode); + } - [TestMethod] - public void MissingClosingQuoteTypeNodeError() - { - var text = + [TestMethod] + public void MissingClosingQuoteTypeNodeError() + { + var text = @"Scr'een1 As screen: Fill: =RGBA(0, 0, 0, 0) Height: =640 @@ -617,16 +615,16 @@ public void MissingClosingQuoteTypeNodeError() Y: =0 ZIndex: =1 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); + var sr = new StringReader(text); + var y = new YamlLexer(sr); - AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteTypeNode); - } + AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteTypeNode); + } - [TestMethod] - public void MissingClosingQuotePropertyError() - { - var text = + [TestMethod] + public void MissingClosingQuotePropertyError() + { + var text = @"Screen1 As screen: Fi'll: =RGBA(0, 0, 0, 0) Height: =640 @@ -636,30 +634,26 @@ public void MissingClosingQuotePropertyError() Y: =0 ZIndex: =1 "; - var sr = new StringReader(text); - var y = new YamlLexer(sr); + var sr = new StringReader(text); + var y = new YamlLexer(sr); - AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteProperty); - } + AssertLexErrorMessage(y, YamlLexer.MissingSingleQuoteProperty); + } - static void AssertLexErrorMessage(YamlLexer y, string expectedErrorMessage) + static void AssertLexErrorMessage(YamlLexer y, string expectedErrorMessage) + { + var p = y.ReadNext(); + while (p.Kind != YamlTokenKind.EndOfFile) { - var p = y.ReadNext(); - while (p.Kind != YamlTokenKind.EndOfFile) + if (p.Kind == YamlTokenKind.Error) { - if (p.Kind == YamlTokenKind.Error) - { - Assert.AreEqual(p.Value, expectedErrorMessage); - return; - } - p = y.ReadNext(); - } - Assert.Fail(); + Assert.AreEqual(p.Value, expectedErrorMessage); + return; + } + p = y.ReadNext(); } - - #endregion - + Assert.Fail(); } - + #endregion }