diff --git a/README.md b/README.md index edf0d5722..77c131341 100755 --- a/README.md +++ b/README.md @@ -48,16 +48,17 @@ Typewriter is a new solution for generating code files using the GraphODataTempl * **-g**, **-generationmode**: Specifies the generation mode. The values can be: `Full`, `Metadata`, or `Files`. `Full` (default) generation mode produces the output code files by cleaning the input metadata, parsing the documentation, and adding annotations before generating the output files. `Metadata` generation mode produces an output metadata file by cleaning metadata, documentation parsing, and adding documentation annotations. `Files` generation mode produces code files from an input metadata and bypasses the cleaning, documentation parsing, and adding documentation annotations. * **-f**, **-outputMetadataFileName**: The base output metadata filename. Only applicable for `-generationmode Metadata`. The default value is `cleanMetadataWithDescriptions` which is used with the value of the `-endpointVersion` to generate a metadata file named `cleanMetadataWithDescriptionsv1.0.xml`. * **-e**, **-endpointVersion**: The endpoint version used when naming a metadata file. Expected values are `v1.0` and `beta`. Only applicable for `-generationmode Metadata`. +* **-p**, **-properties**: Specify properties to support generation logic in the T4 templates. Properties must take the form of *key-string:value-string*. Multiple properties can be specified by setting a space in between property. The only property currently supported is the *php.namespace* property to specify the generated model file namespace. This property is optional. ### Example typewriter usage -#### Generate TypeScript typings from a CSDL (metadata) file without cleaning or annotating the CSDL. +#### Generate TypeScript typings from a CSDL (metadata) file without cleaning or annotating the CSDL. The output will go in to the `outputTypeScript` directory. `.\typewriter.exe -v Info -m D:\cleanMetadataWithDescriptions_v10.xml -o outputTypeScript -l TypeScript -g Files` -#### Clean and annotate a metadata file with documentation annotations sourced from the documentation repo +#### Clean and annotate a metadata file with documentation annotations sourced from the documentation repo The output metadata file will go in to the `output2` directory. The output metadata file will be named `cleanMetadataWithDescriptionsv1.0.xml` based on the default values. @@ -144,7 +145,7 @@ The type of template. #### Template Name -To set the name of the template using the `Name` format string. You can insert ``, ``, ``, and `` the values will be replaced by the names of the corresponding object. If you insert an item that doesn't exist it will be replaced with an empty string. +To set the name of the template using the `Name` format string. You can insert ``, ``, ``, and `` the values will be replaced by the names of the corresponding object. If you insert an item that doesn't exist it will be replaced with an empty string. Note: You can also set the template name from inside the template by : `host.SetTemplateName("foo");` #### Template Editing @@ -174,9 +175,9 @@ There are currently several steps we take to form the metadata into one that wil - Remove HasStream properties from ```onenotePage``` and ```onenoteEntityBaseModel``` - Add ```ContainsTarget="true"``` to navigation properties that do not have a corresponding EntitySet. This currently applies to navigation properties that contain plannerBucket, plannerTask, plannerPlan, and plannerDelta. - Add long descriptions to types and properties from [docs](https://developer.microsoft.com/en-us/graph/docs/concepts/overview) - + In order to build against metadata other than that stored in the [metadata](https://github.com/microsoftgraph/MSGraph-SDK-Code-Generator/tree/master/metadata) directory, you will need to perform the first four on this list. - + ## Contributing Before we can accept your pull request, you'll need to electronically complete Microsoft's [Contributor License Agreement](https://cla.microsoft.com/). If you've done this for other Microsoft projects, then you're already covered. diff --git a/Templates/PHP/Model/ComplexType.php.tt b/Templates/PHP/Model/ComplexType.php.tt index d7e149d58..83e7d3bf9 100644 --- a/Templates/PHP/Model/ComplexType.php.tt +++ b/Templates/PHP/Model/ComplexType.php.tt @@ -5,14 +5,24 @@ CustomT4Host host = (CustomT4Host) Host; OdcmModel model = host.CurrentModel; CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter; +TemplateWriterSettings settings = ConfigurationService.Settings; OdcmClass complex = (OdcmClass)host.CurrentType; String complexName = complex.Name.SanitizeEntityName(); +string targetNamespace = @"Microsoft\Graph\Model"; String complexBaseName = ""; if (complex.Base != null) complexBaseName = complex.Base.Name.SanitizeEntityName(); + +// TemplateWriterSettings.Properties are set at the Typewriter command line. Check the command line +// documentation for more information on how the TemplateWriterSettings.Properties is used. +if (settings.Properties.ContainsKey("php.namespace")) +{ + targetNamespace = settings.Properties["php.namespace"]; +} + #> <#=writer.WriteHeader(writer.GetDocBlock(complexName.ToCheckedCase()))#> -namespace Microsoft\Graph\Model; +namespace <#=targetNamespace#>; <#=writer.GetClassBlock(complexName.ToCheckedCase().ToString(), "Model")#> <# if (complex.Base != null) { @@ -118,7 +128,7 @@ foreach(var property in complex.Properties.Where(prop => prop.Type.GetTypeString public function get<#=propertyName.ToCheckedCase()#>() { if (array_key_exists("<#=property.Name.ToCamelize()#>", $this->_propDict)) { - if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "Microsoft\Graph\Model\<#=property.Type.GetTypeString()#>")) { + if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "<#=targetNamespace#>\<#=property.Type.GetTypeString()#>")) { return $this->_propDict["<#=property.Name.ToCamelize()#>"]; } else { <# if (property.Type.GetTypeString() == "\\GuzzleHttp\\Psr7\\Stream") { #> diff --git a/Templates/PHP/Model/EntityType.php.tt b/Templates/PHP/Model/EntityType.php.tt index 72da51b20..e66df0e6b 100644 --- a/Templates/PHP/Model/EntityType.php.tt +++ b/Templates/PHP/Model/EntityType.php.tt @@ -8,12 +8,22 @@ CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter; OdcmClass entity = host.CurrentType.AsOdcmClass(); TemplateWriterSettings settings = ConfigurationService.Settings; String entityName = entity.Name.SanitizeEntityName(); +string targetNamespace = @"Microsoft\Graph\Model"; String entityBaseName = ""; + if (entity.Base != null) entityBaseName = entity.Base.Name.SanitizeEntityName(); + +// TemplateWriterSettings.Properties are set at the Typewriter command line. Check the command line +// documentation for more information on how the TemplateWriterSettings.Properties is used. +if (settings.Properties.ContainsKey("php.namespace")) +{ + targetNamespace = settings.Properties["php.namespace"]; +} + #> <#=writer.WriteHeader(writer.GetDocBlock(entityName.ToCheckedCase()))#> -namespace Microsoft\Graph\Model; +namespace <#=targetNamespace#>; <#=writer.GetClassBlock(entityName.ToCheckedCase().ToString(), "Model")#> <# @@ -122,7 +132,7 @@ foreach(var property in entity.Properties.Where(prop => prop.Type.GetTypeString( if (property.Type.GetTypeString()[0] == '\\') { #> if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "<#=property.Type.GetTypeString()#>")) { <# } else { #> - if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "Microsoft\Graph\Model\<#=property.Type.GetTypeString()#>")) { + if (is_a($this->_propDict["<#=property.Name.ToCamelize()#>"], "<#=targetNamespace#>\<#=property.Type.GetTypeString()#>")) { <# } #> return $this->_propDict["<#=property.Name.ToCamelize()#>"]; } else { diff --git a/Templates/PHP/Model/EnumType.php.tt b/Templates/PHP/Model/EnumType.php.tt index caad45dd6..ee3096e0f 100644 --- a/Templates/PHP/Model/EnumType.php.tt +++ b/Templates/PHP/Model/EnumType.php.tt @@ -2,14 +2,23 @@ <#@ template debug="true" hostspecific="true" language="C#" #> <#@ output extension="\\" #> <# -CustomT4Host host = (CustomT4Host) Host; -OdcmModel model = host.CurrentModel; -CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter; +CustomT4Host host = (CustomT4Host) Host; +OdcmModel model = host.CurrentModel; +CodeWriterPHP writer = (CodeWriterPHP) host.CodeWriter; +TemplateWriterSettings settings = ConfigurationService.Settings; +string targetNamespace = @"Microsoft\Graph\Model"; +var enumT = host.CurrentType.AsOdcmEnum(); + +// TemplateWriterSettings.Properties are set at the Typewriter command line. Check the command line +// documentation for more information on how the TemplateWriterSettings.Properties is used. +if (settings.Properties.ContainsKey("php.namespace")) +{ + targetNamespace = settings.Properties["php.namespace"]; +} -var enumT = host.CurrentType.AsOdcmEnum(); #> <#=writer.WriteHeader(writer.GetDocBlock(enumT.Name.ToCheckedCase()))#> -namespace Microsoft\Graph\Model; +namespace <#=targetNamespace#>; use Microsoft\Graph\Core\Enum; diff --git a/src/GraphODataTemplateWriter/Settings/ConfigurationService.cs b/src/GraphODataTemplateWriter/Settings/ConfigurationService.cs index 99a1e3e41..0bb408899 100644 --- a/src/GraphODataTemplateWriter/Settings/ConfigurationService.cs +++ b/src/GraphODataTemplateWriter/Settings/ConfigurationService.cs @@ -3,22 +3,46 @@ namespace Microsoft.Graph.ODataTemplateWriter.Settings { using System; + using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using Vipr.Core; + /// + /// The ConfigurationService configures the template writer with the target language and + /// other properties used to direct how the code files are generated. + /// public static class ConfigurationService { private static IConfigurationProvider _configurationProvider; private static TemplateWriterSettings templateWriterSettings = null; private static string targetLanguage = null; - public static void Initialize(IConfigurationProvider configurationProvider, string targetLanguage = null) + private static Dictionary properties = null; + + public static void Initialize(IConfigurationProvider configurationProvider, string targetLanguage = null, IEnumerable properties = null) { _configurationProvider = configurationProvider; if (!String.IsNullOrEmpty(targetLanguage)) { ConfigurationService.targetLanguage = targetLanguage; } + if (properties != null) + { + Dictionary propertyDictionary = new Dictionary(); + foreach (string property in properties) + { + string[] props = property.Split(':'); + + if (props.Length != 2) + { + throw new ArgumentException("A property was set in a unexpected form from the typewriter commandline.", "-p -properties"); + } + + propertyDictionary.Add(props[0],props[1]); + } + + ConfigurationService.properties = propertyDictionary; + } } private static TemplateWriterSettings LoadSettingsForLanguage() @@ -29,6 +53,11 @@ private static TemplateWriterSettings LoadSettingsForLanguage() { mainTWS.TargetLanguage = targetLanguage; } + if (properties != null) + { + mainTWS.Properties = properties; + } + TemplateWriterSettings.mainSettingsObject = mainTWS; @@ -72,7 +101,7 @@ public static TemplateWriterSettings Settings { get { - if (templateWriterSettings == null) + if (templateWriterSettings == null || templateWriterSettings.TargetLanguage != ConfigurationService.targetLanguage) { templateWriterSettings = _configurationProvider != null ? LoadSettingsForLanguage() diff --git a/src/GraphODataTemplateWriter/Settings/TemplateWriterSettings.cs b/src/GraphODataTemplateWriter/Settings/TemplateWriterSettings.cs index 2e03a6711..521c277b0 100644 --- a/src/GraphODataTemplateWriter/Settings/TemplateWriterSettings.cs +++ b/src/GraphODataTemplateWriter/Settings/TemplateWriterSettings.cs @@ -101,6 +101,12 @@ public Dictionary>> TemplateMapping private readonly List> templateConfiguration; + /// + /// A dictionary of strings that represent a property. These properties can be provided from the command line + /// and used in the templates. + /// + public Dictionary Properties { get; set; } + /// /// The dictionary created by combining the "Shared" and current language mapping. /// diff --git a/src/GraphODataTemplateWriter/TemplateProcessor/TemplateProcessor.cs b/src/GraphODataTemplateWriter/TemplateProcessor/TemplateProcessor.cs index e8be14654..732c6d7cc 100644 --- a/src/GraphODataTemplateWriter/TemplateProcessor/TemplateProcessor.cs +++ b/src/GraphODataTemplateWriter/TemplateProcessor/TemplateProcessor.cs @@ -25,14 +25,11 @@ public class TemplateProcessor : ITemplateProcessor protected static CustomT4Host Host(ITemplateInfo templateInfo, string templatesDirectory, OdcmObject odcmObject, OdcmModel odcmModel) { - if (_host == null) - { - _host = new CustomT4Host(templateInfo, templatesDirectory, odcmObject, odcmModel); - } - else - { - _host.Reset(templateInfo, templatesDirectory, odcmObject, odcmModel); - } + // Need to always set the host. Typically, this is run against a single platform when generating codefiels. + // In test cases, we need to target multiple platforms. Since much of this code is static, we need to make sure + // reset the information provided to the template processor. This change fixes a bug when targeting + // multiple platforms in a test. + _host = new CustomT4Host(templateInfo, templatesDirectory, odcmObject, odcmModel); return _host; } diff --git a/src/GraphODataTemplateWriter/TemplateProcessor/TemplateWriter.cs b/src/GraphODataTemplateWriter/TemplateProcessor/TemplateWriter.cs index 597ca59d2..255a0025e 100644 --- a/src/GraphODataTemplateWriter/TemplateProcessor/TemplateWriter.cs +++ b/src/GraphODataTemplateWriter/TemplateProcessor/TemplateWriter.cs @@ -24,6 +24,25 @@ public class TemplateWriter : IConfigurable, IOdcmWriter private static Logger logger = LogManager.GetCurrentClassLogger(); private string pathWriterClassNameFormatString; + + private string targetLanguage; + private IEnumerable properties; + + public TemplateWriter() + { + } + + public TemplateWriter(string targetLanguage) + { + this.targetLanguage = targetLanguage; + } + + public TemplateWriter(string targetLanguage, IEnumerable properties) + { + this.targetLanguage = targetLanguage; + this.properties = properties; + } + private string PathWriterClassNameFormatString { get { @@ -76,22 +95,10 @@ IEnumerable ProcessTemplates() } } - private string targetLanguage; - - public TemplateWriter() - { - - } - - public TemplateWriter(string targetLanguage) - { - this.targetLanguage = targetLanguage; - } - // IConfigurationProvider public void SetConfigurationProvider(IConfigurationProvider configurationProvider) { - ConfigurationService.Initialize(configurationProvider, this.targetLanguage); + ConfigurationService.Initialize(configurationProvider, this.targetLanguage, this.properties); FileNameCasing nameCasing; if(!Enum.TryParse(ConfigurationService.Settings.DefaultFileCasing, out nameCasing)) { diff --git a/src/Typewriter/Generator.cs b/src/Typewriter/Generator.cs index 40684d53f..40f237903 100644 --- a/src/Typewriter/Generator.cs +++ b/src/Typewriter/Generator.cs @@ -18,7 +18,7 @@ internal static class Generator /// The options bag static internal void GenerateFiles(string csdlContents, Options options) { - var filesToWrite = MetadataToClientSource(csdlContents, options.Language); + var filesToWrite = MetadataToClientSource(csdlContents, options.Language, options.Properties); FileWriter.WriteAsync(filesToWrite, options.Output); } @@ -65,13 +65,13 @@ static private string CleanMetadata(string csdlContents, Options options) /// The EDMX file as a string. /// Specifies the target language. Possible values are csharp, php, etc. /// - static private IEnumerable MetadataToClientSource(string edmxString, string targetLanguage) + static private IEnumerable MetadataToClientSource(string edmxString, string targetLanguage, IEnumerable properties) { if (String.IsNullOrEmpty(edmxString)) throw new ArgumentNullException("edmxString", "The EDMX file string contains no content."); var reader = new OdcmReader(); - var writer = new TemplateWriter(targetLanguage); + var writer = new TemplateWriter(targetLanguage, properties); writer.SetConfigurationProvider(new ConfigurationProvider()); var model = reader.GenerateOdcmModel(new List { new TextFile("$metadata", edmxString) }); diff --git a/src/Typewriter/Options.cs b/src/Typewriter/Options.cs index 38ccde493..a8ed3ec1b 100644 --- a/src/Typewriter/Options.cs +++ b/src/Typewriter/Options.cs @@ -1,4 +1,5 @@ using CommandLine; +using System.Collections.Generic; using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("GraphODataTemplateWriter.Test")] @@ -60,5 +61,10 @@ public class Options [Option('e', "endpointVersion", Default = "v1.0", HelpText = "The endpoint version. Expected values are 'v1.0' and 'beta'. Only applicable for GenerationMode.Metadata.")] public string EndpointVersion { get; set; } + + [Option('p', "properties", HelpText = "A space separated list of properties in the form of 'key:value'. These properties can be accessed in the " + + "templates from the TemplateWriterSettings object returned by ConfigurationService.Settings. The suggested convention for specifying a key should be " + + "the targeted template language name and the property name. For example, php.namespace:Microsoft\\Graph\\Beta\\Model would be a property to be consumed in the PHP templates.")] + public IEnumerable Properties { get; set; } } } \ No newline at end of file diff --git a/test/Typewriter.Test/GeneratorTests.cs b/test/Typewriter.Test/GeneratorTests.cs deleted file mode 100644 index 9c58b5628..000000000 --- a/test/Typewriter.Test/GeneratorTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.IO; - -namespace Typewriter.Test -{ - [TestClass] - [Ignore] // Work is needed to generate from the test project. - public class GeneratorTests - { - public string testMetadata; - - /// - /// Load metadata from file into a string so we can validate MetadataPreprocessor. - /// - [TestInitialize] - public void Initialize() - { - testMetadata = Typewriter.Test.Properties.Resources.dirtyMetadata; - } - - [TestMethod] - public void GenerateFilesTest() - { - const string outputDirectory = "output"; - - Options options = new Options() - { - Output = outputDirectory, - Language = "TypeScript" - }; - - Generator.GenerateFiles(testMetadata, options); - - FileInfo fileInfo = new FileInfo(outputDirectory + @"\com\microsoft\graph\src\Microsoft-graph.d.ts"); - Assert.IsTrue(fileInfo.Exists); - } - } -} diff --git a/test/Typewriter.Test/Given_a_valid_metadata_file_to_Typewriter.cs b/test/Typewriter.Test/Given_a_valid_metadata_file_to_Typewriter.cs new file mode 100644 index 000000000..a34dbc37e --- /dev/null +++ b/test/Typewriter.Test/Given_a_valid_metadata_file_to_Typewriter.cs @@ -0,0 +1,76 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using System.IO; + +namespace Typewriter.Test +{ + /// + /// End to end tests that check the results of running Typewriter from the CLI. + /// + [TestClass] + public class Given_a_valid_metadata_file_to_Typewriter + { + public string testMetadata; + // The second segment is generated from the namespace in the target metadata file. + public string generatedOutputUrl = @"\com\microsoft\Graph"; + + /// + /// Load metadata from file into a string so we can validate MetadataPreprocessor. + /// + [TestInitialize] + public void Initialize() + { + testMetadata = Typewriter.Test.Properties.Resources.dirtyMetadata; + } + + [TestMethod] + public void It_generates_a_typings_file() + { + const string outputDirectory = "output"; + + Options options = new Options() + { + Output = outputDirectory, + Language = "TypeScript" + }; + + Generator.GenerateFiles(testMetadata, options); + + FileInfo fileInfo = new FileInfo(outputDirectory + generatedOutputUrl + @"\src\Microsoft-graph.d.ts"); + Assert.IsTrue(fileInfo.Exists, $"Expected {fileInfo.FullName}. File was not found."); + } + + [TestMethod] + public void It_generates_PHP_models_with_a_property() + { + const string testNamespace = "TEST.NAMESPACE"; + const string outputDirectory = "output"; + + Options options = new Options() + { + Output = outputDirectory, + Language = "PHP", + Properties = new List() { $"php.namespace:{testNamespace}" }, + GenerationMode = GenerationMode.Files + }; + + Generator.GenerateFiles(testMetadata, options); + + FileInfo fileInfo = new FileInfo(outputDirectory + generatedOutputUrl + @"\Model\Entity.php"); + Assert.IsTrue(fileInfo.Exists, $"Expected: {fileInfo.FullName}. File was not found."); + + // Check that the namespace applied at the CLI was added to the document. + IEnumerable lines = File.ReadLines(fileInfo.FullName); + bool isExpectedNamespaceSet = false; + foreach (var line in lines) + { + if (line.Contains($"namespace {testNamespace}")) + { + isExpectedNamespaceSet = true; + break; + } + } + Assert.IsTrue(isExpectedNamespaceSet, $"The expected namespace, {testNamespace}, was not set in the generated test file."); + } + } +} diff --git a/test/Typewriter.Test/MetadataPreprocessorTests.cs b/test/Typewriter.Test/Given_a_valid_metadata_file_to_metadata_preprocessor.cs similarity index 90% rename from test/Typewriter.Test/MetadataPreprocessorTests.cs rename to test/Typewriter.Test/Given_a_valid_metadata_file_to_metadata_preprocessor.cs index cae762a39..35fbfb221 100644 --- a/test/Typewriter.Test/MetadataPreprocessorTests.cs +++ b/test/Typewriter.Test/Given_a_valid_metadata_file_to_metadata_preprocessor.cs @@ -5,7 +5,7 @@ namespace Typewriter.Test { [TestClass] - public class MetadataPreprocessorTests + public class Given_a_valid_metadata_file_to_metadata_preprocessor { public string testMetadata; public XDocument testXMetadata; @@ -22,7 +22,7 @@ public void Initialize() } [TestMethod] - public void RemoveHasStreamTest() + public void It_removes_the_HasStream_attribute() { var entityToProcess = "onenotePage"; @@ -44,14 +44,14 @@ public void RemoveHasStreamTest() } [TestMethod] - public void AddContainsTargetTest() + public void It_adds_the_ContainsTarget_attribute() { var navPropTypeToProcess = "plannerPlan"; bool doesntContainTargetBefore = MetadataPreprocessor.GetXMetadata().Descendants() .Where(x => x.Name.LocalName == "NavigationProperty") .Where(x => x.Attribute("ContainsTarget") == null || x.Attribute("ContainsTarget").Value.Equals("false")) - .Where(x => x.Attribute("Type").Value == "Collection(microsoft.graph." + navPropTypeToProcess + ")") + .Where(x => x.Attribute("Type").Value == $"Collection(microsoft.graph.{navPropTypeToProcess})") .Any(); Assert.IsTrue(doesntContainTargetBefore, "Expected: ContainsTarget is false. Actual: ContainsTarget is true"); @@ -62,14 +62,14 @@ public void AddContainsTargetTest() .Where(x => x.Name.LocalName == "NavigationProperty") .Where(x => x.Attribute("ContainsTarget") != null) .Where(x => x.Attribute("ContainsTarget").Value == "true") - .Where(x => x.Attribute("Type").Value == "Collection(microsoft.graph." + navPropTypeToProcess + ")") + .Where(x => x.Attribute("Type").Value == $"Collection(microsoft.graph.{navPropTypeToProcess})") .Any(); Assert.IsTrue(doesContainTargetAfter, "Expected: ContainsTarget is true. Actual: ContainsTarget is false"); } [TestMethod] - public void RemoveCapabilityAnnotationsTest() + public void It_removes_capability_annotations() { bool hasCapabilityAnnotationsBefore = MetadataPreprocessor.GetXMetadata().Descendants() .Where(x => (string)x.Name.LocalName == "Annotation") @@ -86,7 +86,7 @@ public void RemoveCapabilityAnnotationsTest() } [TestMethod] - public void AddLongDescriptionToThumbnailTest() + public void It_adds_long_description_to_thumbnail() { XElement thumbnailComplexTypeBefore = MetadataPreprocessor.GetXMetadata().Descendants() .Where(x => (string)x.Name.LocalName == "ComplexType") diff --git a/test/Typewriter.Test/Resources/dirtyMetadata.xml b/test/Typewriter.Test/Resources/dirtyMetadata.xml index dffad309d..3a6b2de10 100644 --- a/test/Typewriter.Test/Resources/dirtyMetadata.xml +++ b/test/Typewriter.Test/Resources/dirtyMetadata.xml @@ -1,26 +1,26 @@  - + - - - - - - - + + + + + + + - - + + - - + + @@ -29,7 +29,7 @@ - + @@ -37,9 +37,9 @@ - - - + + + diff --git a/test/Typewriter.Test/Typewriter.Test.csproj b/test/Typewriter.Test/Typewriter.Test.csproj index 52c743338..0dd923c38 100644 --- a/test/Typewriter.Test/Typewriter.Test.csproj +++ b/test/Typewriter.Test/Typewriter.Test.csproj @@ -49,14 +49,14 @@ - + True True Resources.resx - +