diff --git a/src/Microsoft.OData.Cli/ODataCliFileHandler.cs b/src/Microsoft.OData.Cli/ODataCliFileHandler.cs index 7ff2b38a..cd9e1719 100644 --- a/src/Microsoft.OData.Cli/ODataCliFileHandler.cs +++ b/src/Microsoft.OData.Cli/ODataCliFileHandler.cs @@ -91,16 +91,16 @@ public Task AddFileAsync(string fileName, string targetPath, ODataFileOp /// Emits the container property attribute. The lastest version gets installed. so we'll always emit this property. /// /// A bool indicating whether to emit the container property or not - public bool EmitContainerPropertyAttribute() + public Task EmitContainerPropertyAttributeAsync() { - return true; + return Task.FromResult(true); } /// /// Sets the CSDL file as an embedded resource /// /// The name of the file to set as embedded resource - public void SetFileAsEmbeddedResource(string fileName) + public Task SetFileAsEmbeddedResourceAsync(string fileName) { if (this.project != null) { @@ -111,6 +111,8 @@ public void SetFileAsEmbeddedResource(string fileName) ProjectHelper.AddProjectItem(this.project, Constants.EmbeddedResourceTag, fileName); } } + + return Task.CompletedTask; } } } diff --git a/src/Microsoft.OData.CodeGen/CodeGeneration/V4CodeGenDescriptor.cs b/src/Microsoft.OData.CodeGen/CodeGeneration/V4CodeGenDescriptor.cs index 42474e6c..bc32ef2c 100644 --- a/src/Microsoft.OData.CodeGen/CodeGeneration/V4CodeGenDescriptor.cs +++ b/src/Microsoft.OData.CodeGen/CodeGeneration/V4CodeGenDescriptor.cs @@ -35,13 +35,13 @@ public V4CodeGenDescriptor(IFileHandler fileHandler, IMessageLogger logger, IPac public override async Task AddNugetPackagesAsync() { - await MessageLogger.WriteMessageAsync(LogMessageCategory.Information, "Adding Nuget Packages...").ConfigureAwait(false); + await MessageLogger.WriteMessageAsync(LogMessageCategory.Information, "Adding Nuget Packages..."); foreach (var nugetPackage in Common.Constants.V4NuGetPackages) - await PackageInstaller.CheckAndInstallNuGetPackageAsync(Common.Constants.NuGetOnlineRepository, nugetPackage).ConfigureAwait(false); + await PackageInstaller.CheckAndInstallNuGetPackageAsync(Common.Constants.NuGetOnlineRepository, nugetPackage); - await MessageLogger.WriteMessageAsync(LogMessageCategory.Information, "Nuget Packages were installed.").ConfigureAwait(false); + await MessageLogger.WriteMessageAsync(LogMessageCategory.Information, "Nuget Packages were installed."); } public override async Task AddGeneratedClientCodeAsync(string metadata, string outputDirectory, LanguageOption languageOption, ServiceConfiguration serviceConfiguration) @@ -96,7 +96,7 @@ private async Task AddT4FileAsync(string metadata, string outputDirectory, Langu await FileHandler.AddFileAsync(csdlTempFile, metadataFile, new ODataFileOptions { SuppressOverwritePrompt = true }); - FileHandler.SetFileAsEmbeddedResource(csdlFileName); + await FileHandler.SetFileAsEmbeddedResourceAsync(csdlFileName); var t4TempFile = Path.GetTempFileName(); @@ -193,28 +193,31 @@ private async Task AddGeneratedCodeAsync(string metadata, string outputDirectory var metadataFile = Path.Combine(referenceFolder, csdlFileName); await FileHandler.AddFileAsync(tempFile, metadataFile, new ODataFileOptions { SuppressOverwritePrompt = true }); - FileHandler.SetFileAsEmbeddedResource(csdlFileName); - t4CodeGenerator.EmitContainerPropertyAttribute = FileHandler.EmitContainerPropertyAttribute(); + await FileHandler.SetFileAsEmbeddedResourceAsync(csdlFileName); + t4CodeGenerator.EmitContainerPropertyAttribute = await FileHandler.EmitContainerPropertyAttributeAsync(); t4CodeGenerator.MetadataFilePath = metadataFile; t4CodeGenerator.MetadataFileRelativePath = csdlFileName; using (StreamWriter writer = File.CreateText(tempFile)) { - await writer.WriteAsync(t4CodeGenerator.TransformText()); + await writer.WriteAsync(await t4CodeGenerator.TransformTextAsync()); await writer.FlushAsync(); if (t4CodeGenerator.Errors != null && t4CodeGenerator.Errors.Count > 0) { foreach (var err in t4CodeGenerator.Errors) { - await MessageLogger.WriteMessageAsync(LogMessageCategory.Warning, err.ToString()).ConfigureAwait(false); + await MessageLogger.WriteMessageAsync(LogMessageCategory.Warning, err.ToString()); } } } var outputFile = Path.Combine(referenceFolder, $"{this.GeneratedFileNamePrefix(serviceConfiguration.GeneratedFileNamePrefix)}{(languageOption == LanguageOption.GenerateCSharpCode ? ".cs" : ".vb")}"); await FileHandler.AddFileAsync(tempFile, outputFile, new ODataFileOptions { SuppressOverwritePrompt = true }); - t4CodeGenerator.MultipleFilesManager?.GenerateFiles(serviceConfiguration.GenerateMultipleFiles, FileHandler, MessageLogger, referenceFolder, true, serviceConfiguration.OpenGeneratedFilesInIDE); + if (t4CodeGenerator.MultipleFilesManager != null) + { + await t4CodeGenerator.MultipleFilesManager?.CopyGeneratedFilesAsync(serviceConfiguration.GenerateMultipleFiles, FileHandler, MessageLogger, referenceFolder, true, serviceConfiguration.OpenGeneratedFilesInIDE); + } await MessageLogger.WriteMessageAsync(LogMessageCategory.Information, "Client Proxy for OData V4 was generated."); } } diff --git a/src/Microsoft.OData.CodeGen/FileHandling/IFileHandler.cs b/src/Microsoft.OData.CodeGen/FileHandling/IFileHandler.cs index 9c9ad739..5f313079 100644 --- a/src/Microsoft.OData.CodeGen/FileHandling/IFileHandler.cs +++ b/src/Microsoft.OData.CodeGen/FileHandling/IFileHandler.cs @@ -26,12 +26,12 @@ public interface IFileHandler /// Sets a file as an embedded resource /// /// The name of the file to set as an embedded resource - void SetFileAsEmbeddedResource(string fileName); + Task SetFileAsEmbeddedResourceAsync(string fileName); /// /// Emits container property attribute /// /// - bool EmitContainerPropertyAttribute(); + Task EmitContainerPropertyAttributeAsync(); } } diff --git a/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenFilesManager.ttinclude b/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenFilesManager.ttinclude index 9f183ad6..9e504b7b 100644 --- a/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenFilesManager.ttinclude +++ b/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenFilesManager.ttinclude @@ -18,6 +18,7 @@ #><#@ import namespace="System.IO" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Text" +#><#@ import namespace="System.Threading.Tasks" #><#@ import namespace="System.Security" #><#@ import namespace="System.Xml.Linq" #><#@ import namespace="Microsoft.OData.CodeGen.Logging" @@ -34,18 +35,21 @@ public class FilesManager { /// Creates an instance of the FilesManager. The object used to generate and manage /// multiple source files. /// - private class Block { - + internal class Block + { /// Name of the block. public string Name; + /// Temporary file path of the block. + public string TemporaryFilePath; + /// The line in the template from which the block starts. public int Start; /// Length of the block. public int Length; - /// Block currently being processed. + /// Block currently being processed. public bool IsContainer; } @@ -53,7 +57,7 @@ public class FilesManager { private Block _currentBlock; /// A list of all the blocks of texts to be used to generate multiple files. - private List _files = new List(); + internal List files = new List(); /// A block describing the footer of all files. private Block _footer = new Block(); @@ -61,8 +65,6 @@ public class FilesManager { /// A block describing the header of all files. private Block _header = new Block(); - /// A list of file names to be generated. - protected List _generatedFileNames = new List(); /// Contains generated text. public StringBuilder Template @@ -84,6 +86,8 @@ public class FilesManager { CurrentBlock = new Block { Name = name, IsContainer = isContainer}; } + public int FileCount => files.Count; + /// /// Marks the start of the footer for all files. /// @@ -115,7 +119,7 @@ public class FilesManager { if (CurrentBlock != _header && CurrentBlock != _footer) { - _files.Add(CurrentBlock); + files.Add(CurrentBlock); } _currentBlock = null; @@ -126,43 +130,58 @@ public class FilesManager { /// /// If true the function is executed and multiple files generated /// otherwise only a single file is generated. - [SecurityCritical] - public virtual void GenerateFiles(bool split, IFileHandler handlerHelper, IMessageLogger logger, string referenceFolder, bool fileCreated, bool OpenGeneratedFilesInIDE) + public virtual async Task GenerateFilesAsync(bool split) { if (split) { + var tasks = new List(); EndBlock(); string headerText = Template.ToString(_header.Start, _header.Length); string footerText = Template.ToString(_footer.Start, _footer.Length); - string outputPath =""; - - outputPath = Path.GetTempPath(); - - _files.Reverse(); - - foreach(Block block in _files) + var outputPath = Path.GetTempPath(); + int length = files.Count; + for (int i = length; i > 0; i--) { - if(block.IsContainer) continue; + Block block = files[i - 1]; + if (block.IsContainer) continue; string fileName = Path.Combine(outputPath, block.Name); + string content = headerText + Template.ToString(block.Start, block.Length) + footerText; + tasks.Add(CreateFileAsync(fileName, content)); + block.TemporaryFilePath = fileName; + Template.Remove(block.Start, block.Length); + } + + await Task.WhenAll(tasks); + } + } - if(fileCreated) - { - string outputFile = Path.Combine(referenceFolder, block.Name); - bool fileExists = File.Exists(outputFile); - handlerHelper.AddFileAsync(fileName, outputFile, new ODataFileOptions { OpenOnComplete = OpenGeneratedFilesInIDE, SuppressOverwritePrompt = true }).ContinueWith( - async _ => - { - await logger?.WriteMessageAsync(LogMessageCategory.Information, - "\"{0}\" has been {1}.", new FileInfo(fileName).Name, fileExists ? "updated" : "added"); - }, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously); - } - else - { - string content = headerText + Template.ToString(block.Start, block.Length) + footerText; - _generatedFileNames.Add(fileName); - CreateFile(fileName, content); - Template.Remove(block.Start, block.Length); - } + /// + /// Copies the generated file asyncronously using the file handler. + /// + /// If true the function is executed and multiple files generated otherwise only a single file is generated. + public virtual async Task CopyGeneratedFilesAsync(bool split, IFileHandler handlerHelper, IMessageLogger logger, string referenceFolder, bool fileCreated, bool OpenGeneratedFilesInIDE) + { + if (split) + { + int length = files.Count; + await logger?.WriteMessageAsync(LogMessageCategory.Information, "Adding {0} Generated files to the project. This may take a while!", length); + for (int i = length; i > 0; i--) + { + Block block = files[i - 1]; + if (block.IsContainer) continue; + string fileName = block.TemporaryFilePath; + if (!File.Exists(fileName)) continue; + + string outputFile = Path.Combine(referenceFolder, block.Name); + bool fileExists = File.Exists(outputFile); + + await handlerHelper.AddFileAsync(fileName, outputFile, new ODataFileOptions { OpenOnComplete = OpenGeneratedFilesInIDE, SuppressOverwritePrompt = true }) + .ContinueWith( + async _ => + { + await logger?.WriteMessageAsync(LogMessageCategory.Information, + "\"{0}\" has been {1}.", block.Name, fileExists ? "updated" : "added"); + }, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously); } } } @@ -172,12 +191,15 @@ public class FilesManager { /// /// Name of the file to be created /// Content of the file to be created - protected virtual void CreateFile(string fileName, string content) + protected virtual async Task CreateFileAsync(string fileName, string content) { - if (IsFileContentDifferent(fileName, content)) + using (FileStream fileStream = File.OpenWrite(fileName)) + using (var writer = new StreamWriter(fileStream)) { - File.WriteAllText(fileName, content); - } + // Truncates the file so if it exists the older content is overwritten. + fileStream.SetLength(0); + await writer.WriteAsync(content).ConfigureAwait(false); + } } public virtual string GetCustomToolNamespace(string fileName) @@ -193,17 +215,6 @@ public class FilesManager { } } - /// - /// checks if the generated content is different from the existing content. - /// - /// Name of the existing file - /// Content of existing file - /// true if the file content is different - protected bool IsFileContentDifferent(string fileName, string newContent) - { - return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent); - } - /// /// FilesManager constructor. Initializes the template variable. /// @@ -235,30 +246,6 @@ public class FilesManager { private class VSManager : FilesManager { - /// - ///Creates a file with the name and content . - /// - /// Name of the file to be created - /// Content of the file to be created - protected override void CreateFile(string fileName, string content) - { - if (IsFileContentDifferent(fileName, content)) - { - File.WriteAllText(fileName, content); - } - } - - /// - /// Generates multiple files depending on the number of blocks. - /// - /// If true the function is executed and multiple files generated - /// otherwise only a single file is generated. - [SecurityCritical] - public override void GenerateFiles(bool split, IFileHandler handlerHelper, IMessageLogger logger, string referenceFolder, bool fileCreated, bool OpenGeneratedFilesInIDE) - { - base.GenerateFiles(split, handlerHelper, logger, referenceFolder, fileCreated, OpenGeneratedFilesInIDE); - } - /// /// VSManager constructor. Initializes the template variable. /// @@ -267,4 +254,4 @@ public class FilesManager { { } } -} #> +} #> \ No newline at end of file diff --git a/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.cs b/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.cs index d459238c..8f70adb8 100644 --- a/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.cs +++ b/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.cs @@ -17,9 +17,9 @@ namespace Microsoft.OData.CodeGen.Templates using System.IO; using System.Linq; using System.Net; - using System.Security; using System.Text; using System.Text.RegularExpressions; + using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using Microsoft.OData.Edm.Csdl; @@ -31,6 +31,7 @@ namespace Microsoft.OData.CodeGen.Templates using Microsoft.OData.CodeGen.FileHandling; using Microsoft.OData.CodeGen.Logging; using Microsoft.OData.CodeGen.Common; + using System.Security; /// /// Class to produce the template output @@ -62,97 +63,8 @@ public virtual string TransformText() //--------------------------------------------------------------------------------- */ - CodeGenerationContext context; - if (!string.IsNullOrWhiteSpace(this.Edmx)) - { - context = new CodeGenerationContext(this.Edmx, this.NamespacePrefix) - { - UseDataServiceCollection = this.UseDataServiceCollection, - TargetLanguage = this.TargetLanguage, - EnableNamingAlias = this.EnableNamingAlias, - IgnoreUnexpectedElementsAndAttributes = this.IgnoreUnexpectedElementsAndAttributes, - MetadataFilePath = this.MetadataFilePath, - MetadataFileRelativePath = this.MetadataFileRelativePath, - MakeTypesInternal = this.MakeTypesInternal, - MultipleFilesManager = new FilesManager(null), - OmitVersioningInfo = this.OmitVersioningInfo, - GenerateMultipleFiles = this.GenerateMultipleFiles, - ExcludedOperationImports = this.ExcludedOperationImports, - ExcludedBoundOperations = this.ExcludedBoundOperations, - ExcludedSchemaTypes = this.ExcludedSchemaTypes, - EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute - }; - } - else - { - this.ApplyParametersFromCommandLine(); - if (string.IsNullOrEmpty(metadataDocumentUri)) - { - this.ApplyParametersFromConfigurationClass(); - } - - WebProxy proxy = null; - if(this.IncludeWebProxy) - { - proxy = new WebProxy(this.WebProxyHost,true); - - if(this.IncludeWebProxyNetworkCredentials) - { - NetworkCredential credentials = new NetworkCredential(this.WebProxyNetworkCredentialsUsername, - this.WebProxyNetworkCredentialsPassword, - this.WebProxyNetworkCredentialsDomain); - proxy.Credentials = credentials; - } - - } - - context = new CodeGenerationContext(new Uri(this.MetadataDocumentUri, UriKind.Absolute), this.NamespacePrefix, proxy, this.CustomHttpHeaders) - { - UseDataServiceCollection = this.UseDataServiceCollection, - TargetLanguage = this.TargetLanguage, - EnableNamingAlias = this.EnableNamingAlias, - IgnoreUnexpectedElementsAndAttributes = this.IgnoreUnexpectedElementsAndAttributes, - MetadataFilePath = this.MetadataFilePath, - MetadataFileRelativePath = this.MetadataFileRelativePath, - MakeTypesInternal = this.MakeTypesInternal, - MultipleFilesManager = new FilesManager(null), - OmitVersioningInfo = this.OmitVersioningInfo, - GenerateMultipleFiles = this.GenerateMultipleFiles, - ExcludedOperationImports = this.ExcludedOperationImports, - ExcludedBoundOperations = this.ExcludedBoundOperations, - ExcludedSchemaTypes = this.ExcludedSchemaTypes, - EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute - }; - } - - this.MultipleFilesManager = context.MultipleFilesManager; - - if(this.GetReferencedModelReaderFunc != null) - { - context.GetReferencedModelReaderFunc = this.GetReferencedModelReaderFunc; - } - - ODataClientTemplate template; - switch(this.TargetLanguage) - { - case LanguageOption.CSharp: - template = new ODataClientCSharpTemplate(context); - break; - case LanguageOption.VB: - template = new ODataClientVBTemplate(context); - break; - - default: - throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Code gen for the target language '{0}' is not supported.", this.TargetLanguage.ToString())); - } - - - this.Write(this.ToStringHelper.ToStringWithCulture(template.TransformText())); - - foreach (string warning in context.Warnings) - { - this.Warning(warning); - } + // This will be used whenever the code is ran in the t4 environment as T4 doesn't support full async execution. + this.TransformTextAsync().GetAwaiter().GetResult(); return this.GenerationEnvironment.ToString(); } @@ -276,6 +188,103 @@ internal static string CustomizeNamespace(string fullNamespace) } + +public virtual async Task TransformTextAsync() +{ + CodeGenerationContext context; + if (!string.IsNullOrWhiteSpace(this.Edmx)) + { + context = new CodeGenerationContext(this.Edmx, this.NamespacePrefix) + { + UseDataServiceCollection = this.UseDataServiceCollection, + TargetLanguage = this.TargetLanguage, + EnableNamingAlias = this.EnableNamingAlias, + IgnoreUnexpectedElementsAndAttributes = this.IgnoreUnexpectedElementsAndAttributes, + MetadataFilePath = this.MetadataFilePath, + MetadataFileRelativePath = this.MetadataFileRelativePath, + MakeTypesInternal = this.MakeTypesInternal, + MultipleFilesManager = new FilesManager(null), + OmitVersioningInfo = this.OmitVersioningInfo, + GenerateMultipleFiles = this.GenerateMultipleFiles, + ExcludedOperationImports = this.ExcludedOperationImports, + ExcludedBoundOperations = this.ExcludedBoundOperations, + ExcludedSchemaTypes = this.ExcludedSchemaTypes, + EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute + }; + } + else + { + this.ApplyParametersFromCommandLine(); + if (string.IsNullOrEmpty(metadataDocumentUri)) + { + this.ApplyParametersFromConfigurationClass(); + } + + WebProxy proxy = null; + if(this.IncludeWebProxy) + { + proxy = new WebProxy(this.WebProxyHost,true); + + if(this.IncludeWebProxyNetworkCredentials) + { + NetworkCredential credentials = new NetworkCredential(this.WebProxyNetworkCredentialsUsername, + this.WebProxyNetworkCredentialsPassword, + this.WebProxyNetworkCredentialsDomain); + proxy.Credentials = credentials; + } + + } + + context = new CodeGenerationContext(new Uri(this.MetadataDocumentUri, UriKind.Absolute), this.NamespacePrefix, proxy, this.CustomHttpHeaders) + { + UseDataServiceCollection = this.UseDataServiceCollection, + TargetLanguage = this.TargetLanguage, + EnableNamingAlias = this.EnableNamingAlias, + IgnoreUnexpectedElementsAndAttributes = this.IgnoreUnexpectedElementsAndAttributes, + MetadataFilePath = this.MetadataFilePath, + MetadataFileRelativePath = this.MetadataFileRelativePath, + MakeTypesInternal = this.MakeTypesInternal, + MultipleFilesManager = new FilesManager(null), + OmitVersioningInfo = this.OmitVersioningInfo, + GenerateMultipleFiles = this.GenerateMultipleFiles, + ExcludedOperationImports = this.ExcludedOperationImports, + ExcludedBoundOperations = this.ExcludedBoundOperations, + ExcludedSchemaTypes = this.ExcludedSchemaTypes, + EmitContainerPropertyAttribute = this.EmitContainerPropertyAttribute + }; + } + + this.MultipleFilesManager = context.MultipleFilesManager; + + if(this.GetReferencedModelReaderFunc != null) + { + context.GetReferencedModelReaderFunc = this.GetReferencedModelReaderFunc; + } + + ODataClientTemplate template; + switch(this.TargetLanguage) + { + case LanguageOption.CSharp: + template = new ODataClientCSharpTemplate(context); + break; + case LanguageOption.VB: + template = new ODataClientVBTemplate(context); + break; + + default: + throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Code gen for the target language '{0}' is not supported.", this.TargetLanguage.ToString())); + } + + this.Write(this.ToStringHelper.ToStringWithCulture(await template.TransformText())); + + foreach (string warning in context.Warnings) + { + this.Warning(warning); + } + + return this.GenerationEnvironment.ToString(); +} + /// /// The string for the edmx content. /// @@ -1807,13 +1816,13 @@ internal string ClassAccessModifier { /// Generates code for the OData client. /// /// The generated code for the OData client. - public override string TransformText() + public override async Task TransformText() { context.MultipleFilesManager.StartHeader(); this.WriteFileHeader(); context.MultipleFilesManager.EndBlock(); this.WriteNamespaces(); - context.MultipleFilesManager.GenerateFiles(context.GenerateMultipleFiles, null, null, null, false, false); + await context.MultipleFilesManager.GenerateFilesAsync(context.GenerateMultipleFiles); return context.MultipleFilesManager.Template.ToString(); } @@ -1841,7 +1850,6 @@ internal void WriteNamespace(string fullNamespace) { this.WriteNamespaceStart(this.context.GetPrefixedNamespace(fullNamespace, this, true, false)); - IEdmSchemaElement[] schemaElementsInModel = this.context.NamespacesInModel.SelectMany(n => this.context.GetSchemaElements(n)).ToArray(); IEdmSchemaElement[] schemaElements = this.context.GetSchemaElements(fullNamespace).ToArray(); if (schemaElements.OfType().Any()) { IEdmEntityContainer container = schemaElements.OfType().Single(); @@ -3487,7 +3495,7 @@ public string CurrentIndent /// /// Create the template output /// - public abstract string TransformText(); + public abstract Task TransformText(); #region Transform-time helpers /// @@ -8571,18 +8579,21 @@ public class FilesManager { /// Creates an instance of the FilesManager. The object used to generate and manage /// multiple source files. /// - private class Block { - + internal class Block + { /// Name of the block. public string Name; + /// Temporary file path of the block. + public string TemporaryFilePath; + /// The line in the template from which the block starts. public int Start; /// Length of the block. public int Length; - /// Block currently being processed. + /// Block currently being processed. public bool IsContainer; } @@ -8590,7 +8601,7 @@ private class Block { private Block _currentBlock; /// A list of all the blocks of texts to be used to generate multiple files. - private List _files = new List(); + internal List files = new List(); /// A block describing the footer of all files. private Block _footer = new Block(); @@ -8598,8 +8609,6 @@ private class Block { /// A block describing the header of all files. private Block _header = new Block(); - /// A list of file names to be generated. - protected List _generatedFileNames = new List(); /// Contains generated text. public StringBuilder Template @@ -8621,6 +8630,8 @@ public void StartNewFile(string name, bool isContainer) CurrentBlock = new Block { Name = name, IsContainer = isContainer}; } + public int FileCount => files.Count; + /// /// Marks the start of the footer for all files. /// @@ -8652,7 +8663,7 @@ public void EndBlock() if (CurrentBlock != _header && CurrentBlock != _footer) { - _files.Add(CurrentBlock); + files.Add(CurrentBlock); } _currentBlock = null; @@ -8663,43 +8674,58 @@ public void EndBlock() /// /// If true the function is executed and multiple files generated /// otherwise only a single file is generated. - [SecurityCritical] - public virtual void GenerateFiles(bool split, IFileHandler handlerHelper, IMessageLogger logger, string referenceFolder, bool fileCreated, bool OpenGeneratedFilesInIDE) + public virtual async Task GenerateFilesAsync(bool split) { if (split) { + var tasks = new List(); EndBlock(); string headerText = Template.ToString(_header.Start, _header.Length); string footerText = Template.ToString(_footer.Start, _footer.Length); - string outputPath =""; - - outputPath = Path.GetTempPath(); - - _files.Reverse(); - - foreach(Block block in _files) + var outputPath = Path.GetTempPath(); + int length = files.Count; + for (int i = length; i > 0; i--) { - if(block.IsContainer) continue; + Block block = files[i - 1]; + if (block.IsContainer) continue; string fileName = Path.Combine(outputPath, block.Name); + string content = headerText + Template.ToString(block.Start, block.Length) + footerText; + tasks.Add(CreateFileAsync(fileName, content)); + block.TemporaryFilePath = fileName; + Template.Remove(block.Start, block.Length); + } - if(fileCreated) - { - string outputFile = Path.Combine(referenceFolder, block.Name); - bool fileExists = File.Exists(outputFile); - handlerHelper.AddFileAsync(fileName, outputFile, new ODataFileOptions { OpenOnComplete = OpenGeneratedFilesInIDE, SuppressOverwritePrompt = true }).ContinueWith( - async _ => - { - await logger?.WriteMessageAsync(LogMessageCategory.Information, - "\"{0}\" has been {1}.", new FileInfo(fileName).Name, fileExists ? "updated" : "added"); - }, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously); - } - else - { - string content = headerText + Template.ToString(block.Start, block.Length) + footerText; - _generatedFileNames.Add(fileName); - CreateFile(fileName, content); - Template.Remove(block.Start, block.Length); - } + await Task.WhenAll(tasks); + } + } + + /// + /// Copies the generated file asyncronously using the file handler. + /// + /// If true the function is executed and multiple files generated otherwise only a single file is generated. + public virtual async Task CopyGeneratedFilesAsync(bool split, IFileHandler handlerHelper, IMessageLogger logger, string referenceFolder, bool fileCreated, bool OpenGeneratedFilesInIDE) + { + if (split) + { + int length = files.Count; + await logger?.WriteMessageAsync(LogMessageCategory.Information, "Adding {0} Generated files to the project. This may take a while!", length); + for (int i = length; i > 0; i--) + { + Block block = files[i - 1]; + if (block.IsContainer) continue; + string fileName = block.TemporaryFilePath; + if (!File.Exists(fileName)) continue; + + string outputFile = Path.Combine(referenceFolder, block.Name); + bool fileExists = File.Exists(outputFile); + + await handlerHelper.AddFileAsync(fileName, outputFile, new ODataFileOptions { OpenOnComplete = OpenGeneratedFilesInIDE, SuppressOverwritePrompt = true }) + .ContinueWith( + async _ => + { + await logger?.WriteMessageAsync(LogMessageCategory.Information, + "\"{0}\" has been {1}.", block.Name, fileExists ? "updated" : "added"); + }, System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously); } } } @@ -8709,12 +8735,15 @@ public virtual void GenerateFiles(bool split, IFileHandler handlerHelper, IMessa /// /// Name of the file to be created /// Content of the file to be created - protected virtual void CreateFile(string fileName, string content) + protected virtual async Task CreateFileAsync(string fileName, string content) { - if (IsFileContentDifferent(fileName, content)) + using (FileStream fileStream = File.OpenWrite(fileName)) + using (var writer = new StreamWriter(fileStream)) { - File.WriteAllText(fileName, content); - } + // Truncates the file so if it exists the older content is overwritten. + fileStream.SetLength(0); + await writer.WriteAsync(content).ConfigureAwait(false); + } } public virtual string GetCustomToolNamespace(string fileName) @@ -8730,17 +8759,6 @@ public virtual string DefaultProjectNamespace } } - /// - /// checks if the generated content is different from the existing content. - /// - /// Name of the existing file - /// Content of existing file - /// true if the file content is different - protected bool IsFileContentDifferent(string fileName, string newContent) - { - return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent); - } - /// /// FilesManager constructor. Initializes the template variable. /// @@ -8772,30 +8790,6 @@ private Block CurrentBlock private class VSManager : FilesManager { - /// - ///Creates a file with the name and content . - /// - /// Name of the file to be created - /// Content of the file to be created - protected override void CreateFile(string fileName, string content) - { - if (IsFileContentDifferent(fileName, content)) - { - File.WriteAllText(fileName, content); - } - } - - /// - /// Generates multiple files depending on the number of blocks. - /// - /// If true the function is executed and multiple files generated - /// otherwise only a single file is generated. - [SecurityCritical] - public override void GenerateFiles(bool split, IFileHandler handlerHelper, IMessageLogger logger, string referenceFolder, bool fileCreated, bool OpenGeneratedFilesInIDE) - { - base.GenerateFiles(split, handlerHelper, logger, referenceFolder, fileCreated, OpenGeneratedFilesInIDE); - } - /// /// VSManager constructor. Initializes the template variable. /// diff --git a/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.ttinclude b/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.ttinclude index 67c6ed18..f50826fe 100644 --- a/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.ttinclude +++ b/src/Microsoft.OData.CodeGen/Templates/ODataT4CodeGenerator.ttinclude @@ -25,9 +25,9 @@ <#@ Import Namespace="System.IO" #> <#@ Import Namespace="System.Linq" #> <#@ Import Namespace="System.Net"#> -<#@ Import Namespace="System.Security"#> <#@ Import Namespace="System.Text"#> <#@ Import Namespace= "System.Text.RegularExpressions"#> +<#@ Import Namespace= "System.Threading.Tasks"#> <#@ Import Namespace="System.Xml"#> <#@ Import Namespace="System.Xml.Linq" #> <#@ Import Namespace="Microsoft.OData.Edm.Csdl" #> @@ -40,8 +40,15 @@ <#@ Import Namespace="Microsoft.OData.CodeGen.Logging" #> <#@ Import Namespace= "Microsoft.OData.CodeGen.Common"#> <#@include file="ODataT4CodeGenFilesManager.ttinclude" -#><# - CodeGenerationContext context; +#> +<# + // This will be used whenever the code is ran in the t4 environment as T4 doesn't support full async execution. + this.TransformTextAsync().GetAwaiter().GetResult(); +#><#+ + +public virtual async Task TransformTextAsync() +{ + CodeGenerationContext context; if (!string.IsNullOrWhiteSpace(this.Edmx)) { context = new CodeGenerationContext(this.Edmx, this.NamespacePrefix) @@ -125,12 +132,16 @@ throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Code gen for the target language '{0}' is not supported.", this.TargetLanguage.ToString())); } -#><#=template.TransformText()#><# + this.Write(this.ToStringHelper.ToStringWithCulture(await template.TransformText())); + foreach (string warning in context.Warnings) { this.Warning(warning); } -#><#+ + + return this.GenerationEnvironment.ToString(); +} + /// /// The string for the edmx content. /// @@ -1662,13 +1673,13 @@ public abstract class ODataClientTemplate : TemplateBase /// Generates code for the OData client. /// /// The generated code for the OData client. - public override string TransformText() + public override async Task TransformText() { context.MultipleFilesManager.StartHeader(); this.WriteFileHeader(); context.MultipleFilesManager.EndBlock(); this.WriteNamespaces(); - context.MultipleFilesManager.GenerateFiles(context.GenerateMultipleFiles, null, null, null, false, false); + await context.MultipleFilesManager.GenerateFilesAsync(context.GenerateMultipleFiles); return context.MultipleFilesManager.Template.ToString(); } @@ -1696,7 +1707,6 @@ public abstract class ODataClientTemplate : TemplateBase { this.WriteNamespaceStart(this.context.GetPrefixedNamespace(fullNamespace, this, true, false)); - IEdmSchemaElement[] schemaElementsInModel = this.context.NamespacesInModel.SelectMany(n => this.context.GetSchemaElements(n)).ToArray(); IEdmSchemaElement[] schemaElements = this.context.GetSchemaElements(fullNamespace).ToArray(); if (schemaElements.OfType().Any()) { IEdmEntityContainer container = schemaElements.OfType().Single(); @@ -3342,7 +3352,7 @@ public abstract class TemplateBase /// /// Create the template output /// - public abstract string TransformText(); + public abstract Task TransformText(); #region Transform-time helpers /// @@ -6464,4 +6474,4 @@ End Namespace <#+ } } -#> +#> \ No newline at end of file diff --git a/src/ODataConnectedService.Shared/ConnectedServiceFileHandler.cs b/src/ODataConnectedService.Shared/ConnectedServiceFileHandler.cs index 7da5b73f..d81b1756 100644 --- a/src/ODataConnectedService.Shared/ConnectedServiceFileHandler.cs +++ b/src/ODataConnectedService.Shared/ConnectedServiceFileHandler.cs @@ -8,10 +8,12 @@ using System; using System.Threading.Tasks; using EnvDTE; -using Microsoft.OData.CodeGen; using Microsoft.OData.CodeGen.FileHandling; using Microsoft.VisualStudio.ConnectedServices; +using Microsoft.VisualStudio.Shell; using VSLangProj; +using Microsoft.OData.ConnectedService.Threading; +using Task = System.Threading.Tasks.Task; namespace Microsoft.OData.ConnectedService { @@ -21,6 +23,8 @@ namespace Microsoft.OData.ConnectedService public class ConnectedServiceFileHandler : IFileHandler { private ConnectedServiceHandlerContext Context; + private readonly IThreadHelper threadHelper; + public Project Project { get; private set; } /// @@ -28,10 +32,12 @@ public class ConnectedServiceFileHandler : IFileHandler /// /// The /// An object of the project. - public ConnectedServiceFileHandler(ConnectedServiceHandlerContext context, Project project ) + /// A thread helper that marshals the thread to the correct thread. + public ConnectedServiceFileHandler(ConnectedServiceHandlerContext context, Project project, IThreadHelper threadHelper) { this.Context = context; this.Project = project; + this.threadHelper = threadHelper; } /// @@ -41,55 +47,59 @@ public ConnectedServiceFileHandler(ConnectedServiceHandlerContext context, Proje /// The path target where you want to copy a file to /// The options to use when adding a file to a target path. /// Returns the path to the file that was added - public async Task AddFileAsync(string fileName, string targetPath, ODataFileOptions oDataFileOptions) - { - if (oDataFileOptions != null) - { - return await this.Context.HandlerHelper.AddFileAsync(fileName, targetPath, new AddFileOptions { SuppressOverwritePrompt = oDataFileOptions.SuppressOverwritePrompt, OpenOnComplete = oDataFileOptions.OpenOnComplete }).ConfigureAwait(true); - } - else - { - return await this.Context.HandlerHelper.AddFileAsync(fileName, targetPath).ConfigureAwait(true); - } - } + public Task AddFileAsync(string fileName, string targetPath, ODataFileOptions oDataFileOptions) + => oDataFileOptions != null + ? this.Context.HandlerHelper.AddFileAsync(fileName, targetPath, new AddFileOptions { SuppressOverwritePrompt = oDataFileOptions.SuppressOverwritePrompt, OpenOnComplete = oDataFileOptions.OpenOnComplete }) + : this.Context.HandlerHelper.AddFileAsync(fileName, targetPath); /// - /// Sets the CSDL file as an embedded resource + /// Sets the CSDL file as an embedded resource. + /// Since this method may be executed in a background thread this will require to switch to the main thread. /// /// The name of the file to set as embedded resource - public void SetFileAsEmbeddedResource(string fileName) + public async Task SetFileAsEmbeddedResourceAsync(string fileName) { - var dte = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(DTE)) as DTE; - if (dte != null) + await this.threadHelper.RunInUiThreadAsync(() => { - var projectItem = this.Project.ProjectItems.Item("Connected Services").ProjectItems.Item(((ODataConnectedServiceInstance)this.Context.ServiceInstance).ServiceConfig.ServiceName).ProjectItems.Item(fileName); - projectItem.Properties.Item("BuildAction").Value = prjBuildAction.prjBuildActionEmbeddedResource; - } +#pragma warning disable VSTHRD010 // This invokes the code in the required main thread. + if (Package.GetGlobalService(typeof(DTE)) is DTE dte) + { + var projectItem = this.Project.ProjectItems.Item("Connected Services").ProjectItems.Item(((ODataConnectedServiceInstance)this.Context.ServiceInstance).ServiceConfig.ServiceName).ProjectItems.Item(fileName); + projectItem.Properties.Item("BuildAction").Value = prjBuildAction.prjBuildActionEmbeddedResource; + return true; + } +#pragma warning restore VSTHRD010 // This invokes the code in the required main thread. + return false; + }); } /// /// Sets the container property attribute to either true or false + /// Since this method may be executed in a background thread this will require to switch to the main thread. /// /// A value of either true or false - public bool EmitContainerPropertyAttribute() - { - var vsProject = this.Project.Object as VSProject; - if (vsProject != null) - { - foreach (Reference reference in vsProject.References) - { - if (reference.SourceProject == null) - { - // Assembly reference (For project reference, SourceProject != null) - if (reference.Name.Equals("Microsoft.OData.Client", StringComparison.Ordinal)) - { - return Version.Parse(reference.Version) > Version.Parse("7.6.4.0"); - } - } - } - } + public Task EmitContainerPropertyAttributeAsync() + => threadHelper.RunInUiThreadAsync(() => - return false; - } + { +#pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread + if (this.Project.Object is VSProject vsProject) + { + foreach (Reference reference in vsProject.References) + { + if (reference.SourceProject == null) + { + // Assembly reference (For project reference, SourceProject != null) + if (reference.Name.Equals("Microsoft.OData.Client", StringComparison.Ordinal)) + { + return Version.Parse(reference.Version) > Version.Parse("7.6.4.0"); + } + } + } + } +#pragma warning restore VSTHRD010 // Invoke single-threaded types on Main thread + + return false; + }); } -} +} \ No newline at end of file diff --git a/src/ODataConnectedService.Shared/ConnectedServicePackageInstaller.cs b/src/ODataConnectedService.Shared/ConnectedServicePackageInstaller.cs index cb942e93..5cac7f2d 100644 --- a/src/ODataConnectedService.Shared/ConnectedServicePackageInstaller.cs +++ b/src/ODataConnectedService.Shared/ConnectedServicePackageInstaller.cs @@ -15,6 +15,7 @@ using NuGet.VisualStudio; using Shell = Microsoft.VisualStudio.Shell; + namespace Microsoft.OData.ConnectedService { /// @@ -26,6 +27,7 @@ public class ConnectedServicePackageInstaller : IPackageInstaller public Project Project { get; private set; } public IMessageLogger MessageLogger { get; private set; } public IVsPackageInstaller PackageInstaller { get; protected set; } + public IVsPackageInstallerServices PackageInstallerServices { get; protected set; } /// @@ -68,8 +70,7 @@ public async Task CheckAndInstallNuGetPackageAsync(string packageSource, string { if (!PackageInstallerServices.IsPackageInstalled(this.Project, packageName)) { - Version packageVersion = null; - PackageInstaller.InstallPackage(packageSource, this.Project, packageName, packageVersion, false); + PackageInstaller.InstallPackage(packageSource, this.Project, packageName, (string)null, false); ; await (this.MessageLogger?.WriteMessageAsync(LogMessageCategory.Information, $"Nuget Package \"{packageName}\" for OData client was added.")).ConfigureAwait(false); } diff --git a/src/ODataConnectedService.Shared/ConnectedServiceThreadHelper.cs b/src/ODataConnectedService.Shared/ConnectedServiceThreadHelper.cs new file mode 100644 index 00000000..2896fa14 --- /dev/null +++ b/src/ODataConnectedService.Shared/ConnectedServiceThreadHelper.cs @@ -0,0 +1,18 @@ +using System; + +using System.Threading.Tasks; +using Microsoft.OData.ConnectedService.Threading; +using Microsoft.VisualStudio.Shell; + +namespace ODataConnectedService.Shared +{ + internal class ConnectedServiceThreadHelper : IThreadHelper + { + /// + public async Task RunInUiThreadAsync(Func foregroundTask) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + return foregroundTask(); + } + } +} diff --git a/src/ODataConnectedService.Shared/ODataConnectedService.Shared.projitems b/src/ODataConnectedService.Shared/ODataConnectedService.Shared.projitems index add224db..f2b0a519 100644 --- a/src/ODataConnectedService.Shared/ODataConnectedService.Shared.projitems +++ b/src/ODataConnectedService.Shared/ODataConnectedService.Shared.projitems @@ -17,6 +17,7 @@ + @@ -25,6 +26,7 @@ + diff --git a/src/ODataConnectedService.Shared/ODataConnectedServiceHandler.cs b/src/ODataConnectedService.Shared/ODataConnectedServiceHandler.cs index 4c0ba9b0..54a45b1a 100644 --- a/src/ODataConnectedService.Shared/ODataConnectedServiceHandler.cs +++ b/src/ODataConnectedService.Shared/ODataConnectedServiceHandler.cs @@ -11,9 +11,11 @@ using System.Threading; using System.Threading.Tasks; using EnvDTE; +using Microsoft.OData.CodeGen; using Microsoft.OData.CodeGen.CodeGeneration; using Microsoft.OData.ConnectedService.Common; using Microsoft.VisualStudio.ConnectedServices; +using ODataConnectedService.Shared; namespace Microsoft.OData.ConnectedService { @@ -49,7 +51,6 @@ private async Task SaveServiceInstanceAsync(ConnectedServ { Project project = ProjectHelper.GetProjectFromHierarchy(context.ProjectHierarchy); var serviceInstance = (ODataConnectedServiceInstance)context.ServiceInstance; - var codeGenDescriptor = await GenerateCodeAsync(serviceInstance.ServiceConfig.Endpoint, serviceInstance.ServiceConfig.EdmxVersion, context, project).ConfigureAwait(false); if (!serviceInstance.ServiceConfig.StoreCustomHttpHeaders) { @@ -89,7 +90,9 @@ private async Task GenerateCodeAsync(string metadataUri, languageOption = LanguageOption.GenerateCSharpCode; } - BaseCodeGenDescriptor codeGenDescriptor = codeGenDescriptorFactory.Create(edmxVersion, new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); + var vsThreadHelper = new ConnectedServiceThreadHelper(); + + BaseCodeGenDescriptor codeGenDescriptor = codeGenDescriptorFactory.Create(edmxVersion, new ConnectedServiceFileHandler(context, project, vsThreadHelper), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); await codeGenDescriptor.AddNugetPackagesAsync().ConfigureAwait(false); await codeGenDescriptor.AddGeneratedClientCodeAsync(metadataUri, outputDirectory, (Microsoft.OData.CodeGen.Common.LanguageOption)languageOption, ((ODataConnectedServiceInstance)context.ServiceInstance).ServiceConfig).ConfigureAwait(false); return codeGenDescriptor; diff --git a/src/ODataConnectedService.Shared/Threading/IThreadHelper.cs b/src/ODataConnectedService.Shared/Threading/IThreadHelper.cs new file mode 100644 index 00000000..b536fa45 --- /dev/null +++ b/src/ODataConnectedService.Shared/Threading/IThreadHelper.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// +// Copyright (c) .NET Foundation and Contributors. All rights reserved. +// See License.txt in the project root for license information. +// +//---------------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; + +namespace Microsoft.OData.ConnectedService.Threading +{ + /// + /// A Thread helper to assist users to marshal certain work in certain threads. + /// + public interface IThreadHelper + { + /// + /// Runs the block provided in the foreground UI thread. + /// + /// Return type generic type parameter. + /// A func representing the foreground task. + /// A task respresenting the completion of the task. + Task RunInUiThreadAsync(Func foregroundTask); + } +} diff --git a/test/ODataConnectedService.Tests/CodeGeneration/CodeGenDescriptorTest.cs b/test/ODataConnectedService.Tests/CodeGeneration/CodeGenDescriptorTest.cs index 1ec361ac..dfc35a47 100644 --- a/test/ODataConnectedService.Tests/CodeGeneration/CodeGenDescriptorTest.cs +++ b/test/ODataConnectedService.Tests/CodeGeneration/CodeGenDescriptorTest.cs @@ -13,6 +13,7 @@ using System.Net; using System.Text; using System.Text.RegularExpressions; +using System.Threading.Tasks; using EnvDTE; using Microsoft.OData.CodeGen.CodeGeneration; using Microsoft.OData.CodeGen.Common; @@ -186,32 +187,27 @@ public void TestAddGenerateClientCode_GeneratesMultipleFiles() GenerateMultipleFiles = true, Endpoint = "http://localhost:9000" }; - var codeGen = new TestODataT4CodeGenerator(); var codeGenFactory = new TestODataT4CodeGeneratorFactory(codeGen); var handlerHelper = new TestConnectedServiceHandlerHelper(); var codeGenDescriptor = SetupCodeGenDescriptor(serviceConfig, serviceName, codeGenFactory, handlerHelper); - var template = new StringBuilder(); codeGen.MultipleFilesManager = new ODataT4CodeGenerator.FilesManager(template); codeGen.MultipleFilesManager.StartNewFile("File1.cs", false); template.Append("Contents1"); codeGen.MultipleFilesManager.EndBlock(); - codeGen.MultipleFilesManager.StartNewFile("File2.cs", false); template.Append("Contents2"); codeGen.MultipleFilesManager.EndBlock(); - //The file manager expects the files to have been saved in the Temp directory // when ODataT4CodeGenerator.TransformText() was called. Since we're using a dummy code generator // we need to manually ensure those files exist - var file1TempPath = Path.Combine(Path.GetTempPath(), "File1.cs"); - File.WriteAllText(file1TempPath, "Contents1"); - var file2TempPath = Path.Combine(Path.GetTempPath(), "File2.cs"); - File.WriteAllText(file2TempPath, "Contents2"); - + codeGen.MultipleFilesManager.GenerateFilesAsync(true).Wait(); + codeGenDescriptor.AddGeneratedClientCodeAsync(serviceConfig.Endpoint, referenceFolderPath, LanguageOption.GenerateCSharpCode, serviceConfig).Wait(); + var file1TempPath = codeGen.MultipleFilesManager.files[0].TemporaryFilePath; + var file2TempPath = codeGen.MultipleFilesManager.files[1].TemporaryFilePath; var expectedMainFilePath = Path.Combine(TestProjectRootPath, ServicesRootFolder, serviceName, "Main.cs"); var mainFile = handlerHelper.AddedFiles.FirstOrDefault(f => f.CreatedFile == expectedMainFilePath); Assert.IsNotNull(mainFile); @@ -312,6 +308,7 @@ public void Test_GeneratesAndSavesCodeFileWithoutProxy() [DataRow("vb", "TestConfigBasicVB.txt")] public void TestAddGeneratedClientCode_GeneratesT4TemplateFiles(string lang, string referenceFile) { + var serviceName = "MyService"; var referenceFolderPath = Path.Combine(TestProjectRootPath, ServicesRootFolder, serviceName); ServiceConfiguration serviceConfig = new ServiceConfigurationV4() @@ -678,7 +675,7 @@ public void TestV3AddNugetPackageAsync_ShouldInstallODataLibrariesForV3() handlerHelper.ServicesRootFolder = ServicesRootFolder; ConnectedServiceHandlerContext context = new TestConnectedServiceHandlerContext(serviceInstance, handlerHelper); - var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); + var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project, new TestThreadHelper()), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); descriptor.AddNugetPackagesAsync().Wait(); var installer = descriptor.PackageInstaller as TestVsPackageInstaller; @@ -712,7 +709,7 @@ public void TestV2AddGeneratedClientCode_GeneratesCodeForv2() handlerHelper.ServicesRootFolder = ServicesRootFolder; ConnectedServiceHandlerContext context = new TestConnectedServiceHandlerContext(serviceInstance, handlerHelper); - var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); + var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project, new TestThreadHelper()), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); descriptor.AddGeneratedClientCodeAsync(serviceConfig.Endpoint, referenceFolderPath, LanguageOption.GenerateCSharpCode, serviceConfig).Wait(); var addedFile = handlerHelper.AddedFiles.FirstOrDefault(); var generatedCode = File.ReadAllText(addedFile.SourceFile); @@ -746,7 +743,7 @@ public void TestV2AddGeneratedClientCode_GeneratesCodeForv2_ForVB() handlerHelper.ServicesRootFolder = ServicesRootFolder; ConnectedServiceHandlerContext context = new TestConnectedServiceHandlerContext(serviceInstance, handlerHelper); - var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); + var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project, new TestThreadHelper()), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); descriptor.AddGeneratedClientCodeAsync(serviceConfig.Endpoint, referenceFolderPath, LanguageOption.GenerateVBCode, serviceConfig).Wait(); var addedFile = handlerHelper.AddedFiles.FirstOrDefault(); var generatedCode = File.ReadAllText(addedFile.SourceFile); @@ -780,7 +777,7 @@ public void TestV3AddGeneratedClientCode_GeneratesCodeForv3() handlerHelper.ServicesRootFolder = ServicesRootFolder; ConnectedServiceHandlerContext context = new TestConnectedServiceHandlerContext(serviceInstance, handlerHelper); - var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); + var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project, new TestThreadHelper()), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); descriptor.AddGeneratedClientCodeAsync(serviceConfig.Endpoint, referenceFolderPath, LanguageOption.GenerateCSharpCode, serviceConfig).Wait(); var addedFile = handlerHelper.AddedFiles.FirstOrDefault(); var generatedCode = File.ReadAllText(addedFile.SourceFile); @@ -814,7 +811,7 @@ public void TestV3AddGeneratedClientCode_GeneratesCodeForv3_ForVB() handlerHelper.ServicesRootFolder = ServicesRootFolder; ConnectedServiceHandlerContext context = new TestConnectedServiceHandlerContext(serviceInstance, handlerHelper); - var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); + var descriptor = new TestV3CodeGenDescriptor(new ConnectedServiceFileHandler(context, project, new TestThreadHelper()), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context))); descriptor.AddGeneratedClientCodeAsync(serviceConfig.Endpoint, referenceFolderPath, LanguageOption.GenerateVBCode, serviceConfig).Wait(); var addedFile = handlerHelper.AddedFiles.FirstOrDefault(); var generatedCode = File.ReadAllText(addedFile.SourceFile); @@ -838,7 +835,7 @@ static V4CodeGenDescriptor SetupCodeGenDescriptor(ServiceConfiguration serviceCo handlerHelper.ServicesRootFolder = ServicesRootFolder; ConnectedServiceHandlerContext context = new TestConnectedServiceHandlerContext(serviceInstance, handlerHelper); - return new TestV4CodeGenDescriptor(new ConnectedServiceFileHandler(context, project), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context)), codeGenFactory); + return new TestV4CodeGenDescriptor(new ConnectedServiceFileHandler(context, project, new TestThreadHelper()), new ConnectedServiceMessageLogger(context), new ConnectedServicePackageInstaller(context, project, new ConnectedServiceMessageLogger(context)), codeGenFactory); } static Project CreateTestProject(string projectPath, ODataT4CodeGenerator.LanguageOption targetLanguage = ODataT4CodeGenerator.LanguageOption.CSharp) @@ -890,9 +887,9 @@ public TestV3CodeGenDescriptor(IFileHandler fileHandler, IMessageLogger messageL class TestODataT4CodeGenerator : ODataT4CodeGenerator { - public override string TransformText() + public override Task TransformTextAsync() { - return "Generated code"; + return Task.FromResult("Generated code"); } } class TestODataT4CodeGeneratorUsingProxy : ODataT4CodeGenerator diff --git a/test/ODataConnectedService.Tests/ODataConnectedService.Tests.csproj b/test/ODataConnectedService.Tests/ODataConnectedService.Tests.csproj index 4ed33c81..184faf88 100644 --- a/test/ODataConnectedService.Tests/ODataConnectedService.Tests.csproj +++ b/test/ODataConnectedService.Tests/ODataConnectedService.Tests.csproj @@ -72,6 +72,7 @@ + @@ -144,6 +145,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + + 1.1.11 + diff --git a/test/ODataConnectedService.Tests/ODataConnectedServiceHandlerTest.cs b/test/ODataConnectedService.Tests/ODataConnectedServiceHandlerTest.cs index 774b4b60..59bc5ddc 100644 --- a/test/ODataConnectedService.Tests/ODataConnectedServiceHandlerTest.cs +++ b/test/ODataConnectedService.Tests/ODataConnectedServiceHandlerTest.cs @@ -16,24 +16,23 @@ using Microsoft.OData.CodeGen.PackageInstallation; using Microsoft.OData.ConnectedService.Tests.TestHelpers; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Xunit; namespace Microsoft.OData.ConnectedService.Tests { - [TestClass] public class ODataConnectedServiceHandlerTest { - [DataTestMethod] - [DataRow("AddServiceInstanceAsync", 4, "V4")] - [DataRow("AddServiceInstanceAsync", 3, "V3")] - [DataRow("AddServiceInstanceAsync", 2, "V3")] - [DataRow("AddServiceInstanceAsync", 1, "V3")] - [DataRow("UpdateServiceInstanceAsync", 4, "V4")] - [DataRow("UpdateServiceInstanceAsync", 3, "V3")] - [DataRow("UpdateServiceInstanceAsync", 2, "V3")] - [DataRow("UpdateServiceInstanceAsync", 1, "V3")] - public void TestUpdateServiceInstance_GeneratesCodeAndSavesConfig(string method, int edmxVersion, string generatorVersion) + [StaTheory] + [InlineData("AddServiceInstanceAsync", 4, "V4")] + [InlineData("AddServiceInstanceAsync", 3, "V3")] + [InlineData("AddServiceInstanceAsync", 2, "V3")] + [InlineData("AddServiceInstanceAsync", 1, "V3")] + [InlineData("UpdateServiceInstanceAsync", 4, "V4")] + [InlineData("UpdateServiceInstanceAsync", 3, "V3")] + [InlineData("UpdateServiceInstanceAsync", 2, "V3")] + [InlineData("UpdateServiceInstanceAsync", 1, "V3")] + public async Task TestUpdateServiceInstance_GeneratesCodeAndSavesConfigAsync(string method, int edmxVersion, string generatorVersion) { var descriptorFactory = new TestCodeGenDescriptorFactory(); var serviceHandler = new ODataConnectedServiceHandler(descriptorFactory); @@ -46,21 +45,21 @@ public void TestUpdateServiceInstance_GeneratesCodeAndSavesConfig(string method, }; var tokenSource = new CancellationTokenSource(); var context = SetupContext(serviceConfig); - (typeof(ODataConnectedServiceHandler).GetMethod(method).Invoke( - serviceHandler, new object[] { context, tokenSource.Token }) as Task).Wait(); + await (typeof(ODataConnectedServiceHandler).GetMethod(method).Invoke( + serviceHandler, new object[] { context, tokenSource.Token }) as Task); var descriptor = descriptorFactory.CreatedInstance as TestCodeGenDescriptor; - Assert.IsTrue(descriptor.AddedClientCode); - Assert.IsTrue(descriptor.AddedNugetPackages); - Assert.AreEqual(generatorVersion, descriptor.Version); - Assert.AreEqual(serviceConfig, context.SavedExtendedDesignData); + Assert.True(descriptor.AddedClientCode); + Assert.True(descriptor.AddedNugetPackages); + Assert.Equal(generatorVersion, descriptor.Version); + Assert.Equal(serviceConfig, context.SavedExtendedDesignData); } - [DataTestMethod] - [DataRow("AddServiceInstanceAsync", 4, false)] - [DataRow("AddServiceInstanceAsync", 4, true)] - [DataRow("UpdateServiceInstanceAsync", 4, false)] - [DataRow("UpdateServiceInstanceAsync", 4, true)] - public void TestAddUpdateServiceInstance_SavesCustomHttpHeadersToDesignerDataAccordingToStoreCustomHttpHeaders(string method, int edmxVersion, bool store) + [StaTheory] + [InlineData("AddServiceInstanceAsync", 4, false)] + [InlineData("AddServiceInstanceAsync", 4, true)] + [InlineData("UpdateServiceInstanceAsync", 4, false)] + [InlineData("UpdateServiceInstanceAsync", 4, true)] + public async Task TestAddUpdateServiceInstance_SavesCustomHttpHeadersToDesignerDataAccordingToStoreCustomHttpHeadersAsync(string method, int edmxVersion, bool store) { var descriptorFactory = new TestCodeGenDescriptorFactory(); var serviceHandler = new ODataConnectedServiceHandler(descriptorFactory); @@ -77,20 +76,20 @@ public void TestAddUpdateServiceInstance_SavesCustomHttpHeadersToDesignerDataAcc }; var tokenSource = new CancellationTokenSource(); var context = SetupContext(serviceConfig); - (typeof(ODataConnectedServiceHandler).GetMethod(method).Invoke( - serviceHandler, new object[] { context, tokenSource.Token }) as Task).Wait(); + await (typeof(ODataConnectedServiceHandler).GetMethod(method).Invoke( + serviceHandler, new object[] { context, tokenSource.Token }) as Task); // CustomHttpHeaders should be null when StoreCustomHttpHeaders is false since we are not saving them to DesignerData - Assert.AreEqual(serviceConfig, context.SavedExtendedDesignData); - Assert.AreEqual(serviceConfig.CustomHttpHeaders, store ? headerValue : null); + Assert.Equal(serviceConfig, context.SavedExtendedDesignData); + Assert.Equal(serviceConfig.CustomHttpHeaders, store ? headerValue : null); } - [DataTestMethod] - [DataRow("AddServiceInstanceAsync", 4, false)] - [DataRow("AddServiceInstanceAsync", 4, true)] - [DataRow("UpdateServiceInstanceAsync", 4, false)] - [DataRow("UpdateServiceInstanceAsync", 4, true)] - public void TestAddUpdateServiceInstance_SavesWebProxyDetailsToDesignerDataAccordingToStoreWebProxyNetworkCredentials(string method, int edmxVersion, bool store) + [StaTheory] + [InlineData("AddServiceInstanceAsync", 4, false)] + [InlineData("AddServiceInstanceAsync", 4, true)] + [InlineData("UpdateServiceInstanceAsync", 4, false)] + [InlineData("UpdateServiceInstanceAsync", 4, true)] + public async Task TestAddUpdateServiceInstance_SavesWebProxyDetailsToDesignerDataAccordingToStoreWebProxyNetworkCredentialsAsync(string method, int edmxVersion, bool store) { var descriptorFactory = new TestCodeGenDescriptorFactory(); var serviceHandler = new ODataConnectedServiceHandler(descriptorFactory); @@ -111,13 +110,13 @@ public void TestAddUpdateServiceInstance_SavesWebProxyDetailsToDesignerDataAccor }; var tokenSource = new CancellationTokenSource(); var context = SetupContext(serviceConfig); - (typeof(ODataConnectedServiceHandler).GetMethod(method).Invoke( - serviceHandler, new object[] { context, tokenSource.Token }) as Task).Wait(); + await (typeof(ODataConnectedServiceHandler).GetMethod(method).Invoke( + serviceHandler, new object[] { context, tokenSource.Token }) as Task); // WebProxy username and password should be null when StoreWebProxyNetworkCredentials is false since we are not saving them to DesignerData - Assert.AreEqual(serviceConfig, context.SavedExtendedDesignData); - Assert.AreEqual(serviceConfig.WebProxyNetworkCredentialsUsername, store ? username : null); - Assert.AreEqual(serviceConfig.WebProxyNetworkCredentialsPassword, store ? password : null); + Assert.Equal(serviceConfig, context.SavedExtendedDesignData); + Assert.Equal(serviceConfig.WebProxyNetworkCredentialsUsername, store ? username : null); + Assert.Equal(serviceConfig.WebProxyNetworkCredentialsPassword, store ? password : null); } static TestConnectedServiceHandlerContext SetupContext(ServiceConfiguration serviceConfig) diff --git a/test/ODataConnectedService.Tests/ODataConnectedServiceWizardTests.cs b/test/ODataConnectedService.Tests/ODataConnectedServiceWizardTests.cs index de59e808..bfaf08d7 100644 --- a/test/ODataConnectedService.Tests/ODataConnectedServiceWizardTests.cs +++ b/test/ODataConnectedService.Tests/ODataConnectedServiceWizardTests.cs @@ -11,13 +11,13 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Windows; using System.Windows.Documents; using FluentAssertions; using Microsoft.OData.CodeGen.Common; using Microsoft.OData.CodeGen.Models; using Microsoft.OData.ConnectedService; -using Microsoft.OData.ConnectedService.ViewModels; using Microsoft.OData.ConnectedService.Views; using Microsoft.OData.Edm; using Microsoft.VisualStudio.ConnectedServices; @@ -98,7 +98,7 @@ private ServiceConfigurationV4 GetTestConfig() }; } - [Fact] + [StaFact] public void TestLoadUserSettingsWhenWizardIsCreated() { var settings = new UserSettings(); @@ -114,8 +114,8 @@ public void TestLoadUserSettingsWhenWizardIsCreated() } } - [Fact] - public void TestConstructor_ShouldUseDefaultSettingsWhenNotUpdating() + [StaFact] + public async Task TestConstructor_ShouldUseDefaultSettingsWhenNotUpdatingAsync() { var savedConfig = GetTestConfig(); var context = new TestConnectedServiceProviderContext(false, savedConfig); @@ -123,7 +123,7 @@ public void TestConstructor_ShouldUseDefaultSettingsWhenNotUpdating() using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(new WizardEnteringArgs(null)).Wait(); + await endpointPage.OnPageEnteringAsync(new WizardEnteringArgs(null)); Assert.Equal(Constants.DefaultServiceName, endpointPage.UserSettings.ServiceName); Assert.Null(endpointPage.UserSettings.Endpoint); Assert.Null(endpointPage.EdmxVersion); @@ -142,7 +142,7 @@ public void TestConstructor_ShouldUseDefaultSettingsWhenNotUpdating() endpointPage.UserSettings.Endpoint = MetadataPath; endpointPage.MetadataTempPath = MetadataPath; endpointPage.EdmxVersion = Constants.EdmxVersion4; - operationsPage.OnPageEnteringAsync(new WizardEnteringArgs(endpointPage)).Wait(); + await operationsPage.OnPageEnteringAsync(new WizardEnteringArgs(endpointPage)); operationsPage.OperationImports.ShouldBeEquivalentTo(new List() { new OperationImportModel @@ -169,7 +169,7 @@ public void TestConstructor_ShouldUseDefaultSettingsWhenNotUpdating() }); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(new WizardEnteringArgs(operationsPage)).Wait(); + await typesPage.OnPageEnteringAsync(new WizardEnteringArgs(operationsPage)); typesPage.SchemaTypes.ShouldBeEquivalentTo(new List { new SchemaTypeModel("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airline", "Airline") @@ -253,7 +253,7 @@ public void TestConstructor_ShouldUseDefaultSettingsWhenNotUpdating() }); var advancedPage = wizard.AdvancedSettingsViewModel; - advancedPage.OnPageEnteringAsync(new WizardEnteringArgs(typesPage)).Wait(); + await advancedPage.OnPageEnteringAsync(new WizardEnteringArgs(typesPage)); Assert.Equal(Constants.DefaultReferenceFileName, advancedPage.UserSettings.GeneratedFileNamePrefix); Assert.False(advancedPage.UserSettings.UseNamespacePrefix); Assert.Null(advancedPage.UserSettings.NamespacePrefix); @@ -268,8 +268,8 @@ public void TestConstructor_ShouldUseDefaultSettingsWhenNotUpdating() } } - [Fact] - public void TestConstructor_LoadsSavedConfigWhenUpdating() + [StaFact] + public async Task TestConstructor_LoadsSavedConfigWhenUpdatingAsync() { var savedConfig = GetTestConfig(); var context = new TestConnectedServiceProviderContext(true, savedConfig); @@ -279,7 +279,7 @@ public void TestConstructor_LoadsSavedConfigWhenUpdating() Assert.Equal(savedConfig, wizard.ServiceConfig); var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(new WizardEnteringArgs(null)).Wait(); + await endpointPage.OnPageEnteringAsync(new WizardEnteringArgs(null)); Assert.Equal("https://service/$metadata", endpointPage.UserSettings.Endpoint); Assert.Equal("MyService", endpointPage.UserSettings.ServiceName); Assert.True(endpointPage.UserSettings.IncludeCustomHeaders); @@ -297,7 +297,8 @@ public void TestConstructor_LoadsSavedConfigWhenUpdating() var operationsPage = wizard.OperationImportsViewModel; endpointPage.MetadataTempPath = MetadataPath; endpointPage.EdmxVersion = Constants.EdmxVersion4; - operationsPage.OnPageEnteringAsync(new WizardEnteringArgs(endpointPage)).Wait(); + + await operationsPage.OnPageEnteringAsync(new WizardEnteringArgs(endpointPage)); operationsPage.OperationImports.ShouldBeEquivalentTo(new List() { new OperationImportModel @@ -324,7 +325,8 @@ public void TestConstructor_LoadsSavedConfigWhenUpdating() }); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(new WizardEnteringArgs(operationsPage)).Wait(); + + await typesPage.OnPageEnteringAsync(new WizardEnteringArgs(operationsPage)); typesPage.SchemaTypes.ShouldBeEquivalentTo(new List { new SchemaTypeModel("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airline", "Airline") @@ -418,7 +420,8 @@ public void TestConstructor_LoadsSavedConfigWhenUpdating() }); var advancedPage = wizard.AdvancedSettingsViewModel; - advancedPage.OnPageEnteringAsync(new WizardEnteringArgs(typesPage)).Wait(); + + await advancedPage.OnPageEnteringAsync(new WizardEnteringArgs(typesPage)); Assert.Equal("GeneratedCode", advancedPage.UserSettings.GeneratedFileNamePrefix); Assert.True(advancedPage.UserSettings.UseNamespacePrefix); Assert.Equal("Namespace", advancedPage.UserSettings.NamespacePrefix); @@ -433,8 +436,8 @@ public void TestConstructor_LoadsSavedConfigWhenUpdating() } } - [Fact] - public void TestDisableReadOnlyFieldsWhenUpdating() + [StaFact] + public async Task TestDisableReadOnlyFieldsWhenUpdatingAsync() { ServiceConfigurationV4 savedConfig = GetTestConfig(); var context = new TestConnectedServiceProviderContext(true, savedConfig); @@ -442,7 +445,7 @@ public void TestDisableReadOnlyFieldsWhenUpdating() { // endpoint page var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(new WizardEnteringArgs(null)).Wait(); + await endpointPage.OnPageEnteringAsync(new WizardEnteringArgs(null)); var endpointView = endpointPage.View as ConfigODataEndpoint; Assert.False(endpointView.ServiceName.IsEnabled); @@ -456,7 +459,8 @@ public void TestDisableReadOnlyFieldsWhenUpdating() // advanced settings page var advancedPage = wizard.AdvancedSettingsViewModel; - advancedPage.OnPageEnteringAsync(new WizardEnteringArgs(endpointPage)).Wait(); + + await advancedPage.OnPageEnteringAsync(new WizardEnteringArgs(endpointPage)); var advancedView = advancedPage.View as AdvancedSettings; Assert.False(advancedView.IncludeT4File.IsEnabled); Assert.False(advancedView.GenerateMultipleFiles.IsEnabled); @@ -464,8 +468,8 @@ public void TestDisableReadOnlyFieldsWhenUpdating() } } - [Fact] - public void TestGetFinishedServiceInstanceAsync_SavesUserSettingsAndReturnsServiceInstanceWithConfigFromTheWizard() + [StaFact] + public async Task TestGetFinishedServiceInstanceAsync_SavesUserSettingsAndReturnsServiceInstanceWithConfigFromTheWizardAsync() { var context = new TestConnectedServiceProviderContext(false); using (var wizard = new ODataConnectedServiceWizard(context)) @@ -486,7 +490,8 @@ public void TestGetFinishedServiceInstanceAsync_SavesUserSettingsAndReturnsServi endpointPage.UserSettings.WebProxyNetworkCredentialsPassword = "pass"; var operationsPage = wizard.OperationImportsViewModel; - endpointPage.OnPageLeavingAsync(new WizardLeavingArgs(operationsPage)).Wait(); + + await endpointPage.OnPageLeavingAsync(new WizardLeavingArgs(operationsPage)); operationsPage.OperationImports = new List() { new OperationImportModel() { Name = "GetNearestAirport", IsSelected = false }, @@ -495,7 +500,8 @@ public void TestGetFinishedServiceInstanceAsync_SavesUserSettingsAndReturnsServi }; var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(new WizardEnteringArgs(operationsPage)).Wait(); + + await typesPage.OnPageEnteringAsync(new WizardEnteringArgs(operationsPage)); typesPage.SchemaTypes = new List() { new SchemaTypeModel("Microsoft.OData.Service.Sample.TrippinInMemory.Models.Airline", "Airline") { IsSelected = false }, @@ -537,10 +543,10 @@ public void TestGetFinishedServiceInstanceAsync_SavesUserSettingsAndReturnsServi advancedPage.UserSettings.OpenGeneratedFilesInIDE = true; advancedPage.UserSettings.OmitVersioningInfo = true; - operationsPage.OnPageLeavingAsync(new WizardLeavingArgs(typesPage)).Wait(); - typesPage.OnPageLeavingAsync(new WizardLeavingArgs(advancedPage)).Wait(); - advancedPage.OnPageLeavingAsync(new WizardLeavingArgs(null)).Wait(); - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + await operationsPage.OnPageLeavingAsync(new WizardLeavingArgs(typesPage)); + await typesPage.OnPageLeavingAsync(new WizardLeavingArgs(advancedPage)); + await advancedPage.OnPageLeavingAsync(new WizardLeavingArgs(null)); + ODataConnectedServiceInstance serviceInstance = (ODataConnectedServiceInstance)await wizard.GetFinishedServiceInstanceAsync(); var config = serviceInstance.ServiceConfig as ServiceConfigurationV4; // saved user settings @@ -622,8 +628,8 @@ public void TestGetFinishedServiceInstanceAsync_SavesUserSettingsAndReturnsServi } } - [Fact] - public void GetFinishedServiceInstanceAsync_WhenUpdating_ShouldUseSavedConfigWhenUserDoesNotVisitPages() + [StaFact] + public async Task GetFinishedServiceInstanceAsync_WhenUpdating_ShouldUseSavedConfigWhenUserDoesNotVisitPagesAsync() { var savedConfig = GetTestConfig(); savedConfig.Endpoint = MetadataPath; @@ -631,10 +637,10 @@ public void GetFinishedServiceInstanceAsync_WhenUpdating_ShouldUseSavedConfigWhe using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(null).Wait(); - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); + await endpointPage.OnPageLeavingAsync(null); - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + var serviceInstance = (ODataConnectedServiceInstance)await wizard.GetFinishedServiceInstanceAsync(); var config = serviceInstance.ServiceConfig as ServiceConfigurationV4; Assert.Equal("GeneratedCode", serviceInstance.InstanceId); @@ -681,19 +687,19 @@ public void GetFinishedServiceInstanceAsync_WhenUpdating_ShouldUseSavedConfigWhe } } - [Fact] - public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() + [StaFact] + public async Task ShouldPreserveState_WhenMovingBetweenPagesAndBackAsync() { var context = new TestConnectedServiceProviderContext(); using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); endpointPage.UserSettings.Endpoint = MetadataPath; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); var typesPageView = typesPage.View as SchemaTypes; Assert.Equal(typesPage.SchemaTypesCount, typesPage.SchemaTypes.Count()); Assert.Equal(typesPage.BoundOperationsCount, typesPage.SchemaTypes.SelectMany(x => x.BoundOperations).Count()); @@ -709,10 +715,10 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() Assert.Equal(typesPageView?.SelectedBoundOperationsCount.Text, (typesPage.BoundOperationsCount - typesPage.ExcludedBoundOperationsNames.Count()).ToString(CultureInfo.InvariantCulture)); - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); var operationsPage = wizard.OperationImportsViewModel; - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); var operationsPageView = operationsPage.View as OperationImports; Assert.Equal(operationsPage.OperationImportsCount, operationsPage.OperationImports.Count()); Assert.Equal(operationsPageView?.SelectedOperationImportsCount.Text, operationsPage.OperationImportsCount.ToString(CultureInfo.InvariantCulture)); @@ -721,17 +727,17 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() Assert.Equal(operationsPageView?.SelectedOperationImportsCount.Text, (operationsPage.OperationImportsCount - operationsPage.ExcludedOperationImportsNames.Count()) .ToString(CultureInfo.InvariantCulture)); - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); var advancedPage = wizard.AdvancedSettingsViewModel; - advancedPage.OnPageEnteringAsync(null).Wait(); + await advancedPage.OnPageEnteringAsync(null); advancedPage.UserSettings.UseDataServiceCollection = true; advancedPage.UserSettings.MakeTypesInternal = true; advancedPage.UserSettings.UseNamespacePrefix = true; advancedPage.UserSettings.OmitVersioningInfo = true; - advancedPage.OnPageLeavingAsync(null).Wait(); + await advancedPage.OnPageLeavingAsync(null); - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); Assert.Equal(Constants.DefaultServiceName, endpointPage.UserSettings.ServiceName); Assert.Equal(MetadataPath, endpointPage.UserSettings.Endpoint); endpointPage.UserSettings.ServiceName = "Service"; @@ -739,9 +745,9 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() endpointPage.UserSettings.StoreCustomHttpHeaders = true; endpointPage.UserSettings.StoreWebProxyNetworkCredentials = true; endpointPage.UserSettings.CustomHttpHeaders = "A:b"; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); - advancedPage.OnPageEnteringAsync(null).Wait(); + await advancedPage.OnPageEnteringAsync(null); Assert.True(advancedPage.UserSettings.UseNamespacePrefix); Assert.True(advancedPage.UserSettings.UseDataServiceCollection); Assert.True(advancedPage.UserSettings.MakeTypesInternal); @@ -749,9 +755,9 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() advancedPage.UserSettings.NamespacePrefix = "MyNamespace"; advancedPage.UserSettings.GenerateMultipleFiles = true; advancedPage.UserSettings.UseDataServiceCollection = false; - advancedPage.OnPageLeavingAsync(null).Wait(); + await advancedPage.OnPageLeavingAsync(null); - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); operationNearestAirport = operationsPage.OperationImports.FirstOrDefault(o => o.Name == "GetNearestAirport"); Assert.False(operationNearestAirport.IsSelected); Assert.Equal(operationsPageView?.SelectedOperationImportsCount.Text, @@ -759,9 +765,9 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() .ToString(CultureInfo.InvariantCulture)); var operationResetDataSource = operationsPage.OperationImports.FirstOrDefault(o => o.Name == "ResetDataSource"); operationResetDataSource.IsSelected = false; - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); typeEmployee = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Employee"); Assert.False(typeEmployee.IsSelected); Assert.Equal(typesPageView?.SelectedSchemaTypesCount.Text, @@ -769,9 +775,9 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() typeEmployee.IsSelected = true; var typeFlight = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Flight"); typeFlight.IsSelected = false; - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + var serviceInstance = (ODataConnectedServiceInstance)await wizard.GetFinishedServiceInstanceAsync(); var config = serviceInstance.ServiceConfig as ServiceConfigurationV4; Assert.Equal("Service", config.ServiceName); @@ -802,8 +808,8 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack() } } - [Fact] - public void ShouldPreserveState_WhenMovingBetweenPagesAndBack_WhenUpdating() + [StaFact] + public async Task ShouldPreserveState_WhenMovingBetweenPagesAndBack_WhenUpdatingAsync() { var savedConfig = GetTestConfig(); savedConfig.Endpoint = MetadataPath; @@ -811,44 +817,44 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack_WhenUpdating() using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(null).Wait(); - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); + await endpointPage.OnPageLeavingAsync(null); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); var typeGender = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "PersonGender"); Assert.False(typeGender.IsSelected); typeGender.IsSelected = true; var typePerson = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Person"); Assert.False(typePerson.IsSelected); - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); var operationsPage = wizard.OperationImportsViewModel; - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); var operationNearestAirport = operationsPage.OperationImports.FirstOrDefault(o => o.Name == "GetNearestAirport"); Assert.True(operationNearestAirport.IsSelected); operationNearestAirport.IsSelected = false; - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); var boundOperationGetFavoriteAirline = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Person").BoundOperations.FirstOrDefault(o => o.Name == "GetFavoriteAirline(Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person)"); Assert.False(boundOperationGetFavoriteAirline.IsSelected); var advancedPage = wizard.AdvancedSettingsViewModel; - advancedPage.OnPageEnteringAsync(null).Wait(); + await advancedPage.OnPageEnteringAsync(null); advancedPage.UserSettings.UseDataServiceCollection = true; advancedPage.UserSettings.MakeTypesInternal = true; advancedPage.UserSettings.UseNamespacePrefix = true; advancedPage.UserSettings.OmitVersioningInfo = true; - advancedPage.OnPageLeavingAsync(null).Wait(); + await advancedPage.OnPageLeavingAsync(null); - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); Assert.Equal(savedConfig.ServiceName, endpointPage.UserSettings.ServiceName); Assert.Equal(savedConfig.Endpoint, endpointPage.UserSettings.Endpoint); endpointPage.UserSettings.IncludeCustomHeaders = true; endpointPage.UserSettings.CustomHttpHeaders = "A:b"; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); - advancedPage.OnPageEnteringAsync(null).Wait(); + await advancedPage.OnPageEnteringAsync(null); Assert.True(advancedPage.UserSettings.UseNamespacePrefix); Assert.True(advancedPage.UserSettings.UseDataServiceCollection); Assert.True(advancedPage.UserSettings.MakeTypesInternal); @@ -856,17 +862,17 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack_WhenUpdating() advancedPage.UserSettings.NamespacePrefix = "MyNamespace"; advancedPage.UserSettings.GenerateMultipleFiles = true; advancedPage.UserSettings.UseDataServiceCollection = false; - advancedPage.OnPageLeavingAsync(null).Wait(); + await advancedPage.OnPageLeavingAsync(null); - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); operationNearestAirport = operationsPage.OperationImports.FirstOrDefault(o => o.Name == "GetNearestAirport"); Assert.False(operationNearestAirport.IsSelected); var operationResetDataSource = operationsPage.OperationImports.FirstOrDefault(o => o.Name == "ResetDataSource"); Assert.False(operationResetDataSource.IsSelected); operationResetDataSource.IsSelected = true; - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); typeGender = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "PersonGender"); Assert.True(typeGender.IsSelected); var typeFlight = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Flight"); @@ -875,9 +881,9 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack_WhenUpdating() Assert.False(typePerson.IsSelected); boundOperationGetFavoriteAirline = typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Person").BoundOperations.FirstOrDefault(o => o.Name == "GetFavoriteAirline(Microsoft.OData.Service.Sample.TrippinInMemory.Models.Person)"); Assert.False(boundOperationGetFavoriteAirline.IsSelected); - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + var serviceInstance = await wizard.GetFinishedServiceInstanceAsync() as ODataConnectedServiceInstance; var config = serviceInstance.ServiceConfig as ServiceConfigurationV4; Assert.Equal(savedConfig.ServiceName, config.ServiceName); @@ -915,42 +921,42 @@ public void ShouldPreserveState_WhenMovingBetweenPagesAndBack_WhenUpdating() } } - [Fact] - public void ShouldReloadOperationsAndTypesForNewEndpoint_WhenEndpointIsChangedBeforeFinishing() + [StaFact] + public async Task ShouldReloadOperationsAndTypesForNewEndpoint_WhenEndpointIsChangedBeforeFinishingAsync() { var context = new TestConnectedServiceProviderContext(); using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); endpointPage.UserSettings.Endpoint = MetadataPath; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "Employee").IsSelected = false; - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); var operationsPage = wizard.OperationImportsViewModel; - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); operationsPage.OperationImports.FirstOrDefault(o => o.Name == "GetNearestAirport").IsSelected = false; - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); // go back to first page and change endpoint - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); endpointPage.UserSettings.Endpoint = MetadataPathSimple; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); typesPage.SchemaTypes.ShouldBeEquivalentTo(new List() { new SchemaTypeModel("SimpleService.Models.OtherThing", "OtherThing") { IsSelected = true }, new SchemaTypeModel("SimpleService.Models.Thing", "Thing") { IsSelected = true } }); typesPage.SchemaTypes.FirstOrDefault(t => t.ShortName == "OtherThing").IsSelected = false; - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); operationsPage.OperationImports.ShouldBeEquivalentTo(new List() { new OperationImportModel @@ -970,7 +976,7 @@ public void ShouldReloadOperationsAndTypesForNewEndpoint_WhenEndpointIsChangedBe }); operationsPage.OperationImports.FirstOrDefault(o => o.Name == "ResetThings").IsSelected = false; - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + var serviceInstance = await wizard.GetFinishedServiceInstanceAsync() as ODataConnectedServiceInstance; var config = serviceInstance.ServiceConfig as ServiceConfigurationV4; Assert.Equal(MetadataPathSimple, config.Endpoint); @@ -979,28 +985,28 @@ public void ShouldReloadOperationsAndTypesForNewEndpoint_WhenEndpointIsChangedBe } } - [Fact] - public void ShouldDeselectOperations_WhenRelatedTypeIsDeselectedBefore() + [StaFact] + public async Task ShouldDeselectOperations_WhenRelatedTypeIsDeselectedBeforeAsync() { var context = new TestConnectedServiceProviderContext(); using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); endpointPage.UserSettings.Endpoint = MetadataPath; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); typesPage.SchemaTypes.First(t => t.ShortName == "Airport").IsSelected = false; - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); var operationsPage = wizard.OperationImportsViewModel; - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); operationsPage.OperationImports.First(o => o.Name == "GetNearestAirport").IsSelected.Should().BeFalse(); - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + var serviceInstance = await wizard.GetFinishedServiceInstanceAsync() as ODataConnectedServiceInstance; var config = serviceInstance?.ServiceConfig as ServiceConfigurationV4; config?.ExcludedOperationImports.ShouldBeEquivalentTo(new List { "GetNearestAirport" }); @@ -1013,28 +1019,28 @@ public void ShouldDeselectOperations_WhenRelatedTypeIsDeselectedBefore() } } - [Fact] - public void ShouldDeselectBoundOperations_WhenRelatedTypeIsDeselectedBefore() + [StaFact] + public async Task ShouldDeselectBoundOperations_WhenRelatedTypeIsDeselectedBeforeAsync() { var context = new TestConnectedServiceProviderContext(); using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; - endpointPage.OnPageEnteringAsync(null).Wait(); + await endpointPage.OnPageEnteringAsync(null); endpointPage.UserSettings.Endpoint = MetadataPath; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); var typesPage = wizard.SchemaTypesViewModel; - typesPage.OnPageEnteringAsync(null).Wait(); + await typesPage.OnPageEnteringAsync(null); typesPage.SchemaTypes.First(t => t.ShortName == "Person").IsSelected = false; - typesPage.OnPageLeavingAsync(null).Wait(); + await typesPage.OnPageLeavingAsync(null); var operationsPage = wizard.OperationImportsViewModel; - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); operationsPage.OperationImports.First(o => o.Name == "GetPersonWithMostFriends").IsSelected.Should().BeFalse(); - operationsPage.OnPageLeavingAsync(null).Wait(); + await operationsPage.OnPageLeavingAsync(null); - var serviceInstance = wizard.GetFinishedServiceInstanceAsync().Result as ODataConnectedServiceInstance; + var serviceInstance = await wizard.GetFinishedServiceInstanceAsync() as ODataConnectedServiceInstance; var config = serviceInstance?.ServiceConfig as ServiceConfigurationV4; config?.ExcludedOperationImports.ShouldBeEquivalentTo(new List { "GetPersonWithMostFriends" }); @@ -1056,23 +1062,23 @@ public void ShouldDeselectBoundOperations_WhenRelatedTypeIsDeselectedBefore() } [Fact] - public void UnsupportedFeaturesAreDisabledOrHidden_WhenServiceIsV3OrLess() + public async Task UnsupportedFeaturesAreDisabledOrHidden_WhenServiceIsV3OrLessAsync() { var context = new TestConnectedServiceProviderContext(); using (var wizard = new ODataConnectedServiceWizard(context)) { var endpointPage = wizard.ConfigODataEndpointViewModel; endpointPage.UserSettings.Endpoint = MetadataPathV3; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); Assert.Equal(Constants.EdmxVersion1, endpointPage.EdmxVersion); var operationsPage = wizard.OperationImportsViewModel; - operationsPage.OnPageEnteringAsync(null).Wait(); + await operationsPage.OnPageEnteringAsync(null); Assert.False(operationsPage.View.IsEnabled); Assert.False(operationsPage.IsSupportedODataVersion); var advancedPage = wizard.AdvancedSettingsViewModel; - advancedPage.OnPageEnteringAsync(null).Wait(); + await advancedPage.OnPageEnteringAsync(null); var advancedView = advancedPage.View as AdvancedSettings; advancedView.settings.RaiseEvent(new RoutedEventArgs(Hyperlink.ClickEvent)); Assert.Equal(Visibility.Hidden, advancedView.AdvancedSettingsForv4.Visibility); @@ -1080,8 +1086,8 @@ public void UnsupportedFeaturesAreDisabledOrHidden_WhenServiceIsV3OrLess() } - [Fact] - public void LoadSchemaTypes_ShouldOnlyShowItemsInPaginator() + [StaFact] + public async Task LoadSchemaTypes_ShouldOnlyShowItemsInPaginatorAsync() { ServiceConfigurationV4 savedConfig = GetTestConfig(); var context = new TestConnectedServiceProviderContext(true, savedConfig); @@ -1089,7 +1095,7 @@ public void LoadSchemaTypes_ShouldOnlyShowItemsInPaginator() { var endpointPage = wizard.ConfigODataEndpointViewModel; endpointPage.UserSettings.Endpoint = MetadataPathV3; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); var viewModel = wizard.SchemaTypesViewModel; @@ -1099,7 +1105,7 @@ public void LoadSchemaTypes_ShouldOnlyShowItemsInPaginator() viewModel.LoadSchemaTypes(listToLoad, new Dictionary>()); - viewModel.OnPageEnteringAsync(null).Wait(); + await viewModel.OnPageEnteringAsync(null); Assert.Equal(viewModel.SchemaTypes.Count(), listToLoad.Length); var view = viewModel.View as SchemaTypes; Assert.NotNull(viewModel); @@ -1112,8 +1118,8 @@ public void LoadSchemaTypes_ShouldOnlyShowItemsInPaginator() } } - [Fact] - public void LoadOperationTypes_ShouldOnlyShowItemsInPaginator() + [StaFact] + public async Task LoadOperationTypes_ShouldOnlyShowItemsInPaginatorAsync() { ServiceConfigurationV4 savedConfig = GetTestConfig(); var context = new TestConnectedServiceProviderContext(true, savedConfig); @@ -1121,7 +1127,7 @@ public void LoadOperationTypes_ShouldOnlyShowItemsInPaginator() { var endpointPage = wizard.ConfigODataEndpointViewModel; endpointPage.UserSettings.Endpoint = MetadataPathV3; - endpointPage.OnPageLeavingAsync(null).Wait(); + await endpointPage.OnPageLeavingAsync(null); var viewModel = wizard.OperationImportsViewModel; var container = new EdmEntityContainer("Test", "Default"); @@ -1132,7 +1138,7 @@ public void LoadOperationTypes_ShouldOnlyShowItemsInPaginator() viewModel.LoadOperationImports(listToLoad, new HashSet(), new Dictionary()); - viewModel.OnPageEnteringAsync(null).Wait(); + await viewModel.OnPageEnteringAsync(null); Assert.Equal(viewModel.OperationImports.Count(), listToLoad.Count); var view = viewModel.View as OperationImports; Assert.NotNull(viewModel); diff --git a/test/ODataConnectedService.Tests/TestHelpers/TestThreadHelper.cs b/test/ODataConnectedService.Tests/TestHelpers/TestThreadHelper.cs new file mode 100644 index 00000000..612f9cce --- /dev/null +++ b/test/ODataConnectedService.Tests/TestHelpers/TestThreadHelper.cs @@ -0,0 +1,16 @@ +using System; + +using System.Threading.Tasks; +using Microsoft.OData.ConnectedService.Threading; + +namespace ODataConnectedService.Tests.TestHelpers +{ + internal class TestThreadHelper : IThreadHelper + { + /// + public Task RunInUiThreadAsync(Func backgroundTask) + { + return Task.Run(backgroundTask); + } + } +} diff --git a/test/ODataConnectedService.Tests/ViewModels/ConfigOdataEndPointViewModelTests.cs b/test/ODataConnectedService.Tests/ViewModels/ConfigOdataEndPointViewModelTests.cs index b3514e6c..265799e4 100644 --- a/test/ODataConnectedService.Tests/ViewModels/ConfigOdataEndPointViewModelTests.cs +++ b/test/ODataConnectedService.Tests/ViewModels/ConfigOdataEndPointViewModelTests.cs @@ -13,20 +13,18 @@ using Microsoft.OData.ConnectedService; using Microsoft.OData.ConnectedService.ViewModels; using Microsoft.VisualStudio.ConnectedServices; -using Microsoft.VisualStudio.TestTools.UnitTesting; using ODataConnectedService.Tests.TestHelpers; +using Xunit; namespace ODataConnectedService.Tests.ViewModels { - [TestClass] public class ConfigOdataEndPointViewModelTests { private static ConfigODataEndpointViewModel configOdataEndPointViewModel; private static UserSettings userSettings; private static ODataConnectedServiceWizard serviceWizard; - [TestInitialize] - public void Init() + public ConfigOdataEndPointViewModelTests() { userSettings = new UserSettings(); serviceWizard = new ODataConnectedServiceWizard(null); @@ -35,8 +33,8 @@ public void Init() CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; } - [TestMethod] - public void OnPageLeavingConfigODataEndpointPageTest() + [StaFact] + public async Task OnPageLeavingConfigODataEndpointPageTestAsync() { string edmx = GeneratedCodeHelpers.LoadReferenceContent("Simple.xml"); string expectedTempfileContent = GeneratedCodeHelpers.LoadReferenceContent("TempSimple.xml"); @@ -48,85 +46,85 @@ public void OnPageLeavingConfigODataEndpointPageTest() //Check if an error is thrown if the on leaving the page without providing the endpoint pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); - pageNavigationResult = pageNavigationResultTask?.Result; - Assert.IsNotNull(pageNavigationResult.ErrorMessage); - Assert.IsTrue(pageNavigationResult.ErrorMessage.Contains(Constants.InputServiceEndpointMsg), "User is not prompted to enter endpoint"); - Assert.IsFalse(pageNavigationResult.IsSuccess); - Assert.IsTrue(pageNavigationResult.ShowMessageBoxOnFailure); + pageNavigationResult = await pageNavigationResultTask; + Assert.NotNull(pageNavigationResult.ErrorMessage); + Assert.True(pageNavigationResult.ErrorMessage.Contains(Constants.InputServiceEndpointMsg), "User is not prompted to enter endpoint"); + Assert.False(pageNavigationResult.IsSuccess); + Assert.True(pageNavigationResult.ShowMessageBoxOnFailure); //Provide a url without $metadata configOdataEndPointViewModel.UserSettings.Endpoint = "http://mysite/ODataService"; pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); //Check if $metadata is appended as the last segment if it was not the last segment of the url - Assert.AreEqual("http://mysite/ODataService/$metadata", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + Assert.Equal("http://mysite/ODataService/$metadata", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); //Provide a url with $metadata/ configOdataEndPointViewModel.UserSettings.Endpoint = "http://mysite/ODataService/$metadata/"; pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); - //Check if $metadata is appended as the last segment and '/' is removed - Assert.AreEqual("http://mysite/ODataService/$metadata", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + // Check if $metadata is appended as the last segment and '/' is removed + Assert.Equal("http://mysite/ODataService/$metadata", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); //Provide a url with "$metadata" as the last segment in the url with fragment and query segments configOdataEndPointViewModel.UserSettings.Endpoint = "http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0#fragment"; _ = configOdataEndPointViewModel.OnPageLeavingAsync(null); - //Check if the url is detected as valid and is unmodified - Assert.AreEqual("http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + // Check if the url is detected as valid and is unmodified + Assert.Equal("http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); - //Provide a url with query and fragment segments without $metadata + // Provide a url with query and fragment segments without $metadata configOdataEndPointViewModel.UserSettings.Endpoint = "http://user:password@mysite/ODataService?$schemaversion=2.0#fragment"; pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); //Check if $metadata is appended as the last segment - Assert.AreEqual("http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + Assert.Equal("http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); //Provide a url with a fragment segment without $metadata configOdataEndPointViewModel.UserSettings.Endpoint = "http://user:password@mysite/ODataService#fragment"; pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); - //Check if $metadata is appended as the last segment - Assert.AreEqual("http://user:password@mysite/ODataService/$metadata#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + // Check if $metadata is appended as the last segment + Assert.Equal("http://user:password@mysite/ODataService/$metadata#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); - //Provide a url with $metadata and a fragment segment without a query segment + // Provide a url with $metadata and a fragment segment without a query segment configOdataEndPointViewModel.UserSettings.Endpoint = "http://user:password@mysite/ODataService/$metadata#fragment"; pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); - //Check if $metadata is appended as the last segment - Assert.AreEqual("http://user:password@mysite/ODataService/$metadata#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + // Check if $metadata is appended as the last segment + Assert.Equal("http://user:password@mysite/ODataService/$metadata#fragment", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); - //Provide a url with $metadata and a query segment without a fragment segment + // Provide a url with $metadata and a query segment without a fragment segment configOdataEndPointViewModel.UserSettings.Endpoint = "http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0"; pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); - //Check if $metadata is appended as the last segment - Assert.AreEqual("http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); + // Check if $metadata is appended as the last segment + Assert.Equal("http://user:password@mysite/ODataService/$metadata?$schemaversion=2.0", configOdataEndPointViewModel.ServiceConfiguration.Endpoint); - //Check if an exception is thrown for an invalid url and the user is notified - pageNavigationResult = pageNavigationResultTask?.Result; - Assert.IsNotNull(pageNavigationResult.ErrorMessage); - Assert.IsTrue(pageNavigationResult.ErrorMessage.Contains("The remote name could not be resolved") + // Check if an exception is thrown for an invalid url and the user is notified + pageNavigationResult = await pageNavigationResultTask; + Assert.NotNull(pageNavigationResult.ErrorMessage); + Assert.True(pageNavigationResult.ErrorMessage.Contains("The remote name could not be resolved") || pageNavigationResult.ErrorMessage.Contains("The remote server returned an error: (407) Proxy Authentication Required")); - Assert.IsFalse(pageNavigationResult.IsSuccess); - Assert.IsTrue(pageNavigationResult.ShowMessageBoxOnFailure); + Assert.False(pageNavigationResult.IsSuccess); + Assert.True(pageNavigationResult.ShowMessageBoxOnFailure); configOdataEndPointViewModel.UserSettings.Endpoint = Path.Combine(Directory.GetCurrentDirectory(), "EdmxFile.xml"); pageNavigationResultTask = configOdataEndPointViewModel.OnPageLeavingAsync(null); //Check if any errors were reported - pageNavigationResult = pageNavigationResultTask?.Result; - Assert.IsNull(pageNavigationResult.ErrorMessage); - Assert.IsTrue(pageNavigationResult.IsSuccess); - Assert.IsFalse(pageNavigationResult.ShowMessageBoxOnFailure); + pageNavigationResult = await pageNavigationResultTask; + Assert.Null(pageNavigationResult.ErrorMessage); + Assert.True(pageNavigationResult.IsSuccess); + Assert.False(pageNavigationResult.ShowMessageBoxOnFailure); //Check if the content writtent to the temp file is correct string actualTempFileContent = File.ReadAllText(configOdataEndPointViewModel.MetadataTempPath); - Assert.AreEqual(expectedTempfileContent.Trim(), actualTempFileContent.Trim(), "temp metadata file not properly written"); + Assert.True(expectedTempfileContent.Trim().Equals(actualTempFileContent.Trim()), "temp metadata file not properly written"); //Check if Edmx verison of has correctly been detected - Assert.AreEqual(configOdataEndPointViewModel.EdmxVersion.ToString(), "4.0.0.0", "Version not properly detected"); + Assert.True(configOdataEndPointViewModel.EdmxVersion.ToString().Equals("4.0.0.0"), "Version not properly detected"); } } }