From c46032c71f97577b70617afa121264dc6ef94f69 Mon Sep 17 00:00:00 2001 From: Leila Lali Date: Fri, 28 Apr 2017 09:44:45 -0700 Subject: [PATCH] fixed db trigger, system types and oe tests (#331) * fixed db trigger, system types and oe tests --- .../ObjectExplorer/Nodes/NodeFilter.cs | 18 + .../ObjectExplorer/ObjectExplorerService.cs | 726 +++++++++--------- .../SmoModel/SmoChildFactoryBase.cs | 24 +- .../ObjectExplorer/SmoModel/SmoQueryModel.cs | 56 +- .../SmoModel/SmoQueryModelDefinition.xml | 9 +- .../SmoModel/TreeNodeDefinition.xml | 5 +- .../SmoModel/TreeNodeGenerator.cs | 4 +- .../ObjectExplorerServiceTests.cs | 200 ++--- .../Baselined/BaselinedTest.cs | 2 +- .../SqlTestDb.cs | 48 +- .../Baselines/AllSqlObjects.txt | 136 ++++ .../TestScripts/AllSqlObjects.sql | Bin 0 -> 35720 bytes .../TestServiceProvider.cs | 27 +- 13 files changed, 690 insertions(+), 565 deletions(-) create mode 100644 test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/Baselines/AllSqlObjects.txt create mode 100644 test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/TestScripts/AllSqlObjects.sql diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/NodeFilter.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/NodeFilter.cs index 0c101c04..72b0dc99 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/NodeFilter.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/NodeFilter.cs @@ -4,7 +4,9 @@ // using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes { @@ -76,6 +78,22 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes filter = $"{filter} {orPrefix} @{Property} = {proeprtyValue}"; } } + filter = $"({filter})"; + + return filter; + } + + public static string ConcatProperties(IEnumerable filters) + { + string filter = ""; + var list = filters.ToList(); + for (int i = 0; i < list.Count; i++) + { + var value = list[i]; + + string orPrefix = i == 0 ? "" : "and"; + filter = $"{filter} {orPrefix} {value.ToPropertyFilterString()}"; + } filter = $"[{filter}]"; return filter; diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs index 99b0b385..8587bed0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs @@ -1,353 +1,363 @@ -// -// 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.Collections.Generic; -using System.Composition; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.SqlTools.Extensibility; -using Microsoft.SqlTools.Hosting; -using Microsoft.SqlTools.Hosting.Protocol; -using Microsoft.SqlTools.ServiceLayer.Connection; -using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; -using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts; -using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; -using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel; -using Microsoft.SqlTools.Utility; - -namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer -{ - /// - /// A Service to support querying server and database information as an Object Explorer tree. - /// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API. - /// - [Export(typeof(IHostedService))] - public class ObjectExplorerService : HostedService, IComposableService - { - internal const string uriPrefix = "objectexplorer://"; - - // Instance of the connection service, used to get the connection info for a given owner URI - private ConnectionService connectionService; - private IProtocolEndpoint serviceHost; - private Dictionary sessionMap; - private readonly Lazy>> applicableNodeChildFactories; - private IMultiServiceProvider serviceProvider; - - /// - /// Singleton constructor - /// - public ObjectExplorerService() - { - sessionMap = new Dictionary(); - applicableNodeChildFactories = new Lazy>>(() => PopulateFactories()); - } - - /// - /// Internal for testing only - /// - internal ObjectExplorerService(ExtensionServiceProvider serviceProvider) - : this() - { - SetServiceProvider(serviceProvider); - } - - private Dictionary> ApplicableNodeChildFactories - { - get - { - return applicableNodeChildFactories.Value; - } - } - - /// - /// As an , this will be set whenever the service is initialized - /// via an - /// - /// - public override void SetServiceProvider(IMultiServiceProvider provider) - { - Validate.IsNotNull(nameof(provider), provider); - serviceProvider = provider; - connectionService = provider.GetService(); - } - - /// - /// Initializes the service with the service host and registers request handlers. - /// - /// The service host instance to register with - public override void InitializeService(IProtocolEndpoint serviceHost) - { - Logger.Write(LogLevel.Verbose, "ObjectExplorer service initialized"); - this.serviceHost = serviceHost; - // Register handlers for requests - serviceHost.SetRequestHandler(CreateSessionRequest.Type, HandleCreateSessionRequest); - serviceHost.SetRequestHandler(ExpandRequest.Type, HandleExpandRequest); - } - - internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext context) - { - Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest"); - Func> doCreateSession = async () => - { - Validate.IsNotNull(nameof(connectionDetails), connectionDetails); - Validate.IsNotNull(nameof(context), context); - - string uri = GenerateUri(connectionDetails); - - ObjectExplorerSession session; - if (!sessionMap.TryGetValue(uri, out session)) - { - // Establish a connection to the specified server/database - session = await DoCreateSession(connectionDetails, uri); - } - - CreateSessionResponse response; - if (session == null) - { - response = new CreateSessionResponse() { Success = false }; - } - else - { - // Else we have a session available, response with existing session information - response = new CreateSessionResponse() - { - Success = true, - RootNode = session.Root.ToNodeInfo(), - SessionId = session.Uri - }; - } - return response; - }; - - await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest"); - } - - internal async Task ExpandNode(ObjectExplorerSession session, string nodePath) - { - return await Task.Factory.StartNew(() => - { - NodeInfo[] nodes = null; - TreeNode node = session.Root.FindNodeByPath(nodePath); - if(node != null) - { - nodes = node.Expand().Select(x => x.ToNodeInfo()).ToArray(); - } - return nodes; - }); - } - - /// - /// Establishes a new session and stores its information - /// - /// object if successful, null if unsuccessful - internal async Task DoCreateSession(ConnectionDetails connectionDetails, string uri) - { - ObjectExplorerSession session; - - ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails }; - - ConnectionCompleteParams connectionResult = await Connect(connectParams); - if (connectionResult == null) - { - return null; - } - - session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider); - sessionMap[uri] = session; - return session; - } - - - private async Task Connect(ConnectParams connectParams) - { - try - { - // open connection based on request details - ConnectionCompleteParams result = await connectionService.Connect(connectParams); - if(result != null && !string.IsNullOrEmpty(result.ConnectionId)) - { - return result; - } - else - { - await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result); - return null; - } - - } - catch (Exception ex) - { - // Send a connection failed error message in this case. - ConnectionCompleteParams result = new ConnectionCompleteParams() - { - Messages = ex.ToString() - }; - await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result); - return null; - } - } - - internal async Task HandleExpandRequest(ExpandParams expandParams, RequestContext context) - { - Logger.Write(LogLevel.Verbose, "HandleExpandRequest"); - Func> expandNode = async () => - { - Validate.IsNotNull(nameof(expandParams), expandParams); - Validate.IsNotNull(nameof(context), context); - - string uri = expandParams.SessionId; - ObjectExplorerSession session = null; - NodeInfo[] nodes = null; - if (sessionMap.ContainsKey(uri)) - { - session = sessionMap[uri]; - } - else - { - //TODO: error - } - - - if (session != null) - { - // Establish a connection to the specified server/database - nodes = await ExpandNode(session, expandParams.NodePath); - } - - ExpandResponse response; - response = new ExpandResponse() { Nodes = nodes, SessionId = uri }; - return response; - }; - - await HandleRequestAsync(expandNode, context, "HandleExpandRequest"); - } - - private async Task HandleRequestAsync(Func> handler, RequestContext requestContext, string requestType) - { - Logger.Write(LogLevel.Verbose, requestType); - - try - { - T result = await handler(); - await requestContext.SendResult(result); - } - catch (Exception ex) - { - await requestContext.SendError(ex.ToString()); - } - } - - /// - /// Generates a URI for object explorer using a similar pattern to Mongo DB (which has URI-based database definition) - /// as this should ensure uniqueness - /// - /// - /// string representing a URI - /// Internal for testing purposes only - internal static string GenerateUri(ConnectionDetails details) - { - Validate.IsNotNull("details", details); - string uri = string.Format(CultureInfo.InvariantCulture, "{0}{1}", uriPrefix, Uri.EscapeUriString(details.ServerName)); - uri = AppendIfExists(uri, "databaseName", details.DatabaseName); - uri = AppendIfExists(uri, "user", details.UserName); - return uri; - } - - private static string AppendIfExists(string uri, string propertyName, string propertyValue) - { - if (!string.IsNullOrEmpty(propertyValue)) - { - uri += string.Format(CultureInfo.InvariantCulture, ";{0}={1}", propertyName, Uri.EscapeUriString(propertyValue)); - } - return uri; - } - - public IEnumerable GetApplicableChildFactories(TreeNode item) - { - if (ApplicableNodeChildFactories != null) - { - HashSet applicableFactories; - if (ApplicableNodeChildFactories.TryGetValue(item.NodeTypeId.ToString(), out applicableFactories)) - { - return applicableFactories; - } - } - return null; - } - - internal Dictionary> PopulateFactories() - { - VerifyServicesInitialized(); - - var childFactories = new Dictionary>(); - // Create our list of all NodeType to ChildFactory objects so we can expand appropriately - foreach (var factory in serviceProvider.GetServices()) - { - var parents = factory.ApplicableParents(); - if (parents != null) - { - foreach (var parent in parents) - { - AddToApplicableChildFactories(childFactories, factory, parent); - } - } - } - return childFactories; - } - - private void VerifyServicesInitialized() - { - if (serviceProvider == null) - { - throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet); - } - if (connectionService == null) - { - throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet); - } - } - - private static void AddToApplicableChildFactories(Dictionary> childFactories, ChildFactory factory, string parent) - { - HashSet applicableFactories; - if (!childFactories.TryGetValue(parent, out applicableFactories)) - { - applicableFactories = new HashSet(); - childFactories[parent] = applicableFactories; - } - applicableFactories.Add(factory); - } - - internal class ObjectExplorerSession - { - private ConnectionService connectionService; - private IMultiServiceProvider serviceProvider; - - // TODO decide whether a cache is needed to handle lookups in elements with a large # children - //private const int Cachesize = 10000; - //private Cache cache; - - public ObjectExplorerSession(string uri, TreeNode root, IMultiServiceProvider serviceProvider, ConnectionService connectionService) - { - Validate.IsNotNullOrEmptyString("uri", uri); - Validate.IsNotNull("root", root); - Uri = uri; - Root = root; - this.serviceProvider = serviceProvider; - this.connectionService = connectionService; - } - - public string Uri { get; private set; } - public TreeNode Root { get; private set; } - - public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider) - { - TreeNode rootNode = new ServerNode(response, serviceProvider); - var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService()); +// +// 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.Collections.Generic; +using System.Composition; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.SqlTools.Extensibility; +using Microsoft.SqlTools.Hosting; +using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; +using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts; +using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; +using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel; +using Microsoft.SqlTools.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer +{ + /// + /// A Service to support querying server and database information as an Object Explorer tree. + /// The APIs used for this are modeled closely on the VSCode TreeExplorerNodeProvider API. + /// + [Export(typeof(IHostedService))] + public class ObjectExplorerService : HostedService, IComposableService + { + internal const string uriPrefix = "objectexplorer://"; + + // Instance of the connection service, used to get the connection info for a given owner URI + private ConnectionService connectionService; + private IProtocolEndpoint serviceHost; + private Dictionary sessionMap; + private readonly Lazy>> applicableNodeChildFactories; + private IMultiServiceProvider serviceProvider; + + /// + /// Singleton constructor + /// + public ObjectExplorerService() + { + sessionMap = new Dictionary(); + applicableNodeChildFactories = new Lazy>>(() => PopulateFactories()); + } + + /// + /// Internal for testing only + /// + internal ObjectExplorerService(ExtensionServiceProvider serviceProvider) + : this() + { + SetServiceProvider(serviceProvider); + } + + private Dictionary> ApplicableNodeChildFactories + { + get + { + return applicableNodeChildFactories.Value; + } + } + + /// + /// As an , this will be set whenever the service is initialized + /// via an + /// + /// + public override void SetServiceProvider(IMultiServiceProvider provider) + { + Validate.IsNotNull(nameof(provider), provider); + serviceProvider = provider; + connectionService = provider.GetService(); + } + + /// + /// Initializes the service with the service host and registers request handlers. + /// + /// The service host instance to register with + public override void InitializeService(IProtocolEndpoint serviceHost) + { + Logger.Write(LogLevel.Verbose, "ObjectExplorer service initialized"); + this.serviceHost = serviceHost; + // Register handlers for requests + serviceHost.SetRequestHandler(CreateSessionRequest.Type, HandleCreateSessionRequest); + serviceHost.SetRequestHandler(ExpandRequest.Type, HandleExpandRequest); + } + + public void CloseSession(string uri) + { + ObjectExplorerSession session; + if (sessionMap.TryGetValue(uri, out session)) + { + // Establish a connection to the specified server/database + sessionMap.Remove(session.Uri); + } + } + + internal async Task HandleCreateSessionRequest(ConnectionDetails connectionDetails, RequestContext context) + { + Logger.Write(LogLevel.Verbose, "HandleCreateSessionRequest"); + Func> doCreateSession = async () => + { + Validate.IsNotNull(nameof(connectionDetails), connectionDetails); + Validate.IsNotNull(nameof(context), context); + + string uri = GenerateUri(connectionDetails); + + ObjectExplorerSession session; + if (!sessionMap.TryGetValue(uri, out session)) + { + // Establish a connection to the specified server/database + session = await DoCreateSession(connectionDetails, uri); + } + + CreateSessionResponse response; + if (session == null) + { + response = new CreateSessionResponse() { Success = false }; + } + else + { + // Else we have a session available, response with existing session information + response = new CreateSessionResponse() + { + Success = true, + RootNode = session.Root.ToNodeInfo(), + SessionId = session.Uri + }; + } + return response; + }; + + await HandleRequestAsync(doCreateSession, context, "HandleCreateSessionRequest"); + } + + internal async Task ExpandNode(ObjectExplorerSession session, string nodePath) + { + return await Task.Factory.StartNew(() => + { + NodeInfo[] nodes = null; + TreeNode node = session.Root.FindNodeByPath(nodePath); + if(node != null) + { + nodes = node.Expand().Select(x => x.ToNodeInfo()).ToArray(); + } + return nodes; + }); + } + + /// + /// Establishes a new session and stores its information + /// + /// object if successful, null if unsuccessful + internal async Task DoCreateSession(ConnectionDetails connectionDetails, string uri) + { + ObjectExplorerSession session; + + ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails }; + + ConnectionCompleteParams connectionResult = await Connect(connectParams); + if (connectionResult == null) + { + return null; + } + + session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider); + sessionMap[uri] = session; + return session; + } + + + private async Task Connect(ConnectParams connectParams) + { + try + { + // open connection based on request details + ConnectionCompleteParams result = await connectionService.Connect(connectParams); + if(result != null && !string.IsNullOrEmpty(result.ConnectionId)) + { + return result; + } + else + { + await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result); + return null; + } + + } + catch (Exception ex) + { + // Send a connection failed error message in this case. + ConnectionCompleteParams result = new ConnectionCompleteParams() + { + Messages = ex.ToString() + }; + await serviceHost.SendEvent(ConnectionCompleteNotification.Type, result); + return null; + } + } + + internal async Task HandleExpandRequest(ExpandParams expandParams, RequestContext context) + { + Logger.Write(LogLevel.Verbose, "HandleExpandRequest"); + Func> expandNode = async () => + { + Validate.IsNotNull(nameof(expandParams), expandParams); + Validate.IsNotNull(nameof(context), context); + + string uri = expandParams.SessionId; + ObjectExplorerSession session = null; + NodeInfo[] nodes = null; + if (sessionMap.ContainsKey(uri)) + { + session = sessionMap[uri]; + } + else + { + //TODO: error + } + + + if (session != null) + { + // Establish a connection to the specified server/database + nodes = await ExpandNode(session, expandParams.NodePath); + } + + ExpandResponse response; + response = new ExpandResponse() { Nodes = nodes, SessionId = uri }; + return response; + }; + + await HandleRequestAsync(expandNode, context, "HandleExpandRequest"); + } + + private async Task HandleRequestAsync(Func> handler, RequestContext requestContext, string requestType) + { + Logger.Write(LogLevel.Verbose, requestType); + + try + { + T result = await handler(); + await requestContext.SendResult(result); + } + catch (Exception ex) + { + await requestContext.SendError(ex.ToString()); + } + } + + /// + /// Generates a URI for object explorer using a similar pattern to Mongo DB (which has URI-based database definition) + /// as this should ensure uniqueness + /// + /// + /// string representing a URI + /// Internal for testing purposes only + internal static string GenerateUri(ConnectionDetails details) + { + Validate.IsNotNull("details", details); + string uri = string.Format(CultureInfo.InvariantCulture, "{0}{1}", uriPrefix, Uri.EscapeUriString(details.ServerName)); + uri = AppendIfExists(uri, "databaseName", details.DatabaseName); + uri = AppendIfExists(uri, "user", details.UserName); + return uri; + } + + private static string AppendIfExists(string uri, string propertyName, string propertyValue) + { + if (!string.IsNullOrEmpty(propertyValue)) + { + uri += string.Format(CultureInfo.InvariantCulture, ";{0}={1}", propertyName, Uri.EscapeUriString(propertyValue)); + } + return uri; + } + + public IEnumerable GetApplicableChildFactories(TreeNode item) + { + if (ApplicableNodeChildFactories != null) + { + HashSet applicableFactories; + if (ApplicableNodeChildFactories.TryGetValue(item.NodeTypeId.ToString(), out applicableFactories)) + { + return applicableFactories; + } + } + return null; + } + + internal Dictionary> PopulateFactories() + { + VerifyServicesInitialized(); + + var childFactories = new Dictionary>(); + // Create our list of all NodeType to ChildFactory objects so we can expand appropriately + foreach (var factory in serviceProvider.GetServices()) + { + var parents = factory.ApplicableParents(); + if (parents != null) + { + foreach (var parent in parents) + { + AddToApplicableChildFactories(childFactories, factory, parent); + } + } + } + return childFactories; + } + + private void VerifyServicesInitialized() + { + if (serviceProvider == null) + { + throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet); + } + if (connectionService == null) + { + throw new InvalidOperationException(SqlTools.Hosting.SR.ServiceProviderNotSet); + } + } + + private static void AddToApplicableChildFactories(Dictionary> childFactories, ChildFactory factory, string parent) + { + HashSet applicableFactories; + if (!childFactories.TryGetValue(parent, out applicableFactories)) + { + applicableFactories = new HashSet(); + childFactories[parent] = applicableFactories; + } + applicableFactories.Add(factory); + } + + internal class ObjectExplorerSession + { + private ConnectionService connectionService; + private IMultiServiceProvider serviceProvider; + + // TODO decide whether a cache is needed to handle lookups in elements with a large # children + //private const int Cachesize = 10000; + //private Cache cache; + + public ObjectExplorerSession(string uri, TreeNode root, IMultiServiceProvider serviceProvider, ConnectionService connectionService) + { + Validate.IsNotNullOrEmptyString("uri", uri); + Validate.IsNotNull("root", root); + Uri = uri; + Root = root; + this.serviceProvider = serviceProvider; + this.connectionService = connectionService; + } + + public string Uri { get; private set; } + public TreeNode Root { get; private set; } + + public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider) + { + TreeNode rootNode = new ServerNode(response, serviceProvider); + var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService()); if (!ObjectExplorerUtils.IsSystemDatabaseConnection(response.ConnectionSummary.DatabaseName)) { // Assuming the databases are in a folder under server node @@ -358,11 +368,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer var databaseNode = databases.FirstOrDefault(d => d.Label == response.ConnectionSummary.DatabaseName); databaseNode.Label = rootNode.Label; session.Root = databaseNode; - } - return session; - } - - } - } - -} + } + return session; + } + + } + } + +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs index 89b9641d..a5d91873 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoChildFactoryBase.cs @@ -23,7 +23,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel public override IEnumerable Expand(TreeNode parent) { - //parent.BeginChildrenInit(); try { List allChildren = new List(); @@ -35,7 +34,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel } finally { - //parent.EndChildrenInit(); } } @@ -62,10 +60,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel } SmoQueryContext context = parent.GetContextAs(); Validate.IsNotNull(nameof(context), context); + + var validForFlag = ServerVersionHelper.GetValidForFlag(context.SqlServerType); + if (ShouldFilterNode(parent, validForFlag)) + { + return; + } + IEnumerable queriers = context.ServiceProvider.GetServices(q => IsCompatibleQuerier(q)); var filters = this.Filters; - var validForFlag = ServerVersionHelper.GetValidForFlag(context.SqlServerType); - foreach (var querier in queriers) { string propertyFilter = GetProperyFilter(filters, querier.GetType(), validForFlag); @@ -77,7 +80,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel Console.WriteLine("smoObject should not be null"); } TreeNode childNode = CreateChild(parent, smoObject); - if (childNode != null && !ShouldFilterNode(childNode, validForFlag)) + if (childNode != null && PassesFinalFilters(childNode, smoObject) && !ShouldFilterNode(childNode, validForFlag)) { allChildren.Add(childNode); } @@ -102,15 +105,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel private string GetProperyFilter(IEnumerable filters, Type querierType, ValidForFlag validForFlag) { - string filter = ""; + string filter = string.Empty; if (filters != null) { - var filterToApply = filters.FirstOrDefault(f => f.CanApplyFilter(querierType, validForFlag)); - filter = ""; - - if (filterToApply != null) + var filtersToApply = filters.Where(f => f.CanApplyFilter(querierType, validForFlag)).ToList(); + filter = string.Empty; + if (filtersToApply.Any()) { - filter = filterToApply.ToPropertyFilterString(); + filter = NodeFilter.ConcatProperties(filtersToApply); } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModel.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModel.cs index f8a05fc3..0f744a90 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModel.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModel.cs @@ -923,7 +923,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel [Export(typeof(SmoQuerier))] internal partial class SqlDatabaseDdlTriggerQuerier: SmoQuerier { - Type[] supportedTypes = new Type[] { typeof(Trigger) }; + Type[] supportedTypes = new Type[] { typeof(DatabaseDdlTrigger) }; public override Type[] SupportedObjectTypes { get { return supportedTypes; } } @@ -937,7 +937,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel HashSet urns = null; if (hasFilter) { - string urn = $"{parentDatabase.Urn.ToString()}/Trigger" + filter; + string urn = $"{parentDatabase.Urn.ToString()}/DatabaseDdlTrigger" + filter; Enumerator en = new Enumerator(); Request request = new Request(new Urn(urn)); ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject); @@ -948,11 +948,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { if (hasFilter && urns != null) { - return new SmoCollectionWrapper(retValue).Where(c => urns.Contains(c.Urn)); + return new SmoCollectionWrapper(retValue).Where(c => urns.Contains(c.Urn)); } else { - return new SmoCollectionWrapper(retValue); + return new SmoCollectionWrapper(retValue); } } } @@ -1120,46 +1120,6 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel } } - [Export(typeof(SmoQuerier))] - internal partial class SqlSystemDataTypeQuerier: SmoQuerier - { - Type[] supportedTypes = new Type[] { typeof(SystemDataType) }; - - public override Type[] SupportedObjectTypes { get { return supportedTypes; } } - - public override IEnumerable Query(SmoQueryContext context, string filter) - { - Server parentServer = context.Parent as Server; - if (parentServer != null) - { - var retValue = parentServer.SystemDataTypes; - bool hasFilter = !string.IsNullOrEmpty(filter); - HashSet urns = null; - if (hasFilter) - { - string urn = $"{parentServer.Urn.ToString()}/SystemDataType" + filter; - Enumerator en = new Enumerator(); - Request request = new Request(new Urn(urn)); - ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject); - EnumResult result = en.Process(serverConnection, request); - urns = GetUrns(result); - } - if (retValue != null) - { - if (hasFilter && urns != null) - { - return new SmoCollectionWrapper(retValue).Where(c => urns.Contains(c.Urn)); - } - else - { - return new SmoCollectionWrapper(retValue); - } - } - } - return Enumerable.Empty(); - } - } - [Export(typeof(SmoQuerier))] internal partial class SqlUserDefinedDataTypeQuerier: SmoQuerier { @@ -2802,15 +2762,15 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel public override IEnumerable Query(SmoQueryContext context, string filter) { - Server parentServer = context.Parent as Server; - if (parentServer != null) + Database parentDatabase = context.Parent as Database; + if (parentDatabase != null) { - var retValue = parentServer.SystemDataTypes; + var retValue = parentDatabase.Parent.SystemDataTypes; bool hasFilter = !string.IsNullOrEmpty(filter); HashSet urns = null; if (hasFilter) { - string urn = $"{parentServer.Urn.ToString()}/SystemDataType" + filter; + string urn = $"{parentDatabase.Urn.ToString()}/SystemDataType" + filter; Enumerator en = new Enumerator(); Request request = new Request(new Urn(urn)); ServerConnection serverConnection = new ServerConnection(context.Server.ConnectionContext.SqlConnectionObject); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModelDefinition.xml b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModelDefinition.xml index fd1a5960..f8d14777 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModelDefinition.xml +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryModelDefinition.xml @@ -52,7 +52,9 @@ - + + + @@ -61,7 +63,6 @@ - @@ -129,7 +130,9 @@ - + + + diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeDefinition.xml b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeDefinition.xml index b8e8b868..2a4297cb 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeDefinition.xml +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeDefinition.xml @@ -216,6 +216,7 @@ + @@ -268,8 +269,8 @@ - - + + diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs index 984dd37d..e74c1407 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/TreeNodeGenerator.cs @@ -2393,7 +2393,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { get { - return new [] { typeof(SqlUserDefinedTypeQuerier), }; + return new [] { typeof(SqlBuiltInTypeQuerier), }; } } @@ -2417,7 +2417,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { get { - return new [] { typeof(SqlUserDefinedTypeQuerier), }; + return new [] { typeof(SqlBuiltInTypeQuerier), }; } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectExplorer/ObjectExplorerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectExplorer/ObjectExplorerServiceTests.cs index 970dad7c..50d76a5c 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectExplorer/ObjectExplorerServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/ObjectExplorer/ObjectExplorerServiceTests.cs @@ -4,9 +4,12 @@ // using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; @@ -14,6 +17,7 @@ using Microsoft.SqlTools.ServiceLayer.ObjectExplorer; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts; using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes; using Microsoft.SqlTools.ServiceLayer.Test.Common; +using Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined; using Xunit; using static Microsoft.SqlTools.ServiceLayer.ObjectExplorer.ObjectExplorerService; @@ -33,8 +37,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer { var session = await CreateSession(null, uri); await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); + DisconnectConnection(uri); } - CancelConnection(uri); } [Fact] @@ -47,9 +51,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer { var session = await CreateSession("tempdb", uri); await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); + DisconnectConnection(uri); } - CancelConnection(uri); - } [Fact] @@ -62,15 +65,35 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer { var session = await CreateSession(testDb.DatabaseName, uri); ExpandAndVerifyDatabaseNode(testDb.DatabaseName, session); + DisconnectConnection(uri); } - CancelConnection(uri); + } + [Fact] + public async void VerifyAllSqlObjects() + { + var queryFileName = "AllSqlObjects.sql"; + string uri = "AllSqlObjects"; + string baselineFileName = "AllSqlObjects.txt"; + string databaseName = null; + await TestServiceProvider.CalculateRunTime(() => VerifyObjectExplorerTest(databaseName, queryFileName, uri, baselineFileName), true); + } + + //[Fact] + //This takes take long to run so not a good test for CI builds + public async void VerifySystemObjects() + { + string queryFileName = null; + string uri = "SystemObjects"; + string baselineFileName = null; + string databaseName = null; + await TestServiceProvider.CalculateRunTime(() => VerifyObjectExplorerTest(databaseName, queryFileName, uri, baselineFileName, true), true); } private async Task CreateSession(string databaseName, string uri) { ConnectParams connectParams = TestServiceProvider.Instance.ConnectionProfileService.GetConnectionParameters(TestServerType.OnPrem, databaseName); - connectParams.Connection.Pooling = false; + //connectParams.Connection.Pooling = false; ConnectionDetails details = connectParams.Connection; var session = await _service.DoCreateSession(details, uri); @@ -136,23 +159,37 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer Assert.NotNull(tablesRoot); } - private void CancelConnection(string uri) + private void DisconnectConnection(string uri) { - ConnectionService.Instance.CancelConnect(new CancelConnectParams + ConnectionService.Instance.Disconnect(new DisconnectParams { OwnerUri = uri, Type = ConnectionType.Default }); } - private async Task ExpandTree(NodeInfo node, ObjectExplorerSession session) + private async Task ExpandTree(NodeInfo node, ObjectExplorerSession session, StringBuilder stringBuilder = null, bool verifySystemObjects = false) { if (node != null && !node.IsLeaf) { var children = await _service.ExpandNode(session, node.NodePath); foreach (var child in children) { - await _service.ExpandNode(session, child.NodePath); + if (stringBuilder != null && child.NodeType != "Folder" && child.NodeType != "FileGroupFile") + { + stringBuilder.AppendLine($"NodeType: {child.NodeType} Label: {child.Label}"); + } + if (!verifySystemObjects && (child.Label == SR.SchemaHierarchy_SystemStoredProcedures || + child.Label == SR.SchemaHierarchy_SystemViews || + child.Label == SR.SchemaHierarchy_SystemFunctions || + child.Label == SR.SchemaHierarchy_SystemDataTypes)) + { + // don't expand the system folders because then the test will take for ever + } + else + { + await ExpandTree(child, session, stringBuilder, verifySystemObjects); + } } } } @@ -160,7 +197,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer /// /// Returns the children of a node with the given label /// - private async Task FindNodeByLabel(NodeInfo node, ObjectExplorerSession session, string nodeType, bool nodeFound = false) + private async Task> FindNodeByLabel(NodeInfo node, ObjectExplorerSession session, string nodeType, bool nodeFound = false) { if (node != null && !node.IsLeaf) { @@ -211,117 +248,25 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer } } - - [Fact] - public async void VerifyAdventureWorksDatabaseObjects() + private async Task VerifyObjectExplorerTest(string databaseName, string queryFileName, string uri, string baselineFileName, bool verifySystemObjects = false) { - var query = Scripts.AdventureWorksScript; - string uri = "VerifyAdventureWorksDatabaseObjects"; - string databaseName = null; + var query = string.IsNullOrEmpty(queryFileName) ? string.Empty : LoadScript(queryFileName); + StringBuilder stringBuilder = new StringBuilder(); + SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri); + var session = await CreateSession(testDb.DatabaseName, uri); + await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session, false); + await ExpandTree(session.Root.ToNodeInfo(), session, stringBuilder, verifySystemObjects); - using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) - using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, true, databaseName, query, uri)) + string baseline = string.IsNullOrEmpty(baselineFileName) ? string.Empty : LoadBaseLine(baselineFileName); + if (!string.IsNullOrEmpty(baseline)) { - var session = await CreateSession(testDb.DatabaseName, queryTempFile.FilePath); - var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session, false); - await ExpandTree(session.Root.ToNodeInfo(), session); - var tablesChildren = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables); - - var systemTables = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_SystemTables); - Assert.True(!systemTables.Any()); - - var externalTables = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_ExternalTables); - Assert.True(!externalTables.Any()); - - var fileTables = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_FileTables); - Assert.True(!fileTables.Any()); - - var allTables = tablesChildren.Where(x => x.NodeType != NodeTypes.Folder.ToString()); - Assert.True(allTables.Any()); - - var storedProcedures = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_StoredProcedures); - Assert.True(storedProcedures.Any()); - - var views = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Views); - Assert.True(views.Any()); - - var userDefinedDataTypes = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_UserDefinedDataTypes); - Assert.True(userDefinedDataTypes.Any()); - - var scalarValuedFunctions = await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_ScalarValuedFunctions); - Assert.True(scalarValuedFunctions.Any()); - + string actual = stringBuilder.ToString(); + BaselinedTest.CompareActualWithBaseline(actual, baseline); } - CancelConnection(uri); - - } - - // [Fact] - public async void VerifySql2016Objects() - { - var query = LoadScript("Sql_2016_Additions.sql"); - string uri = "VerifySql2016Objects"; - string databaseName = null; - - using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri)) - { - var session = await CreateSession(testDb.DatabaseName, uri); - var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); - await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables); - } - CancelConnection(uri); - - } - - // [Fact] - public async void VerifySqlObjects() - { - var query = LoadScript("Sql_Additions.sql"); - string uri = "VerifySqlObjects"; - string databaseName = null; - - using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri)) - { - var session = await CreateSession(testDb.DatabaseName, uri); - var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); - await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables); - } - CancelConnection(uri); - - } - - // [Fact] - public async void VerifyFileTableTest() - { - var query = LoadScript("FileTableTest.sql"); - string uri = "VerifyFileTableTest"; - string databaseName = null; - - using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri)) - { - var session = await CreateSession(testDb.DatabaseName, uri); - var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); - await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables); - } - CancelConnection(uri); - - } - - //[Fact] - public async void VerifyColumnstoreindexSql16() - { - var query = LoadScript("ColumnstoreindexSql16.sql"); - string uri = "VerifyColumnstoreindexSql16"; - string databaseName = null; - - using (SqlTestDb testDb = SqlTestDb.CreateNew(TestServerType.OnPrem, false, databaseName, query, uri)) - { - var session = await CreateSession(testDb.DatabaseName, uri); - var databaseNodeInfo = await ExpandServerNodeAndVerifyDatabaseHierachy(testDb.DatabaseName, session); - await FindNodeByLabel(databaseNodeInfo, session, SR.SchemaHierarchy_Tables); - } - CancelConnection(uri); - + _service.CloseSession(session.Uri); + Thread.Sleep(3000); + testDb.Cleanup(); + return true; } private static string TestLocationDirectory @@ -341,15 +286,36 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectExplorer } } + public DirectoryInfo BaselineFileDirectory + { + get + { + string d = Path.Combine(TestLocationDirectory, "Baselines"); + return new DirectoryInfo(d); + } + } + public FileInfo GetInputFile(string fileName) { return new FileInfo(Path.Combine(InputFileDirectory.FullName, fileName)); } + public FileInfo GetBaseLineFile(string fileName) + { + return new FileInfo(Path.Combine(BaselineFileDirectory.FullName, fileName)); + } + private string LoadScript(string fileName) { FileInfo inputFile = GetInputFile(fileName); return TestUtilities.ReadTextAndNormalizeLineEndings(inputFile.FullName); } + + private string LoadBaseLine(string fileName) + { + FileInfo inputFile = GetBaseLineFile(fileName); + return TestUtilities.ReadTextAndNormalizeLineEndings(inputFile.FullName); + } + } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Baselined/BaselinedTest.cs b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Baselined/BaselinedTest.cs index 0afa62c8..60ead92e 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Baselined/BaselinedTest.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Baselined/BaselinedTest.cs @@ -272,7 +272,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common.Baselined /// Actual string /// Expected string /// Fails test if strings do not match; comparison is done using an InvariantCulture StringComparer - public void CompareActualWithBaseline(string actualContent, string baselineContent) + public static void CompareActualWithBaseline(string actualContent, string baselineContent) { int _compareResult = string.Compare(actualContent, baselineContent, StringComparison.OrdinalIgnoreCase); diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/SqlTestDb.cs b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/SqlTestDb.cs index 86a4c48f..f57fdd76 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/SqlTestDb.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/SqlTestDb.cs @@ -54,29 +54,26 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common /// Create the test db if not already exists /// public static SqlTestDb CreateNew( - TestServerType serverType, - bool doNotCleanupDb = false, - string databaseName = null, - string query = null, + TestServerType serverType, + bool doNotCleanupDb = false, + string databaseName = null, + string query = null, string dbNamePrefix = null) { SqlTestDb testDb = new SqlTestDb(); - using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) + databaseName = databaseName ?? GetUniqueDBName(dbNamePrefix); + string createDatabaseQuery = Scripts.CreateDatabaseQuery.Replace("#DatabaseName#", databaseName); + TestServiceProvider.Instance.RunQuery(serverType, MasterDatabaseName, createDatabaseQuery); + Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' is created", databaseName)); + if (!string.IsNullOrEmpty(query)) { - databaseName = databaseName ?? GetUniqueDBName(dbNamePrefix); - string createDatabaseQuery = Scripts.CreateDatabaseQuery.Replace("#DatabaseName#", databaseName); - TestServiceProvider.Instance.RunQuery(serverType, MasterDatabaseName, createDatabaseQuery); - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' is created", databaseName)); - if (!string.IsNullOrEmpty(query)) - { - TestServiceProvider.Instance.RunQuery(serverType, databaseName, query); - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' SQL types are created", databaseName)); - } - testDb.DatabaseName = databaseName; - testDb.ServerType = serverType; - testDb.DoNotCleanupDb = doNotCleanupDb; + TestServiceProvider.Instance.RunQuery(serverType, databaseName, query); + Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Test database '{0}' SQL types are created", databaseName)); } + testDb.DatabaseName = databaseName; + testDb.ServerType = serverType; + testDb.DoNotCleanupDb = doNotCleanupDb; return testDb; } @@ -111,13 +108,20 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common public void Cleanup() { - if (!DoNotCleanupDb) + try { - string dropDatabaseQuery = string.Format(CultureInfo.InvariantCulture, - (ServerType == TestServerType.Azure ? Scripts.DropDatabaseIfExistAzure : Scripts.DropDatabaseIfExist), DatabaseName); + if (!DoNotCleanupDb) + { + string dropDatabaseQuery = string.Format(CultureInfo.InvariantCulture, + (ServerType == TestServerType.Azure ? Scripts.DropDatabaseIfExistAzure : Scripts.DropDatabaseIfExist), DatabaseName); - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Cleaning up database {0}", DatabaseName)); - TestServiceProvider.Instance.RunQuery(ServerType, MasterDatabaseName, dropDatabaseQuery); + Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Cleaning up database {0}", DatabaseName)); + TestServiceProvider.Instance.RunQuery(ServerType, MasterDatabaseName, dropDatabaseQuery); + } + } + catch (Exception ex) + { + Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Failed to cleanup database: {0} error:{1}", DatabaseName, ex.Message)); } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/Baselines/AllSqlObjects.txt b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/Baselines/AllSqlObjects.txt new file mode 100644 index 00000000..7c1a8e58 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/Baselines/AllSqlObjects.txt @@ -0,0 +1,136 @@ +NodeType: Table Label: HumanResources.Employee +NodeType: Column Label: BusinessEntityID +NodeType: Column Label: NationalIDNumber +NodeType: Column Label: LoginID +NodeType: Column Label: OrganizationNode +NodeType: Column Label: OrganizationLevel +NodeType: Column Label: JobTitle +NodeType: Column Label: BirthDate +NodeType: Column Label: MaritalStatus +NodeType: Column Label: Gender +NodeType: Column Label: HireDate +NodeType: Column Label: SalariedFlag +NodeType: Column Label: VacationHours +NodeType: Column Label: SickLeaveHours +NodeType: Column Label: CurrentFlag +NodeType: Column Label: rowguid +NodeType: Column Label: ModifiedDate +NodeType: Key Label: FK_Employee_Person_BusinessEntityID +NodeType: Key Label: PK_Employee_BusinessEntityID +NodeType: Constraint Label: CK_Employee_BirthDate +NodeType: Constraint Label: CK_Employee_Gender +NodeType: Constraint Label: CK_Employee_HireDate +NodeType: Constraint Label: CK_Employee_MaritalStatus +NodeType: Constraint Label: CK_Employee_SickLeaveHours +NodeType: Constraint Label: CK_Employee_VacationHours +NodeType: Index Label: NonClusteredIndex-Login +NodeType: Statistic Label: NonClusteredIndex-Login +NodeType: Statistic Label: PK_Employee_BusinessEntityID +NodeType: Table Label: HumanResources.Employee_Temporal +NodeType: Column Label: BusinessEntityID +NodeType: Column Label: NationalIDNumber +NodeType: Column Label: LoginID +NodeType: Column Label: OrganizationNode +NodeType: Column Label: OrganizationLevel +NodeType: Column Label: JobTitle +NodeType: Column Label: BirthDate +NodeType: Column Label: MaritalStatus +NodeType: Column Label: Gender +NodeType: Column Label: HireDate +NodeType: Column Label: VacationHours +NodeType: Column Label: SickLeaveHours +NodeType: Column Label: ValidFrom +NodeType: Column Label: ValidTo +NodeType: Key Label: PK_Employee_History_BusinessEntityID +NodeType: Statistic Label: PK_Employee_History_BusinessEntityID +NodeType: Table Label: Person.Person +NodeType: Column Label: BusinessEntityID +NodeType: Column Label: PersonType +NodeType: Column Label: NameStyle +NodeType: Column Label: Title +NodeType: Column Label: FirstName +NodeType: Column Label: MiddleName +NodeType: Column Label: LastName +NodeType: Column Label: Suffix +NodeType: Column Label: EmailPromotion +NodeType: Column Label: AdditionalContactInfo +NodeType: Column Label: rowguid +NodeType: Column Label: ModifiedDate +NodeType: Key Label: PK_Person_BusinessEntityID +NodeType: Constraint Label: CK_Person_EmailPromotion +NodeType: Constraint Label: CK_Person_PersonType +NodeType: Trigger Label: TableTrigger +NodeType: Statistic Label: PK_Person_BusinessEntityID +NodeType: View Label: HumanResources.vEmployee +NodeType: Column Label: BusinessEntityID +NodeType: Column Label: Title +NodeType: Column Label: FirstName +NodeType: Column Label: MiddleName +NodeType: Column Label: LastName +NodeType: Column Label: Suffix +NodeType: Column Label: JobTitle +NodeType: Column Label: AdditionalContactInfo +NodeType: Synonym Label: dbo.MyProduct +NodeType: StoredProcedure Label: HumanResources.sp_GetEmployee_Person_Info_AsOf +NodeType: StoredProcedureParameter Label: @asOf +NodeType: TableValuedFunction Label: dbo.ufnGetContactInformation +NodeType: TableValuedFunctionParameter Label: @PersonID +NodeType: ScalarValuedFunction Label: dbo.fun1 +NodeType: ScalarValuedFunction Label: dbo.ufnGetInventoryStock +NodeType: ScalarValuedFunctionParameter Label: @ProductID +NodeType: DatabaseTrigger Label: Trigger_2 +NodeType: Assembly Label: Microsoft.SqlServer.Types +NodeType: UserDefinedDataType Label: dbo.AccountNumber +NodeType: UserDefinedDataType Label: dbo.Flag +NodeType: UserDefinedDataType Label: dbo.Name +NodeType: UserDefinedDataType Label: dbo.NameStyle +NodeType: UserDefinedDataType Label: dbo.OrderNumber +NodeType: UserDefinedDataType Label: dbo.Phone +NodeType: UserDefinedTableType Label: Demo.SalesOrderDetailType_inmem +NodeType: UserDefinedTableTypeColumn Label: OrderQty +NodeType: UserDefinedTableTypeColumn Label: ProductID +NodeType: UserDefinedTableTypeColumn Label: SpecialOfferID +NodeType: UserDefinedTableType Label: Demo.SalesOrderDetailType_ondisk +NodeType: UserDefinedTableTypeColumn Label: OrderQty +NodeType: UserDefinedTableTypeColumn Label: ProductID +NodeType: UserDefinedTableTypeColumn Label: SpecialOfferID +NodeType: XmlSchemaCollection Label: Person.AdditionalContactInfoSchemaCollection +NodeType: Rule Label: dbo.list_rule +NodeType: Default Label: dbo.phonedflt +NodeType: Sequence Label: Demo.ID_Seq +NodeType: FileGroup Label: PRIMARY +NodeType: FullTextCatalog Label: AW2014FullTextCatalog +NodeType: User Label: amy0 +NodeType: User Label: dbo +NodeType: User Label: guest +NodeType: User Label: INFORMATION_SCHEMA +NodeType: User Label: sys +NodeType: DatabaseRole Label: db_accessadmin +NodeType: DatabaseRole Label: db_backupoperator +NodeType: DatabaseRole Label: db_datareader +NodeType: DatabaseRole Label: db_datawriter +NodeType: DatabaseRole Label: db_ddladmin +NodeType: DatabaseRole Label: db_denydatareader +NodeType: DatabaseRole Label: db_denydatawriter +NodeType: DatabaseRole Label: db_owner +NodeType: DatabaseRole Label: db_securityadmin +NodeType: DatabaseRole Label: public +NodeType: DatabaseRole Label: SalesManagers +NodeType: DatabaseRole Label: SalesPersons +NodeType: Schema Label: dbo +NodeType: Schema Label: db_accessadmin +NodeType: Schema Label: db_backupoperator +NodeType: Schema Label: db_datareader +NodeType: Schema Label: db_datawriter +NodeType: Schema Label: db_ddladmin +NodeType: Schema Label: db_denydatareader +NodeType: Schema Label: db_denydatawriter +NodeType: Schema Label: db_owner +NodeType: Schema Label: db_securityadmin +NodeType: Schema Label: Demo +NodeType: Schema Label: guest +NodeType: Schema Label: HumanResources +NodeType: Schema Label: INFORMATION_SCHEMA +NodeType: Schema Label: Person +NodeType: Schema Label: sys +NodeType: DatabaseEncryptionKey Label: diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/TestScripts/AllSqlObjects.sql b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestData/ObjectExplorer/TestScripts/AllSqlObjects.sql new file mode 100644 index 0000000000000000000000000000000000000000..5579185ee44d4117ac522c19e5135a1ad370a9de GIT binary patch literal 35720 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 literal 0 HcmV?d00001 diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestServiceProvider.cs b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestServiceProvider.cs index 693c07fc..69cdd4e3 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestServiceProvider.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/TestServiceProvider.cs @@ -6,6 +6,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Microsoft.SqlTools.Credentials; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; @@ -78,9 +80,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common /// public void RunQuery(TestServerType serverType, string databaseName, string queryText, bool throwOnError = false) { + string uri = ""; using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) { - ConnectionInfo connInfo = InitLiveConnectionInfo(serverType, databaseName, queryTempFile.FilePath); + uri = queryTempFile.FilePath; + + ConnectionInfo connInfo = InitLiveConnectionInfo(serverType, databaseName, uri); Query query = new Query(queryText, connInfo, new QueryExecutionSettings(), MemoryFileSystem.GetFileStreamFactory()); query.Execute(); query.ExecutionTask.Wait(); @@ -96,7 +101,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common string.Join(Environment.NewLine, errorBatches.Select(b => b.BatchText)))); } } + DisconnectConnection(uri); } + + } + + public static async Task CalculateRunTime(Func> testToRun, bool printResult, [CallerMemberName] string testName = "") + { + TestTimer timer = new TestTimer() { PrintResult = printResult }; + T result = await testToRun(); + timer.EndAndPrint(testName); + + return result; } private ConnectionInfo InitLiveConnectionInfo(TestServerType serverType, string databaseName, string scriptFilePath) @@ -119,6 +135,15 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common return connInfo; } + private void DisconnectConnection(string uri) + { + ConnectionService.Instance.Disconnect(new DisconnectParams + { + OwnerUri = uri, + Type = ConnectionType.Default + }); + } + private static bool hasInitServices = false; private static void InitializeTestServices()