diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Driver/ServiceTestDriver.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Driver/ServiceTestDriver.cs index d269bf97..51e46ffe 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Driver/ServiceTestDriver.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Driver/ServiceTestDriver.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel; +using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver @@ -93,6 +94,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver // Setup events to queue for testing this.QueueEventsForType(ConnectionCompleteNotification.Type); this.QueueEventsForType(QueryExecuteCompleteEvent.Type); + this.QueueEventsForType(PublishDiagnosticsNotification.Type); } /// diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Program.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Program.cs index 42936ec0..6b6e23f7 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Program.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Program.cs @@ -4,6 +4,7 @@ // using System; +using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver; @@ -31,20 +32,36 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver Task.Run(async () => { + string testNamespace = "Microsoft.SqlTools.ServiceLayer.TestDriver.Tests."; foreach (var test in args) { try { - var className = test.Substring(0, test.LastIndexOf('.')); - var methodName = test.Substring(test.LastIndexOf('.') + 1); + var testName = test.Contains(testNamespace) ? test.Replace(testNamespace, "") : test; + bool containsTestName = testName.Contains("."); + var className = containsTestName ? testName.Substring(0, testName.LastIndexOf('.')) : testName; + var methodName = containsTestName ? testName.Substring(testName.LastIndexOf('.') + 1) : null; - var type = Type.GetType("Microsoft.SqlTools.ServiceLayer.TestDriver.Tests." + className); - using (var typeInstance = (IDisposable)Activator.CreateInstance(type)) + var type = Type.GetType(testNamespace + className); + if (type == null) { - MethodInfo methodInfo = type.GetMethod(methodName); - - Console.WriteLine("Running test " + test); - await (Task)methodInfo.Invoke(typeInstance, null); + Console.WriteLine("Invalid class name"); + } + else + { + if (string.IsNullOrEmpty(methodName)) + { + var methods = type.GetMethods().Where(x => x.CustomAttributes.Any(a => a.AttributeType == typeof(FactAttribute))); + foreach (var method in methods) + { + await RunTest(type, method, method.Name); + } + } + else + { + MethodInfo methodInfo = type.GetMethod(methodName); + await RunTest(type, methodInfo, test); + } } } catch (Exception ex) @@ -54,5 +71,22 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver } }).Wait(); } + + private static async Task RunTest(Type type, MethodInfo methodInfo, string testName) + { + if (methodInfo == null) + { + Console.WriteLine("Invalid method name"); + } + else + { + using (var typeInstance = (IDisposable)Activator.CreateInstance(type)) + { + Console.WriteLine("Running test " + testName); + await (Task)methodInfo.Invoke(typeInstance, null); + Console.WriteLine("Test ran successfully: " + testName); + } + } + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Scripts/AdventureWorks.sql b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Scripts/AdventureWorks.sql new file mode 100644 index 00000000..fa6b46da --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Scripts/AdventureWorks.sql @@ -0,0 +1,682 @@ +USE [AdventureWorks2016CTP3] +GO + +SELECT [SystemInformationID] + ,[Database Version] + ,[VersionDate] + ,[ModifiedDate] + FROM [dbo].[AWBuildVersion] +GO + + +GO + +SELECT [DatabaseLogID] + ,[PostTime] + ,[DatabaseUser] + ,[Event] + ,[Schema] + ,[Object] + ,[TSQL] + ,[XmlEvent] + FROM [dbo].[DatabaseLog] +GO + + +SELECT [OrderQty] + ,[ProductID] + ,[SpecialOfferID] + ,[OrderID] + ,[LocalID] + FROM [Demo].[DemoSalesOrderDetailSeed] +GO + +SELECT [DueDate] + ,[CustomerID] + ,[SalesPersonID] + ,[BillToAddressID] + ,[ShipToAddressID] + ,[ShipMethodID] + ,[LocalID] + FROM [Demo].[DemoSalesOrderHeaderSeed] +GO + +SELECT [DepartmentID] + ,[Name] + ,[GroupName] + ,[ModifiedDate] + FROM [HumanResources].[Department] +GO + +SELECT [BusinessEntityID] + ,[NationalIDNumber] + ,[LoginID] + ,[OrganizationNode] + ,[OrganizationLevel] + ,[JobTitle] + ,[BirthDate] + ,[MaritalStatus] + ,[Gender] + ,[HireDate] + ,[SalariedFlag] + ,[VacationHours] + ,[SickLeaveHours] + ,[CurrentFlag] + ,[rowguid] + ,[ModifiedDate] + FROM [HumanResources].[Employee] +GO + +SELECT [TrackingEventID] + ,[EventName] + FROM [Sales].[TrackingEvent] +GO + +SELECT [SalesTaxRateID] + ,[StateProvinceID] + ,[TaxType] + ,[TaxRate] + ,[Name] + ,[rowguid] + ,[ModifiedDate] + FROM [Sales].[SalesTaxRate] +GO + +SELECT [SalesOrderID] + ,[SalesOrderDetailID] + ,[CarrierTrackingNumber] + ,[OrderQty] + ,[ProductID] + ,[SpecialOfferID] + ,[UnitPrice] + ,[UnitPriceDiscount] + ,[ModifiedDate] + FROM [Sales].[SalesOrderDetail_inmem] +GO + +SELECT [SalesOrderID] + ,[RevisionNumber] + ,[OrderDate] + ,[DueDate] + ,[ShipDate] + ,[Status] + ,[OnlineOrderFlag] + ,[SalesOrderNumber] + ,[PurchaseOrderNumber] + ,[AccountNumber] + ,[CustomerID] + ,[SalesPersonID] + ,[TerritoryID] + ,[BillToAddressID] + ,[ShipToAddressID] + ,[ShipMethodID] + ,[CreditCardID] + ,[CreditCardApprovalCode] + ,[CurrencyRateID] + ,[SubTotal] + ,[TaxAmt] + ,[Freight] + ,[TotalDue] + ,[Comment] + ,[rowguid] + ,[ModifiedDate] + FROM [Sales].[SalesOrderHeader] +GO + + + +SELECT [SalesOrderID] + ,[RevisionNumber] + ,[OrderDate] + ,[DueDate] + ,[ShipDate] + ,[Status] + ,[OnlineOrderFlag] + ,[SalesOrderNumber] + ,[PurchaseOrderNumber] + ,[AccountNumber] + ,[CustomerID] + ,[SalesPersonID] + ,[TerritoryID] + ,[BillToAddressID] + ,[ShipToAddressID] + ,[ShipMethodID] + ,[CreditCardID] + ,[CreditCardApprovalCode] + ,[CurrencyRateID] + ,[SubTotal] + ,[TaxAmt] + ,[Freight] + ,[TotalDue] + ,[Comment] + ,[rowguid] + ,[ModifiedDate] + FROM [Sales].[SalesOrder_json] +GO + +SELECT [BusinessEntityID] + ,[CreditCardID] + ,[ModifiedDate] + FROM [Sales].[PersonCreditCard] +GO + +SELECT [OrderTrackingID] + ,[SalesOrderID] + ,[CarrierTrackingNumber] + ,[TrackingEventID] + ,[EventDetails] + ,[EventDateTime] + FROM [Sales].[OrderTracking] +GO + +SELECT [CustomerID] + ,[FirstName] + ,[LastName] + ,[SSN] + ,[CreditCardNumber] + ,[EmailAddress] + ,[PhoneNumber] + ,[TerritoryID] + FROM [Sales].[CustomerPII] +GO + +SELECT [CustomerID] + ,[PersonID] + ,[StoreID] + ,[TerritoryID] + ,[AccountNumber] + ,[rowguid] + ,[ModifiedDate] + FROM [Sales].[Customer] +GO + +SELECT [CurrencyRateID] + ,[CurrencyRateDate] + ,[FromCurrencyCode] + ,[ToCurrencyCode] + ,[AverageRate] + ,[EndOfDayRate] + ,[ModifiedDate] + FROM [Sales].[CurrencyRate] +GO + + +SELECT [CurrencyCode] + ,[Name] + ,[ModifiedDate] + FROM [Sales].[Currency] +GO + +SELECT [CreditCardID] + ,[CardType] + ,[CardNumber] + ,[ExpMonth] + ,[ExpYear] + ,[ModifiedDate] + FROM [Sales].[CreditCard] +GO + +SELECT [CountryRegionCode] + ,[CurrencyCode] + ,[ModifiedDate] + FROM [Sales].[CountryRegionCurrency] +GO + +SELECT [BusinessEntityID] + ,[AccountNumber] + ,[Name] + ,[CreditRating] + ,[PreferredVendorStatus] + ,[ActiveFlag] + ,[PurchasingWebServiceURL] + ,[ModifiedDate] + FROM [Purchasing].[Vendor] +GO + +SELECT [ShipMethodID] + ,[Name] + ,[ShipBase] + ,[ShipRate] + ,[rowguid] + ,[ModifiedDate] + FROM [Purchasing].[ShipMethod] +GO + +SELECT [PurchaseOrderID] + ,[RevisionNumber] + ,[Status] + ,[EmployeeID] + ,[VendorID] + ,[ShipMethodID] + ,[OrderDate] + ,[ShipDate] + ,[SubTotal] + ,[TaxAmt] + ,[Freight] + ,[TotalDue] + ,[ModifiedDate] + FROM [Purchasing].[PurchaseOrderHeader] +GO + +SELECT [PurchaseOrderID] + ,[PurchaseOrderDetailID] + ,[DueDate] + ,[OrderQty] + ,[ProductID] + ,[UnitPrice] + ,[LineTotal] + ,[ReceivedQty] + ,[RejectedQty] + ,[StockedQty] + ,[ModifiedDate] + FROM [Purchasing].[PurchaseOrderDetail] +GO + +SELECT [LocationID] + ,[Name] + ,[CostRate] + ,[Availability] + ,[ModifiedDate] + FROM [Production].[Location] +GO + + +SELECT [ProductID] + ,[Name] + ,[ProductNumber] + ,[MakeFlag] + ,[FinishedGoodsFlag] + ,[Color] + ,[SafetyStockLevel] + ,[ReorderPoint] + ,[StandardCost] + ,[ListPrice] + ,[Size] + ,[SizeUnitMeasureCode] + ,[WeightUnitMeasureCode] + ,[Weight] + ,[DaysToManufacture] + ,[ProductLine] + ,[Class] + ,[Style] + ,[ProductSubcategoryID] + ,[ProductModelID] + ,[SellStartDate] + ,[SellEndDate] + ,[DiscontinuedDate] + ,[rowguid] + ,[ModifiedDate] + FROM [Production].[Product] +GO + +SELECT [ProductID] + ,[Name] + ,[ProductNumber] + ,[MakeFlag] + ,[FinishedGoodsFlag] + ,[Color] + ,[SafetyStockLevel] + ,[ReorderPoint] + ,[StandardCost] + ,[ListPrice] + ,[Size] + ,[SizeUnitMeasureCode] + ,[WeightUnitMeasureCode] + ,[Weight] + ,[DaysToManufacture] + ,[ProductLine] + ,[Class] + ,[Style] + ,[ProductSubcategoryID] + ,[ProductModelID] + ,[SellStartDate] + ,[SellEndDate] + ,[DiscontinuedDate] + ,[ModifiedDate] + FROM [Production].[Product_inmem] +GO + +SELECT [ProductID] + ,[Name] + ,[ProductNumber] + ,[MakeFlag] + ,[FinishedGoodsFlag] + ,[Color] + ,[SafetyStockLevel] + ,[ReorderPoint] + ,[StandardCost] + ,[ListPrice] + ,[Size] + ,[SizeUnitMeasureCode] + ,[WeightUnitMeasureCode] + ,[Weight] + ,[DaysToManufacture] + ,[ProductLine] + ,[Class] + ,[Style] + ,[ProductSubcategoryID] + ,[ProductModelID] + ,[SellStartDate] + ,[SellEndDate] + ,[DiscontinuedDate] + ,[ModifiedDate] + FROM [Production].[Product_ondisk] +GO + +SELECT [ProductCategoryID] + ,[Name] + ,[rowguid] + ,[ModifiedDate] + FROM [Production].[ProductCategory] +GO + +SELECT [ProductID] + ,[StartDate] + ,[EndDate] + ,[StandardCost] + ,[ModifiedDate] + FROM [Production].[ProductCostHistory] +GO + +SELECT [ProductDescriptionID] + ,[Description] + ,[rowguid] + ,[ModifiedDate] + FROM [Production].[ProductDescription] +GO + + +SELECT [ProductID] + ,[DocumentNode] + ,[ModifiedDate] + FROM [Production].[ProductDocument] +GO + +SELECT [ProductID] + ,[LocationID] + ,[Shelf] + ,[Bin] + ,[Quantity] + ,[rowguid] + ,[ModifiedDate] + FROM [Production].[ProductInventory] +GO + +SELECT [ProductID] + ,[StartDate] + ,[EndDate] + ,[ListPrice] + ,[ModifiedDate] + FROM [Production].[ProductListPriceHistory] +GO + +SELECT [ProductModelID] + ,[Name] + ,[CatalogDescription] + ,[Instructions] + ,[rowguid] + ,[ModifiedDate] + FROM [Production].[ProductModel] +GO + +SELECT [ProductModelID] + ,[IllustrationID] + ,[ModifiedDate] + FROM [Production].[ProductModelIllustration] +GO + +SELECT [ProductModelID] + ,[IllustrationID] + ,[ModifiedDate] + FROM [Production].[ProductModelIllustration] +GO +SELECT [ProductModelID] + ,[ProductDescriptionID] + ,[CultureID] + ,[ModifiedDate] + FROM [Production].[ProductModelProductDescriptionCulture] +GO +SELECT [ProductPhotoID] + ,[ThumbNailPhoto] + ,[ThumbnailPhotoFileName] + ,[LargePhoto] + ,[LargePhotoFileName] + ,[ModifiedDate] + FROM [Production].[ProductPhoto] +GO +SELECT [ProductID] + ,[ProductPhotoID] + ,[Primary] + ,[ModifiedDate] + FROM [Production].[ProductProductPhoto] +GO +SELECT [ProductReviewID] + ,[ProductID] + ,[ReviewerName] + ,[ReviewDate] + ,[EmailAddress] + ,[Rating] + ,[Comments] + ,[ModifiedDate] + FROM [Production].[ProductReview] +GO + +SELECT [TransactionID] + ,[ProductID] + ,[ReferenceOrderID] + ,[ReferenceOrderLineID] + ,[TransactionDate] + ,[TransactionType] + ,[Quantity] + ,[ActualCost] + ,[ModifiedDate] + FROM [Production].[TransactionHistory] +GO + +SELECT [TransactionID] + ,[ProductID] + ,[ReferenceOrderID] + ,[ReferenceOrderLineID] + ,[TransactionDate] + ,[TransactionType] + ,[Quantity] + ,[ActualCost] + ,[ModifiedDate] + FROM [Production].[TransactionHistoryArchive] +GO +SELECT [UnitMeasureCode] + ,[Name] + ,[ModifiedDate] + FROM [Production].[UnitMeasure] +GO + +SELECT [WorkOrderID] + ,[ProductID] + ,[OrderQty] + ,[StockedQty] + ,[ScrappedQty] + ,[StartDate] + ,[EndDate] + ,[DueDate] + ,[ScrapReasonID] + ,[ModifiedDate] + FROM [Production].[WorkOrder] +GO +SELECT [WorkOrderID] + ,[ProductID] + ,[OperationSequence] + ,[LocationID] + ,[ScheduledStartDate] + ,[ScheduledEndDate] + ,[ActualStartDate] + ,[ActualEndDate] + ,[ActualResourceHrs] + ,[PlannedCost] + ,[ActualCost] + ,[ModifiedDate] + FROM [Production].[WorkOrderRouting] +GO + +SELECT [ProductID] + ,[BusinessEntityID] + ,[AverageLeadTime] + ,[StandardPrice] + ,[LastReceiptCost] + ,[LastReceiptDate] + ,[MinOrderQty] + ,[MaxOrderQty] + ,[OnOrderQty] + ,[UnitMeasureCode] + ,[ModifiedDate] + FROM [Purchasing].[ProductVendor] +GO + +SELECT [PurchaseOrderID] + ,[PurchaseOrderDetailID] + ,[DueDate] + ,[OrderQty] + ,[ProductID] + ,[UnitPrice] + ,[LineTotal] + ,[ReceivedQty] + ,[RejectedQty] + ,[StockedQty] + ,[ModifiedDate] + FROM [Purchasing].[PurchaseOrderDetail] +GO +SELECT [AddressID] + ,[AddressLine1] + ,[AddressLine2] + ,[City] + ,[StateProvinceID] + ,[PostalCode] + ,[SpatialLocation] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[Address] +GO +SELECT [AddressTypeID] + ,[Name] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[AddressType] +GO +SELECT [BusinessEntityID] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[BusinessEntity] +GO +SELECT [BusinessEntityID] + ,[AddressID] + ,[AddressTypeID] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[BusinessEntityAddress] +GO +SELECT [BusinessEntityID] + ,[PersonID] + ,[ContactTypeID] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[BusinessEntityContact] +GO +SELECT [ContactTypeID] + ,[Name] + ,[ModifiedDate] + FROM [Person].[ContactType] +GO +SELECT [CountryRegionCode] + ,[Name] + ,[ModifiedDate] + FROM [Person].[CountryRegion] +GO + +SELECT [BusinessEntityID] + ,[EmailAddressID] + ,[EmailAddress] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[EmailAddress] +GO +SELECT [BusinessEntityID] + ,[PasswordHash] + ,[PasswordSalt] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[Password] +GO + +SELECT [BusinessEntityID] + ,[PersonType] + ,[NameStyle] + ,[Title] + ,[FirstName] + ,[MiddleName] + ,[LastName] + ,[Suffix] + ,[EmailPromotion] + ,[AdditionalContactInfo] + ,[Demographics] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[Person] +GO +SELECT [PersonID] + ,[PersonType] + ,[NameStyle] + ,[Title] + ,[FirstName] + ,[MiddleName] + ,[LastName] + ,[Suffix] + ,[EmailPromotion] + ,[AdditionalContactInfo] + ,[Demographics] + ,[rowguid] + ,[ModifiedDate] + FROM [Person].[Person_json] +GO +SELECT [BusinessEntityID] + ,[NationalIDNumber] + ,[LoginID] + ,[OrganizationNode] + ,[OrganizationLevel] + ,[JobTitle] + ,[BirthDate] + ,[MaritalStatus] + ,[Gender] + ,[HireDate] + ,[VacationHours] + ,[SickLeaveHours] + ,[ValidFrom] + ,[ValidTo] + FROM [HumanResources].[Employee_Temporal] +GO +SELECT [BusinessEntityID] + ,[DepartmentID] + ,[ShiftID] + ,[StartDate] + ,[EndDate] + ,[ModifiedDate] + FROM [HumanResources].[EmployeeDepartmentHistory] +GO +SELECT [BusinessEntityID] + ,[RateChangeDate] + ,[Rate] + ,[PayFrequency] + ,[ModifiedDate] + FROM [HumanResources].[EmployeePayHistory] +GO +SELECT [JobCandidateID] + ,[BusinessEntityID] + ,[Resume] + ,[ModifiedDate] + FROM [HumanResources].[JobCandidate] +GO +SELECT [ShiftID] + ,[Name] + ,[StartTime] + ,[EndTime] + ,[ModifiedDate] + FROM [HumanResources].[Shift] +GO diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/PerformanceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/PerformanceTests.cs new file mode 100644 index 00000000..77007069 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/PerformanceTests.cs @@ -0,0 +1,562 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts; +using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; +using Microsoft.SqlTools.ServiceLayer.TestDriver.Utility; +using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; +using Xunit; + +namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests +{ + public class PerformanceTests : TestBase + { + private static string ComplexQuery = File.ReadAllText("./Scripts/AdventureWorks.sql"); + private static string SimpleQuery = "SELECT * FROM sys.all_columns"; + + [Fact] + public async Task HoverTestOnPrem() + { + try + { + string ownerUri = Path.GetTempFileName(); + string query = SimpleQuery; + + await ConnectAsync(TestServerType.OnPrem, query, ownerUri); + Hover hover = await CalculateRunTime("Hover", async () => + { + return await RequestHover(ownerUri, query, 0, 15); ; + }); + Assert.True(hover != null, "Hover tool-tip is not null"); + + await Disconnect(ownerUri); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task SuggestionsTest() + { + try + { + string query = SimpleQuery; + TestServerType serverType = TestServerType.OnPrem; + string ownerUri = Path.GetTempFileName(); + + WriteToFile(ownerUri, query); + + await ConnectAsync(serverType, query, ownerUri); + await ValidateCompletionResponse(ownerUri, query, null); + + await ValidateCompletionResponse(ownerUri, query, "Suggestions"); + + await Disconnect(ownerUri); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task DiagnosticsTests() + { + try + { + string ownerUri = Path.GetTempFileName(); + string query = "SELECT * FROM sys.objects"; + + await ConnectAsync(TestServerType.OnPrem, query, ownerUri); + Thread.Sleep(500); + + var contentChanges = new TextDocumentChangeEvent[1]; + contentChanges[0] = new TextDocumentChangeEvent() + { + Range = new Range() + { + Start = new Position() + { + Line = 0, + Character = 5 + }, + End = new Position() + { + Line = 0, + Character = 6 + } + }, + RangeLength = 1, + Text = "z" + }; + + DidChangeTextDocumentParams changeParams = new DidChangeTextDocumentParams() + { + ContentChanges = contentChanges, + TextDocument = new VersionedTextDocumentIdentifier() + { + Version = 2, + Uri = ownerUri + } + }; + TestTimer timer = new TestTimer(); + await RequestChangeTextDocumentNotification(changeParams); + + while (true) + { + var completeEvent = await Driver.WaitForEvent(PublishDiagnosticsNotification.Type, 15000); + if (completeEvent != null && completeEvent.Diagnostics != null && completeEvent.Diagnostics.Length > 0) + { + timer.EndAndPrint("Diagnostics"); + break; + } + if (timer.TotalMilliSecondsUntilNow >= 500000) + { + Assert.True(false, "Failed to get Diagnostics"); + break; + } + } + + await Disconnect(ownerUri); + } + finally + { + WaitForExit(); + } + } + + private async Task ValidateCompletionResponse(string ownerUri, string query, string testName) + { + TestTimer timer = new TestTimer(); + CompletionItem completion = null; + while (true) + { + CompletionItem[] completions = await RequestCompletion(ownerUri, query, 0, 15); + + completion = completions != null ? completions.FirstOrDefault(x => x.Label == "master") : null; + if (completion != null) + { + if (testName != null) + { + timer.EndAndPrint(testName); + } + break; + } + if (timer.TotalMilliSecondsUntilNow >= 500000) + { + Assert.True(false, "Failed to get a valid auto-complete list"); + break; + } + + Thread.Sleep(50); + } + } + + private async Task VerifyBindingLoadScenario(TestServerType serverType, string query, string testName = null) + { + string ownerUri = Path.GetTempFileName(); + + WriteToFile(ownerUri, query); + + await ConnectAsync(serverType, query, ownerUri); + await ValidateCompletionResponse(ownerUri, query, testName); + + await Disconnect(ownerUri); + } + + [Fact] + public async Task BindingCacheColdAzureSimpleQuery() + { + try + { + string query = SimpleQuery; + Thread.Sleep(5000); + await VerifyBindingLoadScenario(TestServerType.Azure, query, "[Simple query][Cold][SQL DB] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheColdOnPremSimpleQuery() + { + try + { + string query = SimpleQuery; + await VerifyBindingLoadScenario(TestServerType.OnPrem, query, "[Simple query][Cold][On-Prem] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheWarmAzureSimpleQuery() + { + try + { + string query = SimpleQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.Azure; + await ConnectAsync(serverType, query, ownerUri); + Thread.Sleep(10000); + await VerifyBindingLoadScenario(serverType, query, "[Simple query][Warm][SQL DB] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheWarmOnPremSimpleQuery() + { + try + { + string query = SimpleQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + await ConnectAsync(serverType, query, ownerUri); + Thread.Sleep(10000); + await VerifyBindingLoadScenario(serverType, query, "[Simple query][Warm][On-Prem] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheColdAzureComplexQuery() + { + try + { + string query = ComplexQuery; + await VerifyBindingLoadScenario(TestServerType.Azure, query, "[Complex query][Cold][SQL DB] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheColdOnPremComplexQuery() + { + try + { + string query = ComplexQuery; + await VerifyBindingLoadScenario(TestServerType.OnPrem, query, "[Complex query][Cold][On-Prem] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheWarmAzureComplexQuery() + { + try + { + string query = ComplexQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.Azure; + await ConnectAsync(serverType, query, ownerUri); + Thread.Sleep(100000); + await VerifyBindingLoadScenario(serverType, query, "[Complex query][Warm][SQL DB] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task BindingCacheWarmOnPremComplexQuery() + { + try + { + string query = ComplexQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + await ConnectAsync(serverType, query, ownerUri); + Thread.Sleep(10000); + await VerifyBindingLoadScenario(serverType, query, "[Complex query][Warm][On-Prem] Binding cache"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task ConnectAzureTest() + { + try + { + string query = SimpleQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.Azure; + WriteToFile(ownerUri, query); + + DidOpenTextDocumentNotification openParams = new DidOpenTextDocumentNotification() + { + TextDocument = new TextDocumentItem() + { + Uri = ownerUri, + LanguageId = "enu", + Version = 1, + Text = query + } + }; + + await RequestOpenDocumentNotification(openParams); + + Thread.Sleep(500); + var connected = await CalculateRunTime("Connect SQL DB", async () => + { + var connectParams = await GetDatabaseConnectionAsync(serverType); + return await Connect(ownerUri, connectParams); + }); + Assert.True(connected, "Connection is successful"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task ConnectOnPremTest() + { + try + { + string query = SimpleQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + WriteToFile(ownerUri, query); + + DidOpenTextDocumentNotification openParams = new DidOpenTextDocumentNotification() + { + TextDocument = new TextDocumentItem() + { + Uri = ownerUri, + LanguageId = "enu", + Version = 1, + Text = query + } + }; + + await RequestOpenDocumentNotification(openParams); + + Thread.Sleep(500); + var connected = await CalculateRunTime("Connect On-Prem", async () => + { + var connectParams = await GetDatabaseConnectionAsync(serverType); + return await Connect(ownerUri, connectParams); + }); + Assert.True(connected, "Connection is successful"); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task Disconnect() + { + try + { + string query = SimpleQuery; + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + await ConnectAsync(serverType, query, ownerUri); + Thread.Sleep(1000); + var connected = await CalculateRunTime("Disconnect On-Prem", async () => + { + return await Disconnect(ownerUri); + }); + Assert.True(connected); + } + finally + { + WaitForExit(); + } + } + + [Fact] + public async Task QueryResultSummaryOnPremTest() + { + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + string query = SimpleQuery; + + await ConnectAsync(serverType, query, ownerUri); + + var queryTask = await CalculateRunTime("Basic Query Result On-Prem", async () => + { + return await RunQuery(ownerUri, query); + }); + + Assert.NotNull(queryTask); + Assert.True(queryTask.BatchSummaries.Any(x => x.ResultSetSummaries.Any( r => r.RowCount > 0))); + + await Disconnect(ownerUri); + } + + [Fact] + public async Task QueryResultFirstOnPremTest() + { + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + string query = SimpleQuery; + + await ConnectAsync(serverType, query, ownerUri); + + var queryResult = await CalculateRunTime("Basic Query Result First Rows On-Prem", async () => + { + var queryTask = await RunQuery(ownerUri, query); + return await ExecuteSubset(ownerUri, 0, 0, 0, 100); + }); + + Assert.NotNull(queryResult); + Assert.NotNull(queryResult.ResultSubset); + Assert.True(queryResult.ResultSubset.Rows.Count() > 0); + + await Disconnect(ownerUri); + } + + + [Fact] + public async Task CancelQueryOnPremTest() + { + string ownerUri = Path.GetTempFileName(); + TestServerType serverType = TestServerType.OnPrem; + string query = "WAITFOR DELAY '00:01:00';"; + + await ConnectAsync(serverType, query, ownerUri); + var queryParams = new QueryExecuteParams(); + queryParams.OwnerUri = ownerUri; + queryParams.QuerySelection = null; + + var result = await Driver.SendRequest(QueryExecuteRequest.Type, queryParams); + if (result != null && string.IsNullOrEmpty(result.Messages)) + { + TestTimer timer = new TestTimer(); + + while (true) + { + var queryTask = await CancelQuery(ownerUri); + if (queryTask != null) + { + timer.EndAndPrint("Cancel Query On-Prem"); + break; + } + if (timer.TotalMilliSecondsUntilNow >= 100000) + { + Assert.True(false, "Failed to cancel query"); + break; + } + + Thread.Sleep(10); + } + } + else + { + Assert.True(false, "Failed to run the query"); + } + + await Disconnect(ownerUri); + } + + [Fact] + public async Task TestSaveResultsToCsvTest() + { + string ownerUri = Path.GetTempFileName(); + string query = SimpleQuery; + TestServerType serverType = TestServerType.OnPrem; + string output = Path.GetTempFileName(); + await ConnectAsync(serverType, query, ownerUri); + + // Execute a query + await RunQuery(ownerUri, query); + + var saveTask = await CalculateRunTime("Basic Query Save To CSV", async () => + { + return await SaveAsCsv(ownerUri, output, 0, 0); + }); + + await Disconnect(ownerUri); + } + + [Fact] + public async Task TestSaveResultsToJsonTest() + { + string ownerUri = Path.GetTempFileName(); + string query = SimpleQuery; + TestServerType serverType = TestServerType.OnPrem; + await ConnectAsync(serverType, query, ownerUri); + string output = Path.GetTempFileName(); + // Execute a query + await RunQuery(ownerUri, query); + + var saveTask = await CalculateRunTime("Basic Query Save To Json", async () => + { + return await SaveAsJson(ownerUri, output, 0, 0); + }); + + await Disconnect(ownerUri); + } + + private async Task ConnectAsync(TestServerType serverType, string query, string ownerUri) + { + WriteToFile(ownerUri, query); + + DidOpenTextDocumentNotification openParams = new DidOpenTextDocumentNotification() + { + TextDocument = new TextDocumentItem() + { + Uri = ownerUri, + LanguageId = "enu", + Version = 1, + Text = query + } + }; + + await RequestOpenDocumentNotification(openParams); + + Thread.Sleep(500); + var connectParams = await GetDatabaseConnectionAsync(serverType); + bool connected = await Connect(ownerUri, connectParams); + Assert.True(connected, "Connection is successful"); + if (connected) + { + Console.WriteLine("Connection is successful"); + } + + return connected; + } + + private async Task CalculateRunTime(string testName, Func> testToRun) + { + TestTimer timer = new TestTimer(); + T result = await testToRun(); + timer.EndAndPrint(testName); + return result; + } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestBase.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestBase.cs index 97be72e7..bfff905c 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestBase.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Tests/TestBase.cs @@ -5,12 +5,15 @@ using System; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; +using Microsoft.SqlTools.ServiceLayer.Credentials.Contracts; using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts; using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts; using Microsoft.SqlTools.ServiceLayer.SqlContext; using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver; +using Microsoft.SqlTools.ServiceLayer.TestDriver.Utility; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests @@ -163,7 +166,31 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests var result = await Driver.SendRequest(CompletionResolveRequest.Type, item); return result; } - + + /// + /// Request a Read Credential for given credential id + /// + protected async Task ReadCredential(string credentialId) + { + var credentialParams = new Credential(); + credentialParams.CredentialId = credentialId; + + return await Driver.SendRequest(ReadCredentialRequest.Type, credentialParams); + } + + /// + /// Returns database connection parameters for given server type + /// + protected async Task GetDatabaseConnectionAsync(TestServerType serverType) + { + TestServerIdentity serverIdentiry = ConnectionTestUtils.TestServers.FirstOrDefault(x => x.ServerType == serverType); + var connectionProfile = ConnectionTestUtils.Setting.GetConnentProfile(serverIdentiry.ProfileName, serverIdentiry.ServerName); + Credential credential = await ReadCredential(connectionProfile.formatCredentialId()); + ConnectParams conenctParam = ConnectionTestUtils.CreateConnectParams(connectionProfile.ServerName, connectionProfile.Database, + connectionProfile.User, credential.Password); + return conenctParam; + } + /// /// Request a list of completion items for a position in a block of text /// diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/ConnectionTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/ConnectionTestUtils.cs index f48c86e8..0f0866fb 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/ConnectionTestUtils.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/ConnectionTestUtils.cs @@ -4,6 +4,7 @@ // using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -17,9 +18,41 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Utility /// public class ConnectionTestUtils { + public static IEnumerable TestServers = InitTestServerNames(); + public static Setting Setting = InitSetting(); + private static readonly Lazy azureTestServerConnection = new Lazy(() => GetConnectionFromVsCodeSettings("***REMOVED***")); + private static IEnumerable InitTestServerNames() + { + try + { + string testServerNamesFilePath = Environment.GetEnvironmentVariable("TestServerNamesFile"); + string jsonFileContent = File.ReadAllText(testServerNamesFilePath); + return Newtonsoft.Json.JsonConvert.DeserializeObject>(jsonFileContent); + } + catch (Exception ex) + { + return null; + } + } + + private static Setting InitSetting() + { + try + { + string settingsFileContents = GetSettingFileContent(); + Setting setting = Newtonsoft.Json.JsonConvert.DeserializeObject(settingsFileContents); + + return setting; + } + catch (Exception ex) + { + return null; + } + } + public static ConnectParams AzureTestServerConnection { get { return azureTestServerConnection.Value; } @@ -70,6 +103,26 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Utility private static readonly Lazy dataToolsTelemetryAzureConnection = new Lazy(() => GetConnectionFromVsCodeSettings("***REMOVED***")); + private static string GetSettingFileContent() + { + string settingsFilename; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + settingsFilename = Environment.GetEnvironmentVariable("APPDATA") + @"\Code\User\settings.json"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + settingsFilename = Environment.GetEnvironmentVariable("HOME") + @"/Library/Application Support/Code/User/settings.json"; + } + else + { + settingsFilename = Environment.GetEnvironmentVariable("HOME") + @"/.config/Code/User/settings.json"; + } + string settingsFileContents = File.ReadAllText(settingsFilename); + + return settingsFileContents; + } + public static ConnectParams DataToolsTelemetryAzureConnection { get { return dataToolsTelemetryAzureConnection.Value; } @@ -97,20 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Utility { try { - string settingsFilename; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - settingsFilename = Environment.GetEnvironmentVariable("APPDATA") + @"\Code\User\settings.json"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - settingsFilename = Environment.GetEnvironmentVariable("HOME") + @"/Library/Application Support/Code/User/settings.json"; - } - else - { - settingsFilename = Environment.GetEnvironmentVariable("HOME") + @"/.config/Code/User/settings.json"; - } - string settingsFileContents = File.ReadAllText(settingsFilename); + string settingsFileContents = GetSettingFileContent(); JObject root = JObject.Parse(settingsFileContents); JArray connections = (JArray)root["mssql.connections"]; diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/Setting.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/Setting.cs new file mode 100644 index 00000000..9349abd4 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/Setting.cs @@ -0,0 +1,80 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Linq; +using System.Globalization; +using Newtonsoft.Json; + +namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Utility +{ + /// + /// The model for deserializing settings.json + /// + public class Setting + { + [JsonProperty("mssql.connections")] + public List Connections { get; set; } + + public ConnectionProfile GetConnentProfile(string profilerName, string serverName) + { + if (!string.IsNullOrEmpty(profilerName)) + { + var byPrfileName = Connections.FirstOrDefault(x => x.ProfileName == profilerName); + if (byPrfileName != null) + { + return byPrfileName; + } + } + return Connections.FirstOrDefault(x => x.ServerName == serverName); + } + } + + /// + /// The model to deserializing the connections inside settings.json + /// + public class ConnectionProfile + { + public const string CRED_PREFIX = "Microsoft.SqlTools"; + public const string CRED_SEPARATOR = "|"; + public const string CRED_SERVER_PREFIX = "server:"; + public const string CRED_DB_PREFIX = "db:"; + public const string CRED_USER_PREFIX = "user:"; + public const string CRED_ITEMTYPE_PREFIX = "itemtype:"; + + [JsonProperty("server")] + public string ServerName { get; set; } + public string Database { get; set; } + + public string User { get; set; } + + public string Password { get; set; } + + public string ProfileName { get; set; } + + + public string formatCredentialId(string itemType = "Profile") + { + if (!string.IsNullOrEmpty(ServerName)) + { + List cred = new List(); + cred.Add(CRED_PREFIX); + AddToList(itemType, CRED_ITEMTYPE_PREFIX, cred); + AddToList(ServerName, CRED_SERVER_PREFIX, cred); + AddToList(Database, CRED_DB_PREFIX, cred); + AddToList(User, CRED_USER_PREFIX, cred); + return string.Join(CRED_SEPARATOR, cred.ToArray()); + } + return null; + } + private void AddToList(string item, string prefix, List list) + { + if (!string.IsNullOrEmpty(item)) + { + list.Add(string.Format(CultureInfo.InvariantCulture, "{0}{1}", prefix, item)); + } + } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/TestServerIdentity.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/TestServerIdentity.cs new file mode 100644 index 00000000..ce9b55fe --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/TestServerIdentity.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Utility +{ + /// + /// The model to deserialize the server names json + /// + public class TestServerIdentity + { + public string ServerName { get; set; } + public string ProfileName { get; set; } + + public TestServerType ServerType { get; set; } + } + + public enum TestServerType + { + None, + Azure, + OnPrem + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/TestTimer.cs b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/TestTimer.cs new file mode 100644 index 00000000..993ed0ee --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/Utility/TestTimer.cs @@ -0,0 +1,59 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Globalization; + +namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Utility +{ + /// + /// Timer to calculate the test run time + /// + public class TestTimer + { + public TestTimer() + { + Start(); + } + + public void Start() + { + StartDateTime = DateTime.UtcNow; + } + + public void End() + { + EndDateTime = DateTime.UtcNow; + } + + public void EndAndPrint(string testName) + { + End(); + var currentColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test Name: {0} Run time in milliSeconds: {1}", testName, TotalMilliSeconds)); + Console.ForegroundColor = currentColor; + } + + public double TotalMilliSeconds + { + get + { + return (EndDateTime - StartDateTime).TotalMilliseconds; + } + } + + public double TotalMilliSecondsUntilNow + { + get + { + return (DateTime.UtcNow - StartDateTime).TotalMilliseconds; + } + } + + public DateTime StartDateTime { get; private set; } + public DateTime EndDateTime { get; private set; } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/project.json b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/project.json index e7246f8d..c073d88c 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.TestDriver/project.json +++ b/test/Microsoft.SqlTools.ServiceLayer.TestDriver/project.json @@ -3,7 +3,10 @@ "version": "1.0.0-*", "buildOptions": { "debugType": "portable", - "emitEntryPoint": true + "emitEntryPoint": true, + "embed": [ + "Scripts/AdventureWorks.sql" + ] }, "dependencies": { "xunit": "2.1.0",