// // 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.Threading.Tasks; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.SqlContext; using Microsoft.SqlTools.ServiceLayer.Test.Common; using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility; using Microsoft.SqlTools.ServiceLayer.Workspace; using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts; using Moq; using NUnit.Framework; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Workspace { public class WorkspaceTests { [Test] public async Task FileClosedSuccessfully() { // Given: // ... A workspace that has a single file open var workspace = new ServiceLayer.Workspace.Workspace(); var workspaceService = new WorkspaceService {Workspace = workspace}; var openedFile = workspace.GetFileBuffer(TestObjects.ScriptUri, string.Empty); Assert.NotNull(openedFile); Assert.That(workspace.GetOpenedFiles(), Is.Not.Empty); // ... And there is a callback registered for the file closed event ScriptFile closedFile = null; string closedUri = null; workspaceService.RegisterTextDocCloseCallback((u, f, c) => { closedUri = u; closedFile = f; return Task.FromResult(true); }); // If: // ... An event to close the open file occurs var eventContext = new Mock().Object; var requestParams = new DidCloseTextDocumentParams { TextDocument = new TextDocumentItem {Uri = TestObjects.ScriptUri} }; await workspaceService.HandleDidCloseTextDocumentNotification(requestParams, eventContext); // Then: // ... The file should no longer be in the open files Assert.That(workspace.GetOpenedFiles(), Is.Empty); // ... The callback should have been called // ... The provided script file should be the one we created Assert.NotNull(closedFile); Assert.AreEqual(openedFile, closedFile); Assert.AreEqual(TestObjects.ScriptUri, closedUri); } [Test] public async Task FileClosedNotOpen() { // Given: // ... A workspace that has no files open var workspace = new ServiceLayer.Workspace.Workspace(); var workspaceService = new WorkspaceService {Workspace = workspace}; Assert.That(workspace.GetOpenedFiles(), Is.Empty); // ... And there is a callback registered for the file closed event bool callbackCalled = false; workspaceService.RegisterTextDocCloseCallback((u, f, c) => { callbackCalled = true; return Task.FromResult(true); }); // If: // ... An event to close the a file occurs var eventContext = new Mock().Object; var requestParams = new DidCloseTextDocumentParams { TextDocument = new TextDocumentItem {Uri = TestObjects.ScriptUri} }; // Then: await workspaceService.HandleDidCloseTextDocumentNotification(requestParams, eventContext); // ... There should still be no open files // ... The callback should not have been called Assert.That(workspace.GetOpenedFiles(), Is.Empty); Assert.False(callbackCalled); } [Test] public void BufferRangeNoneNotNull() { Assert.NotNull(BufferRange.None); } [Test] public void BufferRangeStartGreaterThanEnd() { Assert.Throws(() => new BufferRange(new BufferPosition(2, 2), new BufferPosition(1, 1))); } [Test] public void BufferRangeEquals() { var range = new BufferRange(new BufferPosition(1, 1), new BufferPosition(2, 2)); Assert.False(range.Equals(null)); Assert.True(range.Equals(range)); Assert.NotNull(range.GetHashCode()); } [Test] public void UnescapePath() { Assert.NotNull(Microsoft.SqlTools.ServiceLayer.Workspace.Workspace.UnescapePath("`/path/`")); } [Test] public void GetBaseFilePath() { RunIfWrapper.RunIfWindows(() => { using (var workspace = new ServiceLayer.Workspace.Workspace()) { Assert.Throws(() => workspace.GetBaseFilePath("path")); Assert.NotNull(workspace.GetBaseFilePath(@"c:\path\file.sql")); Assert.AreEqual(workspace.GetBaseFilePath("tsqloutput://c:/path/file.sql"), workspace.WorkspacePath); } }); } [Test] public void ResolveRelativeScriptPath() { RunIfWrapper.RunIfWindows(() => { var workspace = new ServiceLayer.Workspace.Workspace(); Assert.NotNull(workspace.ResolveRelativeScriptPath(null, @"c:\path\file.sql")); Assert.NotNull(workspace.ResolveRelativeScriptPath(@"c:\path\", "file.sql")); }); } [Test] public async Task DontProcessGitFileEvents() { await VerifyFileIsNotAddedOnDocOpened("git:/myfile.sql"); } [Test] public async Task DontProcessPerforceFileEvents() { await VerifyFileIsNotAddedOnDocOpened("perforce:/myfile.sql"); } private async Task VerifyFileIsNotAddedOnDocOpened(string filePath) { // setup test workspace var workspace = new ServiceLayer.Workspace.Workspace(); var workspaceService = new WorkspaceService {Workspace = workspace}; // send a document open event with git:/ prefix URI var openParams = new DidOpenTextDocumentNotification { TextDocument = new TextDocumentItem { Uri = filePath } }; await workspaceService.HandleDidOpenTextDocumentNotification(openParams, eventContext: null); // verify the file is not being tracked by workspace Assert.False(workspaceService.Workspace.ContainsFile(filePath)); // send a close event with git:/ prefix URI var closeParams = new DidCloseTextDocumentParams { TextDocument = new TextDocumentItem { Uri = filePath } }; await workspaceService.HandleDidCloseTextDocumentNotification(closeParams, eventContext: null); // this is not that interesting validation since the open is ignored // the main validation is that close doesn't raise an exception Assert.False(workspaceService.Workspace.ContainsFile(filePath)); } [Test] public void GetFileReturnsNullForPerforceFile() { // when I ask for a non-file object in the workspace, it should return null var workspace = new ServiceLayer.Workspace.Workspace(); ScriptFile file = workspace.GetFile("perforce:myfile.sql"); Assert.Null(file); } [Test] [TestCase(TestObjects.ScriptUri)] [TestCase("file://some/path%20with%20encoded%20spaces/file.sql")] [TestCase("file://some/path with spaces/file.sql")] [TestCase("file://some/fileWith#.sql")] [TestCase("file://some/fileUriWithQuery.sql?var=foo")] [TestCase("file://some/fileUriWithFragment.sql#foo")] public async Task WorkspaceContainsFile(string uri) { var workspace = new ServiceLayer.Workspace.Workspace(); var workspaceService = new WorkspaceService {Workspace = workspace}; workspace.GetFileBuffer(uri, string.Empty); // send a document open event var openParams = new DidOpenTextDocumentNotification { TextDocument = new TextDocumentItem { Uri = uri } }; await workspaceService.HandleDidOpenTextDocumentNotification(openParams, eventContext: null); // verify the file is being tracked by workspace Assert.True(workspaceService.Workspace.ContainsFile(uri)); } [Test] public void DontBindToObjectExplorerConnectEvents() { // when I ask for a non-file object in the workspace, it should return null var workspace = new ServiceLayer.Workspace.Workspace(); ScriptFile file = workspace.GetFile("objectexplorer://server;database=database;user=user"); Assert.Null(file); // when I ask for a file, it should return the file string tempFile = Path.GetTempFileName(); string fileContents = "hello world"; File.WriteAllText(tempFile, fileContents); file = workspace.GetFile(tempFile); Assert.AreEqual(fileContents, file.Contents); if (tempFile.StartsWith("/")) { tempFile = tempFile.Substring(1); } file = workspace.GetFile("file://" + tempFile); Assert.AreEqual(fileContents, file.Contents); file = workspace.GetFileBuffer("untitled://"+ tempFile, fileContents); Assert.AreEqual(fileContents, file.Contents); // For windows files, just check scheme is null since it's hard to mock file contents in these Assert.Null(ServiceLayer.Workspace.Workspace.GetScheme(@"C:\myfile.sql")); Assert.Null(ServiceLayer.Workspace.Workspace.GetScheme(@"\\myfile.sql")); } } }