diff --git a/performance/SqlBindingBenchmarks.cs b/performance/SqlBindingBenchmarks.cs index 9a18dc844..4f4e6c200 100644 --- a/performance/SqlBindingBenchmarks.cs +++ b/performance/SqlBindingBenchmarks.cs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.Diagnostics; using System.Linq; using BenchmarkDotNet.Running; -using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; +using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance { @@ -13,40 +14,49 @@ public static void Main(string[] args) { bool runAll = args.Length == 0; - using var testFixture = new IntegrationTestFixture(); + Process azuriteHost = TestUtils.StartAzurite(); + TestUtils.SetupDatabase(out string masterConnectionString, out string connectionString); + try + { + // **IMPORTANT** If changing these make sure to update template-steps-performance.yml as well + if (runAll || args.Contains("input")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("output")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("trigger")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("trigger_batch")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("trigger_poll")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("trigger_overrides")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("trigger_parallel")) + { + BenchmarkRunner.Run(); + } + if (runAll || args.Contains("trigger_changerate")) + { + BenchmarkRunner.Run(); + } + } + finally + { + TestUtils.StopAzurite(azuriteHost); + TestUtils.DropDatabase(masterConnectionString, connectionString); - // **IMPORTANT** If changing these make sure to update template-steps-performance.yml as well - if (runAll || args.Contains("input")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("output")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("trigger")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("trigger_batch")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("trigger_poll")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("trigger_overrides")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("trigger_parallel")) - { - BenchmarkRunner.Run(); - } - if (runAll || args.Contains("trigger_changerate")) - { - BenchmarkRunner.Run(); } } } diff --git a/test/Common/TestUtils.cs b/test/Common/TestUtils.cs index fa5931269..43f045f86 100644 --- a/test/Common/TestUtils.cs +++ b/test/Common/TestUtils.cs @@ -12,6 +12,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; +using Microsoft.Data.SqlClient; namespace Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common { @@ -22,6 +23,125 @@ public static class TestUtils { internal static int ThreadId; + /// + /// This returns a running Azurite storage emulator. + /// + public static Process StartAzurite() + { + Console.WriteLine("Starting Azurite Host..."); + var azuriteHost = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = "azurite", + WindowStyle = ProcessWindowStyle.Hidden, + UseShellExecute = true + } + }; + + azuriteHost.Start(); + return azuriteHost; + } + + public static void StopAzurite(Process azuriteHost) + { + Console.WriteLine("Stopping Azurite Host..."); + try + { + azuriteHost.Kill(true); + azuriteHost.Dispose(); + } + catch (Exception e) + { + Console.WriteLine($"Failed to stop Azurite, Error: {e.Message}"); + } + } + + /// + /// Sets up a test database for the tests to use. + /// + /// + /// The server the database will be created on can be set by the environment variable "TEST_SERVER", otherwise localhost will be used by default. + /// By default, integrated authentication will be used to connect to the server, unless the env variable "SA_PASSWORD" is set. + /// In this case, connection will be made using SQL login with user "SA" and the provided password. + /// + public static void SetupDatabase(out string MasterConnectionString, out string DatabaseName) + { + SqlConnectionStringBuilder connectionStringBuilder; + string connectionString = Environment.GetEnvironmentVariable("TEST_CONNECTION_STRING"); + string masterConnectionString; + if (connectionString != null) + { + masterConnectionString = connectionString; + connectionStringBuilder = new SqlConnectionStringBuilder(connectionString); + } + else + { + // Get the test server name from environment variable "TEST_SERVER", default to localhost if not set + string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); + if (string.IsNullOrEmpty(testServer)) + { + testServer = "localhost"; + } + + // First connect to master to create the database + connectionStringBuilder = new SqlConnectionStringBuilder() + { + DataSource = testServer, + InitialCatalog = "master", + Pooling = false, + Encrypt = SqlConnectionEncryptOption.Optional + }; + + // Either use integrated auth or SQL login depending if SA_PASSWORD is set + string userId = "SA"; + string password = Environment.GetEnvironmentVariable("SA_PASSWORD"); + if (string.IsNullOrEmpty(password)) + { + connectionStringBuilder.IntegratedSecurity = true; + } + else + { + connectionStringBuilder.UserID = userId; + connectionStringBuilder.Password = password; + } + masterConnectionString = connectionStringBuilder.ToString(); + } + + // Create database + // Retry this in case the server isn't fully initialized yet + string databaseName = GetUniqueDBName("SqlBindingsTest"); + Retry(() => + { + using var masterConnection = new SqlConnection(masterConnectionString); + masterConnection.Open(); + ExecuteNonQuery(masterConnection, $"CREATE DATABASE [{databaseName}]", Console.WriteLine); + // Enable change tracking for trigger tests + ExecuteNonQuery(masterConnection, $"ALTER DATABASE [{databaseName}] SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);", Console.WriteLine); + }, Console.WriteLine); + + connectionStringBuilder.InitialCatalog = databaseName; + + // Set SqlConnectionString env var for the tests to use + Environment.SetEnvironmentVariable("SqlConnectionString", connectionStringBuilder.ToString()); + MasterConnectionString = masterConnectionString; + DatabaseName = databaseName; + } + + public static void DropDatabase(string masterConnectionString, string databaseName) + { + try + { + using var masterConnection = new SqlConnection(masterConnectionString); + masterConnection.Open(); + ExecuteNonQuery(masterConnection, $"DROP DATABASE IF EXISTS {databaseName}", Console.WriteLine); + } + catch (Exception e) + { + Console.WriteLine($"Failed to drop {databaseName}, Error: {e.Message}"); + } + } + /// /// Returns a mangled name that unique based on Prefix + Machine + Process /// diff --git a/test/Integration/IntegrationTestFixture.cs b/test/Integration/IntegrationTestFixture.cs index 2d864035a..e89b55d52 100644 --- a/test/Integration/IntegrationTestFixture.cs +++ b/test/Integration/IntegrationTestFixture.cs @@ -7,7 +7,6 @@ using System.IO; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; -using Microsoft.Data.SqlClient; using Xunit; using static Microsoft.Azure.WebJobs.Extensions.Sql.Telemetry.Telemetry; @@ -22,17 +21,17 @@ public class IntegrationTestFixture : IDisposable /// Host process for Azurite local storage emulator. This is required for non-HTTP trigger functions: /// https://docs.microsoft.com/azure/azure-functions/functions-develop-local /// - private Process AzuriteHost; + private readonly Process AzuriteHost; /// /// Connection string to the master database on the test server, mainly used for database setup and teardown. /// - private string MasterConnectionString; + private readonly string MasterConnectionString; /// /// Name of the database used. /// - private string DatabaseName; + private readonly string DatabaseName; /// /// List of all functions in the samples folder that will be started before the @@ -56,98 +55,11 @@ public class IntegrationTestFixture : IDisposable public IntegrationTestFixture() { - this.StartAzurite(); - this.SetupDatabase(); + this.AzuriteHost = TestUtils.StartAzurite(); + TestUtils.SetupDatabase(out this.MasterConnectionString, out this.DatabaseName); this.StartFunctionHosts(); } - /// - /// This starts the Azurite storage emulator. - /// - protected void StartAzurite() - { - Console.WriteLine("Starting Azurite Host..."); - this.AzuriteHost = new Process() - { - StartInfo = new ProcessStartInfo - { - FileName = "azurite", - WindowStyle = ProcessWindowStyle.Hidden, - UseShellExecute = true - } - }; - - this.AzuriteHost.Start(); - } - - /// - /// Sets up a test database for the tests to use. - /// - /// - /// The server the database will be created on can be set by the environment variable "TEST_SERVER", otherwise localhost will be used by default. - /// By default, integrated authentication will be used to connect to the server, unless the env variable "SA_PASSWORD" is set. - /// In this case, connection will be made using SQL login with user "SA" and the provided password. - /// - private void SetupDatabase() - { - SqlConnectionStringBuilder connectionStringBuilder; - string connectionString = Environment.GetEnvironmentVariable("TEST_CONNECTION_STRING"); - if (connectionString != null) - { - this.MasterConnectionString = connectionString; - connectionStringBuilder = new SqlConnectionStringBuilder(connectionString); - } - else - { - // Get the test server name from environment variable "TEST_SERVER", default to localhost if not set - string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); - if (string.IsNullOrEmpty(testServer)) - { - testServer = "localhost"; - } - - // First connect to master to create the database - connectionStringBuilder = new SqlConnectionStringBuilder() - { - DataSource = testServer, - InitialCatalog = "master", - Pooling = false, - Encrypt = SqlConnectionEncryptOption.Optional - }; - - // Either use integrated auth or SQL login depending if SA_PASSWORD is set - string userId = "SA"; - string password = Environment.GetEnvironmentVariable("SA_PASSWORD"); - if (string.IsNullOrEmpty(password)) - { - connectionStringBuilder.IntegratedSecurity = true; - } - else - { - connectionStringBuilder.UserID = userId; - connectionStringBuilder.Password = password; - } - this.MasterConnectionString = connectionStringBuilder.ToString(); - } - - // Create database - // Retry this in case the server isn't fully initialized yet - this.DatabaseName = TestUtils.GetUniqueDBName("SqlBindingsTest"); - TestUtils.Retry(() => - { - using var masterConnection = new SqlConnection(this.MasterConnectionString); - masterConnection.Open(); - TestUtils.ExecuteNonQuery(masterConnection, $"CREATE DATABASE [{this.DatabaseName}]", Console.WriteLine); - // Enable change tracking for trigger tests - TestUtils.ExecuteNonQuery(masterConnection, $"ALTER DATABASE [{this.DatabaseName}] SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);", Console.WriteLine); - }, Console.WriteLine); - - connectionStringBuilder.InitialCatalog = this.DatabaseName; - - // Set SqlConnectionString env var for the tests to use - Environment.SetEnvironmentVariable("SqlConnectionString", connectionStringBuilder.ToString()); - } - /// /// This starts the function hosts for each language. /// @@ -257,28 +169,8 @@ void TestOutputHandler(object sender, DataReceivedEventArgs e) public void Dispose() { this.DisposeFunctionHosts(); - - try - { - this.AzuriteHost.Kill(true); - this.AzuriteHost.Dispose(); - } - catch (Exception e) - { - Console.WriteLine($"Failed to stop Azurite, Error: {e.Message}"); - } - - try - { - // Drop the test database - using var masterConnection = new SqlConnection(this.MasterConnectionString); - masterConnection.Open(); - TestUtils.ExecuteNonQuery(masterConnection, $"DROP DATABASE IF EXISTS {this.DatabaseName}", Console.WriteLine); - } - catch (Exception e4) - { - Console.WriteLine($"Failed to drop {this.DatabaseName}, Error: {e4.Message}"); - } + TestUtils.StopAzurite(this.AzuriteHost); + TestUtils.DropDatabase(this.MasterConnectionString, this.DatabaseName); GC.SuppressFinalize(this); }