Files
sqltoolsservice/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/Execution/QueryTests.cs
Benjamin Russell d9efb95386 Progressive Results Part 2: Result Completion Event (#134)
The main change in this pull request is to add a new event that will be fired upon completion of a resultset but before the completion of a batch. This event will only fire if a resultset is available and generated.

Changes:
* ConnectionService - Slight changes to enable mocking, cleanup 
* Batch - Moving summary generation into ResultSet class, adding generation of ordinals for resultset and locking of result set list (which needs further refinement, but would be outside scope of this change)
* Adding new event and associated parameters for completion of a resultset. Params return the resultset summary
* Adding logic for assigning the event a handler in the query execution service
* Adding unit tests for testing the new event /making sure the existing tests work
* Refactoring some private properties into member variables

* Refactor to remove SectionData class in favor of BufferRange

* Adding callback for batch completion that will let the extension know that a batch has completed execution

* Refactoring to make progressive results work as per async query execution

* Allowing retrieval of batch results while query is in progress

* reverting global.json, whoops

* Adding a few missing comments, and fixing a couple code style bugs

* Using SelectionData everywhere again

* One more missing comment

* Adding new notification type for result set completion

* Plumbing event for result set completion

* Unit tests for result set events

This includes a fairly substantial change to create a mock of the
ConnectionService and to create separate memorystream storage arrays. It
preserves more correct behavior with a integration test, fixes an issue
where the test db reader will return n-1 rows because the Reliable
Connection Helper steals a record.

* Adding locking to ResultSets for thread safety

* Adding/fixing unit tests

* Adding batch ID to result set summary
2016-11-22 17:37:27 -08:00

270 lines
10 KiB
C#

//
// 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.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution.Execution
{
public class QueryTests
{
[Fact]
public void QueryExecuteNoQueryText()
{
// If:
// ... I create a query that has a null query text
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentException>(() =>
new Query(null, Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), Common.GetFileStreamFactory(null)));
}
[Fact]
public void QueryExecuteNoConnectionInfo()
{
// If:
// ... I create a query that has a null connection info
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() => new Query("Some Query", null, new QueryExecutionSettings(), Common.GetFileStreamFactory(null)));
}
[Fact]
public void QueryExecuteNoSettings()
{
// If:
// ... I create a query that has a null settings
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() =>
new Query("Some query", Common.CreateTestConnectionInfo(null, false), null, Common.GetFileStreamFactory(null)));
}
[Fact]
public void QueryExecuteNoBufferFactory()
{
// If:
// ... I create a query that has a null file stream factory
// Then:
// ... It should throw an exception
Assert.Throws<ArgumentNullException>(() =>
new Query("Some query", Common.CreateTestConnectionInfo(null, false), new QueryExecutionSettings(), null));
}
[Fact]
public void QueryExecuteSingleBatch()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from a single batch (without separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(Common.StandardQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
query.BatchCompleted += batchCallback;
// Then:
// ... I should get a single batch to execute that hasn't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with one batch summary returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
// ... The batch callback should have been called precisely 1 time
Assert.Equal(1, batchCallbacksReceived);
}
[Fact]
public void QueryExecuteNoOpBatch()
{
// Setup:
// ... Create a callback for batch completion
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
throw new Exception("Batch completion callback was called");
};
// If:
// ... I create a query from a single batch that does nothing
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(Common.NoOpQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
query.BatchCompleted += batchCallback;
// Then:
// ... I should get no batches back
Assert.NotEmpty(query.QueryText);
Assert.Empty(query.Batches);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I Then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with no batch summaries returned
Assert.True(query.HasExecuted);
Assert.Empty(query.BatchSummaries);
}
[Fact]
public void QueryExecuteMultipleBatches()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from two batches (with separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
string queryText = string.Format("{0}\r\nGO\r\n{0}", Common.StandardQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(queryText, ci, new QueryExecutionSettings(), fileStreamFactory);
query.BatchCompleted += batchCallback;
// Then:
// ... I should get back two batches to execute that haven't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(2, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... The query should have completed successfully with two batch summaries returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(2, query.BatchSummaries.Length);
// ... The batch callback should have been called precisely 2 times
Assert.Equal(2, batchCallbacksReceived);
}
[Fact]
public void QueryExecuteMultipleBatchesWithNoOp()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from a two batches (with separator)
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, false);
string queryText = string.Format("{0}\r\nGO\r\n{1}", Common.StandardQuery, Common.NoOpQuery);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(queryText, ci, new QueryExecutionSettings(), fileStreamFactory);
query.BatchCompleted += batchCallback;
// Then:
// ... I should get back one batch to execute that hasn't been executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// .. I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// ... The query should have completed successfully with one batch summary returned
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
// ... The batch callback should have been called precisely 1 time
Assert.Equal(1, batchCallbacksReceived);
}
[Fact]
public void QueryExecuteInvalidBatch()
{
// Setup:
// ... Create a callback for batch completion
int batchCallbacksReceived = 0;
Batch.BatchAsyncEventHandler batchCallback = summary =>
{
batchCallbacksReceived++;
return Task.CompletedTask;
};
// If:
// ... I create a query from an invalid batch
ConnectionInfo ci = Common.CreateTestConnectionInfo(null, true);
var fileStreamFactory = Common.GetFileStreamFactory(new Dictionary<string, byte[]>());
Query query = new Query(Common.InvalidQuery, ci, new QueryExecutionSettings(), fileStreamFactory);
query.BatchCompleted += batchCallback;
// Then:
// ... I should get back a query with one batch not executed
Assert.NotEmpty(query.QueryText);
Assert.NotEmpty(query.Batches);
Assert.Equal(1, query.Batches.Length);
Assert.False(query.HasExecuted);
Assert.Throws<InvalidOperationException>(() => query.BatchSummaries);
// If:
// ... I then execute the query
query.Execute();
query.ExecutionTask.Wait();
// Then:
// ... There should be an error on the batch
Assert.True(query.HasExecuted);
Assert.NotEmpty(query.BatchSummaries);
Assert.Equal(1, query.BatchSummaries.Length);
Assert.True(query.BatchSummaries[0].HasError);
Assert.NotEmpty(query.BatchSummaries[0].Messages);
// ... The batch callback should have been called once
Assert.Equal(1, batchCallbacksReceived);
}
}
}