diff --git a/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs b/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs
index 2f25bf6d..1b3d4074 100644
--- a/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs
+++ b/src/Microsoft.SqlTools.Hosting/Utility/Validate.cs
@@ -37,9 +37,9 @@ namespace Microsoft.SqlTools.Utility
/// The upper limit which the value should not be greater than.
public static void IsWithinRange(
string parameterName,
- int valueToCheck,
- int lowerLimit,
- int upperLimit)
+ long valueToCheck,
+ long lowerLimit,
+ long upperLimit)
{
// TODO: Debug assert here if lowerLimit >= upperLimit
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditRow.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditRow.cs
new file mode 100644
index 00000000..2042bea0
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditRow.cs
@@ -0,0 +1,45 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts
+{
+ ///
+ /// A way to return a row in a result set that is being edited. It contains state about whether
+ /// or not the row is dirty
+ ///
+ public class EditRow
+ {
+ public enum EditRowState
+ {
+ Clean = 0,
+ DirtyInsert = 1,
+ DirtyDelete = 2,
+ DirtyUpdate = 3
+ }
+
+ ///
+ /// The cells in the row. If the row has pending changes, they will be represented in
+ /// this list
+ ///
+ public DbCellValue[] Cells { get; set; }
+
+ ///
+ /// Internal ID of the row. This should be used whenever referencing a row in row edit operations.
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// Whether or not the row has changes pending
+ ///
+ public bool IsDirty => State != EditRowState.Clean;
+
+ ///
+ /// What type of dirty state (or lack thereof) the row is
+ ///
+ public EditRowState State { get; set; }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditSubsetRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditSubsetRequest.cs
new file mode 100644
index 00000000..623098e4
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/Contracts/EditSubsetRequest.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+
+namespace Microsoft.SqlTools.ServiceLayer.EditData.Contracts
+{
+ ///
+ /// Parameters for a subset retrieval request
+ ///
+ public class EditSubsetParams : SessionOperationParams
+ {
+ ///
+ /// Beginning index of the rows to return from the selected resultset. This index will be
+ /// included in the results.
+ ///
+ public long RowStartIndex { get; set; }
+
+ ///
+ /// Number of rows to include in the result of this request. If the number of the rows
+ /// exceeds the number of rows available after the start index, all available rows after
+ /// the start index will be returned.
+ ///
+ public int RowCount { get; set; }
+ }
+
+ ///
+ /// Parameters for the result of a subset retrieval request
+ ///
+ public class EditSubsetResult
+ {
+ ///
+ /// The number of rows returned from result set, useful for determining if less rows were
+ /// returned than requested.
+ ///
+ public int RowCount { get; set; }
+
+ ///
+ /// The requested subset of rows, with information about whether or not the rows are dirty
+ ///
+ public EditRow[] Subset { get; set; }
+ }
+
+ public class EditSubsetRequest
+ {
+ public static readonly
+ RequestType Type =
+ RequestType.Create("edit/subset");
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs
index de1867d1..e5e67731 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditDataService.cs
@@ -91,6 +91,7 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
serviceHost.SetRequestHandler(EditInitializeRequest.Type, HandleInitializeRequest);
serviceHost.SetRequestHandler(EditRevertCellRequest.Type, HandleRevertCellRequest);
serviceHost.SetRequestHandler(EditRevertRowRequest.Type, HandleRevertRowRequest);
+ serviceHost.SetRequestHandler(EditSubsetRequest.Type, HandleSubsetRequest);
serviceHost.SetRequestHandler(EditUpdateCellRequest.Type, HandleUpdateCellRequest);
}
@@ -241,6 +242,20 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
});
}
+ internal Task HandleSubsetRequest(EditSubsetParams subsetParams,
+ RequestContext requestContext)
+ {
+ return HandleSessionRequest(subsetParams, requestContext, session =>
+ {
+ EditRow[] rows = session.GetRows(subsetParams.RowStartIndex, subsetParams.RowCount).Result;
+ return new EditSubsetResult
+ {
+ RowCount = rows.Length,
+ Subset = rows
+ };
+ });
+ }
+
internal Task HandleUpdateCellRequest(EditUpdateCellParams updateParams,
RequestContext requestContext)
{
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs
index bccd3aa5..bad9f4d4 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/EditSession.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData
@@ -186,6 +187,60 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData
}
}
+ ///
+ /// Retrieves a subset of rows with the pending updates applied. If more rows than exist
+ /// are requested, only the rows that exist will be returned.
+ ///
+ /// Index to start returning rows from
+ /// The number of rows to return.
+ /// An array of rows with pending edits applied
+ public async Task GetRows(long startIndex, int rowCount)
+ {
+ // Get the cached rows from the result set
+ ResultSetSubset cachedRows = startIndex < associatedResultSet.RowCount
+ ? await associatedResultSet.GetSubset(startIndex, rowCount)
+ : new ResultSetSubset
+ {
+ RowCount = 0,
+ Rows = new DbCellValue[][] { }
+ };
+
+ // Convert the rows into EditRows and apply the changes we have
+ List editRows = new List();
+ for (int i = 0; i < cachedRows.RowCount; i++)
+ {
+ long rowId = i + startIndex;
+ RowEditBase edr;
+ if (EditCache.TryGetValue(rowId, out edr))
+ {
+ // Ask the edit object to generate an edit row
+ editRows.Add(edr.GetEditRow(cachedRows.Rows[i]));
+ }
+ else
+ {
+ // Package up the existing row into a clean edit row
+ EditRow er = new EditRow
+ {
+ Id = rowId,
+ Cells = cachedRows.Rows[i],
+ State = EditRow.EditRowState.Clean
+ };
+ editRows.Add(er);
+ }
+ }
+
+ // If the requested range of rows was at the end of the original cell set and we have
+ // added new rows, we need to reflect those changes
+ if (rowCount > cachedRows.RowCount)
+ {
+ long endIndex = startIndex + cachedRows.RowCount;
+ var newRows = EditCache.Where(edit => edit.Key >= endIndex).Take(rowCount - cachedRows.RowCount);
+ editRows.AddRange(newRows.Select(newRow => newRow.Value.GetEditRow(null)));
+ }
+
+ return editRows.ToArray();
+ }
+
///
/// Reverts a cell in a pending edit
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs
index a24e5d4e..9aea0fd5 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/CellUpdate.cs
@@ -85,6 +85,22 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
#region Properties
+ ///
+ /// Converts the cell update to a DbCellValue
+ ///
+ public DbCellValue AsDbCellValue
+ {
+ get
+ {
+ return new DbCellValue
+ {
+ DisplayValue = ValueAsString,
+ IsNull = Value == DBNull.Value,
+ RawObject = Value
+ };
+ }
+ }
+
///
/// The column that the cell will be placed in
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs
index db9fbf29..b5292fa0 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowCreate.cs
@@ -122,6 +122,26 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
return command;
}
+ ///
+ /// Generates a edit row that represents a row pending insertion
+ ///
+ /// Original, cached cell contents. (Should be null in this case)
+ /// EditRow of pending update
+ public override EditRow GetEditRow(DbCellValue[] cachedRow)
+ {
+ // Iterate over the new cells. If they are null, generate a blank value
+ DbCellValue[] editCells = newCells.Select(cell => cell == null
+ ? new DbCellValue {DisplayValue = string.Empty, IsNull = false, RawObject = null}
+ : cell.AsDbCellValue)
+ .ToArray();
+ return new EditRow
+ {
+ Id = RowId,
+ Cells = editCells,
+ State = EditRow.EditRowState.DirtyInsert
+ };
+ }
+
///
/// Generates the INSERT INTO statement that will apply the row creation
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs
index fc92e4af..64b0ddc9 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowDelete.cs
@@ -9,6 +9,7 @@ using System.Globalization;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
@@ -72,6 +73,24 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
return command;
}
+ ///
+ /// Generates a edit row that represents a row pending deletion. All the original cells are
+ /// intact but the state is dirty.
+ ///
+ /// Original, cached cell contents
+ /// EditRow that is pending deletion
+ public override EditRow GetEditRow(DbCellValue[] cachedRow)
+ {
+ Validate.IsNotNull(nameof(cachedRow), cachedRow);
+
+ return new EditRow
+ {
+ Id = RowId,
+ Cells = cachedRow,
+ State = EditRow.EditRowState.DirtyDelete
+ };
+ }
+
///
/// Generates a DELETE statement to delete this row
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs
index 6260a39d..9df74c50 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowEdit.cs
@@ -85,6 +85,14 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
/// Command to commit the change to the db
public abstract DbCommand GetCommand(DbConnection connection);
+ ///
+ /// Generates a row that has the pending update applied. The dirty status of the row is
+ /// reflected in the returned EditRow.
+ ///
+ /// The original, cached row values
+ /// An EditRow with the pending changes applied
+ public abstract EditRow GetEditRow(DbCellValue[] cachedRow);
+
///
/// Converts the row edit into a SQL statement
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs
index 6e43aa29..3342f1d7 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/EditData/UpdateManagement/RowUpdate.cs
@@ -113,6 +113,30 @@ namespace Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement
return command;
}
+ ///
+ /// Generates a edit row that represents a row with pending update. The cells pending
+ /// updates are merged into the unchanged cells.
+ ///
+ /// Original, cached cell contents
+ /// EditRow with pending updates
+ public override EditRow GetEditRow(DbCellValue[] cachedRow)
+ {
+ Validate.IsNotNull(nameof(cachedRow), cachedRow);
+
+ // For each cell that is pending update, replace the db cell value with a new one
+ foreach (var cellUpdate in cellUpdates)
+ {
+ cachedRow[cellUpdate.Key] = cellUpdate.Value.AsDbCellValue;
+ }
+
+ return new EditRow
+ {
+ Id = RowId,
+ Cells = cachedRow,
+ State = EditRow.EditRowState.DirtyUpdate
+ };
+ }
+
///
/// Constructs an update statement to change the associated row.
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
index a912d30f..58ab4ccf 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Batch.cs
@@ -355,7 +355,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// The starting row of the results
/// How many rows to retrieve
/// A subset of results
- public Task GetSubset(int resultSetIndex, int startRow, int rowCount)
+ public Task GetSubset(int resultSetIndex, long startRow, int rowCount)
{
ResultSet targetResultSet;
lock (resultSets)
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/ResultSetSubset.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/ResultSetSubset.cs
index 62308824..d416753a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/ResultSetSubset.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/ResultSetSubset.cs
@@ -19,6 +19,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
///
/// 2D array of the cell values requested from result set
///
- public string[][] Rows { get; set; }
+ public DbCellValue[][] Rows { get; set; }
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SubsetRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SubsetRequest.cs
index aec9ee07..f61fd559 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SubsetRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SubsetRequest.cs
@@ -31,7 +31,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
/// Beginning index of the rows to return from the selected resultset. This index will be
/// included in the results.
///
- public int RowsStartIndex { get; set; }
+ public long RowsStartIndex { get; set; }
///
/// Number of rows to include in the result of this request. If the number of the rows
@@ -46,11 +46,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
///
public class SubsetResult
{
- ///
- /// Subset request error messages. Optional, can be set to null to indicate no errors
- ///
- public string Message { get; set; }
-
///
/// The requested subset of results. Optional, can be set to null to indicate an error
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
index 3a62c9f9..e7cd81eb 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Query.cs
@@ -280,7 +280,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// The starting row of the results
/// How many rows to retrieve
/// A subset of results
- public Task GetSubset(int batchIndex, int resultSetIndex, int startRow, int rowCount)
+ public Task GetSubset(int batchIndex, int resultSetIndex, long startRow, int rowCount)
{
// Sanity check to make sure that the batch is within bounds
if (batchIndex < 0 || batchIndex >= Batches.Length)
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
index a9f0a012..54fa8f36 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
@@ -176,42 +176,13 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
try
{
- // Attempt to load the query
- Query query;
- if (!ActiveQueries.TryGetValue(subsetParams.OwnerUri, out query))
- {
- await requestContext.SendResult(new SubsetResult
- {
- Message = SR.QueryServiceRequestsNoQuery
- });
- return;
- }
-
- // Retrieve the requested subset and return it
+ ResultSetSubset subset = await InterServiceResultSubset(subsetParams);
var result = new SubsetResult
{
- Message = null,
- ResultSubset = await query.GetSubset(subsetParams.BatchIndex,
- subsetParams.ResultSetIndex, subsetParams.RowsStartIndex, subsetParams.RowsCount)
+ ResultSubset = subset
};
await requestContext.SendResult(result);
}
- catch (InvalidOperationException ioe)
- {
- // Return the error as a result
- await requestContext.SendResult(new SubsetResult
- {
- Message = ioe.Message
- });
- }
- catch (ArgumentOutOfRangeException aoore)
- {
- // Return the error as a result
- await requestContext.SendResult(new SubsetResult
- {
- Message = aoore.Message
- });
- }
catch (Exception e)
{
// This was unexpected, so send back as error
@@ -415,7 +386,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// The identifier of the query to be disposed
/// Action to perform on success
/// Action to perform on failure
- ///
public async Task InterServiceDisposeQuery(string ownerUri, Func successAction,
Func failureAction)
{
@@ -444,6 +414,29 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
}
+ ///
+ /// Retrieves the requested subset of rows from the requested result set. Intended to be
+ /// called by another service.
+ ///
+ /// Parameters for the subset to retrieve
+ /// The requested subset
+ /// The requested query does not exist
+ public async Task InterServiceResultSubset(SubsetParams subsetParams)
+ {
+ Validate.IsNotNullOrEmptyString(nameof(subsetParams.OwnerUri), subsetParams.OwnerUri);
+
+ // Attempt to load the query
+ Query query;
+ if (!ActiveQueries.TryGetValue(subsetParams.OwnerUri, out query))
+ {
+ throw new ArgumentOutOfRangeException(SR.QueryServiceRequestsNoQuery);
+ }
+
+ // Retrieve the requested subset and return it
+ return await query.GetSubset(subsetParams.BatchIndex, subsetParams.ResultSetIndex,
+ subsetParams.RowsStartIndex, subsetParams.RowsCount);
+ }
+
#endregion
#region Private Helpers
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs
index 55747e4e..2c7747d1 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/ResultSet.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
+using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
@@ -223,7 +224,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
/// The starting row of the results
/// How many rows to retrieve
/// A subset of results
- public Task GetSubset(int startRow, int rowCount)
+ public Task GetSubset(long startRow, int rowCount)
{
// Sanity check to make sure that the results have been read beforehand
if (!hasBeenRead)
@@ -244,7 +245,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
return Task.Factory.StartNew(() =>
{
- string[][] rows;
+ DbCellValue[][] rows;
using (IFileStreamReader fileStreamReader = fileStreamFactory.GetReader(outputFileName))
{
@@ -255,19 +256,23 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
// Iterate over all the rows and process them into a list of string builders
// ReSharper disable once AccessToDisposedClosure The lambda is used immediately in string.Join call
IEnumerable rowValues = fileOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns)[0].DisplayValue);
- rows = new[] { new[] { string.Join(string.Empty, rowValues) } };
+ string singleString = string.Join(string.Empty, rowValues);
+ DbCellValue cellValue = new DbCellValue
+ {
+ DisplayValue = singleString,
+ IsNull = false,
+ RawObject = singleString
+ };
+ rows = new[] { new[] { cellValue } };
}
else
{
// Figure out which rows we need to read back
- IEnumerable rowOffsets = fileOffsets.Skip(startRow).Take(rowCount);
+ IEnumerable rowOffsets = fileOffsets.LongSkip(startRow).Take(rowCount);
// Iterate over the rows we need and process them into output
// ReSharper disable once AccessToDisposedClosure The lambda is used immediately in .ToArray call
- rows = rowOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns)
- .Select(cell => cell.DisplayValue).ToArray())
- .ToArray();
-
+ rows = rowOffsets.Select(rowOffset => fileStreamReader.ReadRow(rowOffset, Columns).ToArray()).ToArray();
}
}
// Retrieve the subset of the results as per the request
diff --git a/src/Microsoft.SqlTools.Hosting/Utility/LongList.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/LongList.cs
similarity index 72%
rename from src/Microsoft.SqlTools.Hosting/Utility/LongList.cs
rename to src/Microsoft.SqlTools.ServiceLayer/Utility/LongList.cs
index d3d0cc10..25d66110 100644
--- a/src/Microsoft.SqlTools.Hosting/Utility/LongList.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/LongList.cs
@@ -6,8 +6,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using Microsoft.SqlTools.Utility;
-namespace Microsoft.SqlTools.Utility
+namespace Microsoft.SqlTools.ServiceLayer.Utility
{
///
/// Collection class that permits storage of over int.MaxValue items. This is performed
@@ -22,8 +23,7 @@ namespace Microsoft.SqlTools.Utility
public class LongList : IEnumerable
{
#region Member Variables
-
- private int expandListSize = int.MaxValue;
+
private List> expandedList;
private readonly List shortList;
@@ -35,6 +35,7 @@ namespace Microsoft.SqlTools.Utility
public LongList()
{
shortList = new List();
+ ExpandListSize = int.MaxValue;
Count = 0;
}
@@ -45,11 +46,15 @@ namespace Microsoft.SqlTools.Utility
///
public long Count { get; private set; }
+ ///
+ /// Used to get or set the value at a given index in the list
+ ///
+ /// Index into the list to access
public T this[long index]
{
- get
- {
- return GetItem(index);
+ get
+ {
+ return GetItem(index);
}
set
@@ -58,17 +63,10 @@ namespace Microsoft.SqlTools.Utility
}
}
- public int ExpandListSize
- {
- get
- {
- return this.expandListSize;
- }
- internal set
- {
- this.expandListSize = value;
- }
- }
+ ///
+ /// The number of elements to store in a single list before expanding into multiple lists
+ ///
+ public int ExpandListSize { get; internal set; }
#endregion
@@ -81,7 +79,7 @@ namespace Microsoft.SqlTools.Utility
/// Index of the item that was just added
public long Add(T val)
{
- if (Count <= this.ExpandListSize)
+ if (Count < this.ExpandListSize)
{
shortList.Add(val);
}
@@ -118,21 +116,22 @@ namespace Microsoft.SqlTools.Utility
/// The item at the index specified
public T GetItem(long index)
{
- T val = default(T);
+ Validate.IsWithinRange(nameof(index), index, 0, Count - 1);
- if (Count <= this.ExpandListSize)
+ T val = default(T);
+ if (Count < this.ExpandListSize)
{
int i32Index = Convert.ToInt32(index);
val = shortList[i32Index];
}
else
{
- int iArray32Index = (int) (Count / this.ExpandListSize);
+ int iArray32Index = (int) (index / this.ExpandListSize);
if (expandedList.Count > iArray32Index)
{
List arr = expandedList[iArray32Index];
- int i32Index = (int) (Count % this.ExpandListSize);
+ int i32Index = (int) (index % this.ExpandListSize);
if (arr.Count > i32Index)
{
val = arr[i32Index];
@@ -142,6 +141,25 @@ namespace Microsoft.SqlTools.Utility
return val;
}
+ ///
+ /// Skips ahead the number of elements requested and returns the elements after that many elements
+ ///
+ /// The number of elements to skip
+ /// All elements after the number of elements to skip
+ public IEnumerable LongSkip(long start)
+ {
+ Validate.IsWithinRange(nameof(start), start, 0, Count - 1);
+
+ // Generate an enumerator over this list and jump ahead to the position we want
+ LongListEnumerator longEnumerator = new LongListEnumerator(this) {Index = start - 1};
+
+ // While there are results to get, yield return them
+ while (longEnumerator.MoveNext())
+ {
+ yield return longEnumerator.Current;
+ }
+ }
+
///
/// Sets the item at the specified index
///
@@ -158,10 +176,10 @@ namespace Microsoft.SqlTools.Utility
}
else
{
- int iArray32Index = (int) (Count / this.ExpandListSize);
+ int iArray32Index = (int) (index / this.ExpandListSize);
List arr = expandedList[iArray32Index];
- int i32Index = (int)(Count % this.ExpandListSize);
+ int i32Index = (int)(index % this.ExpandListSize);
arr[i32Index] = value;
}
}
@@ -173,6 +191,8 @@ namespace Microsoft.SqlTools.Utility
/// The index to remove from the list
public void RemoveAt(long index)
{
+ Validate.IsWithinRange(nameof(index), index, 0, Count - 1);
+
if (Count <= this.ExpandListSize)
{
int iArray32MemberIndex = Convert.ToInt32(index); // 0 based
@@ -189,14 +209,19 @@ namespace Microsoft.SqlTools.Utility
arr.RemoveAt(iArray32MemberIndex);
// now shift members of the array back one
- int iArray32TotalIndex = (int) (Count / this.ExpandListSize);
- for (int i = arrayIndex + 1; i < iArray32TotalIndex; i++)
+ //int iArray32TotalIndex = (int) (Count / this.ExpandListSize);
+ for (int i = arrayIndex + 1; i < expandedList.Count; i++)
{
List arr1 = expandedList[i - 1];
List arr2 = expandedList[i];
- arr1.Add(arr2[this.ExpandListSize - 1]);
+ arr1.Add(arr2[0]);
arr2.RemoveAt(0);
+
+ if (arr2.Count == 0)
+ {
+ expandedList.RemoveAt(i);
+ }
}
}
--Count;
@@ -228,19 +253,15 @@ namespace Microsoft.SqlTools.Utility
public class LongListEnumerator : IEnumerator
{
- #region Member Variables
-
- ///
- /// The index into the list of the item that is the current item
- ///
- private long index;
-
///
/// The current list that we're iterating over.
///
private readonly LongList localList;
- #endregion
+ ///
+ /// The current index into the list
+ ///
+ private long index;
///
/// Constructs a new enumerator for a given LongList
@@ -249,10 +270,27 @@ namespace Microsoft.SqlTools.Utility
public LongListEnumerator(LongList list)
{
localList = list;
- index = 0;
+ index = -1;
Current = default(TEt);
}
+ ///
+ /// The index into the list of the item that is the current item. Upon setting,
+ /// will be updated if the index is in range. Otherwise,
+ /// default() will be used.
+ ///
+ public long Index
+ {
+ get { return index; }
+ set
+ {
+ index = value;
+ Current = value >= localList.Count || value < 0
+ ? default(TEt)
+ : localList[index];
+ }
+ }
+
#region IEnumerator Implementation
///
@@ -260,10 +298,10 @@ namespace Microsoft.SqlTools.Utility
///
public TEt Current { get; private set; }
- object IEnumerator.Current
- {
- get { return Current; }
- }
+ ///
+ /// Returns the current item in the enumeration
+ ///
+ object IEnumerator.Current => Current;
///
/// Moves to the next item in the list we're iterating over
@@ -271,14 +309,8 @@ namespace Microsoft.SqlTools.Utility
/// Whether or not the move was successful
public bool MoveNext()
{
- if (index < localList.Count)
- {
- Current = localList[index];
- index++;
- return true;
- }
- Current = default(TEt);
- return false;
+ Index++;
+ return Index < localList.Count;
}
///
@@ -286,7 +318,7 @@ namespace Microsoft.SqlTools.Utility
///
public void Reset()
{
- index = 0;
+ Index = 0;
Current = default(TEt);
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs
index 4ed3c557..a61b4ed7 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/CellUpdateTests.cs
@@ -216,6 +216,33 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
}
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void AsDbCellValue(bool isNull)
+ {
+ // Setup: Create a cell update
+ var value = isNull ? "NULL" : "foo";
+ var col = GetWrapper("NTEXT");
+ CellUpdate cu = new CellUpdate(col, value);
+
+ // If: I convert it to a DbCellvalue
+ DbCellValue dbc = cu.AsDbCellValue;
+
+ // Then:
+ // ... It should not be null
+ Assert.NotNull(dbc);
+
+ // ... The display value should be the same as the value we supplied
+ Assert.Equal(value, dbc.DisplayValue);
+
+ // ... The null-ness of the value should be the same as what we supplied
+ Assert.Equal(isNull, dbc.IsNull);
+
+ // ... We don't care *too* much about the raw value, but we'll check it anyhow
+ Assert.Equal(isNull ? (object)DBNull.Value : value, dbc.RawObject);
+ }
+
private static DbColumnWrapper GetWrapper(string dataTypeName, bool allowNull = true)
{
return new DbColumnWrapper(new CellUpdateTestDbColumn(typeof(T), dataTypeName, allowNull));
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs
index 80ac1a66..4c49121b 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowCreateTests.cs
@@ -12,6 +12,7 @@ using Microsoft.SqlTools.ServiceLayer.EditData;
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Xunit;
@@ -181,6 +182,64 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
Assert.Throws(() => rc.GetCommand(mockConn));
}
+ [Fact]
+ public void GetEditRowNoAdditions()
+ {
+ // Setup: Generate a standard row create
+ RowCreate rc = GetStandardRowCreate();
+
+ // If: I request an edit row from the row create
+ EditRow er = rc.GetEditRow(null);
+
+ // Then:
+ // ... The row should not be null
+ Assert.NotNull(er);
+
+ // ... The row should not be clean
+ Assert.True(er.IsDirty);
+ Assert.Equal(EditRow.EditRowState.DirtyInsert, er.State);
+
+ // ... The row should have a bunch of empty cells (equal to number of columns)
+ Assert.Equal(rc.newCells.Length, er.Cells.Length);
+ Assert.All(er.Cells, dbc =>
+ {
+ Assert.Equal(string.Empty, dbc.DisplayValue);
+ Assert.False(dbc.IsNull);
+ });
+ }
+
+ [Fact]
+ public void GetEditRowWithAdditions()
+ {
+ // Setp: Generate a row create with a cell added to it
+ RowCreate rc = GetStandardRowCreate();
+ rc.SetCell(0, "foo");
+
+ // If: I request an edit row from the row create
+ EditRow er = rc.GetEditRow(null);
+
+ // Then:
+ // ... The row should not be null and contain the same number of cells as columns
+ Assert.NotNull(er);
+ Assert.Equal(EditRow.EditRowState.DirtyInsert, er.State);
+
+ // ... The row should not be clean
+ Assert.True(er.IsDirty);
+ Assert.Equal(EditRow.EditRowState.DirtyInsert, er.State);
+
+ // ... The row should have a single non-empty cell at the beginning
+ Assert.Equal("foo", er.Cells[0].DisplayValue);
+ Assert.False(er.Cells[0].IsNull);
+
+ // ... The rest of the cells should be blank
+ for (int i = 1; i < er.Cells.Length; i++)
+ {
+ DbCellValue dbc = er.Cells[i];
+ Assert.Equal(string.Empty, dbc.DisplayValue);
+ Assert.False(dbc.IsNull);
+ }
+ }
+
[Theory]
[InlineData(-1)] // Negative
[InlineData(3)] // At edge of acceptable values
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs
index dfa4e647..941e14ed 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowDeleteTests.cs
@@ -5,11 +5,14 @@
using System;
using System.Data.Common;
+using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.EditData;
+using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Xunit;
@@ -21,15 +24,15 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public void RowDeleteConstruction()
{
// Setup: Create the values to store
- const long rowId = 100;
- ResultSet rs = QueryExecution.Common.GetBasicExecutedBatch().ResultSets[0];
- IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
+ DbColumn[] columns = Common.GetColumns(true);
+ ResultSet rs = Common.GetResultSet(columns, true);
+ IEditTableMetadata etm = Common.GetStandardMetadata(columns, false);
// If: I create a RowCreate instance
- RowCreate rc = new RowCreate(rowId, rs, etm);
+ RowDelete rc = new RowDelete(100, rs, etm);
// Then: The values I provided should be available
- Assert.Equal(rowId, rc.RowId);
+ Assert.Equal(100, rc.RowId);
Assert.Equal(rs, rc.AssociatedResultSet);
Assert.Equal(etm, rc.AssociatedObjectMetadata);
}
@@ -64,14 +67,12 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public async Task ApplyChanges()
{
// Setup: Generate the parameters for the row delete object
- // We don't care about the values besides the row ID
- const long rowId = 0;
var columns = Common.GetColumns(false);
var rs = Common.GetResultSet(columns, false);
var etm = Common.GetStandardMetadata(columns);
// If: I ask for the change to be applied
- RowDelete rd = new RowDelete(rowId, rs, etm);
+ RowDelete rd = new RowDelete(0, rs, etm);
await rd.ApplyChanges(null); // Reader not used, can be null
// Then : The result set should have one less row in it
@@ -87,11 +88,10 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
{
// Setup:
// ... Create a row delete
- const long rowId = 0;
var columns = Common.GetColumns(includeIdentity);
var rs = Common.GetResultSet(columns, includeIdentity);
var etm = Common.GetStandardMetadata(columns, !includeIdentity, isMemoryOptimized);
- RowDelete rd = new RowDelete(rowId, rs, etm);
+ RowDelete rd = new RowDelete(0, rs, etm);
// ... Mock db connection for building the command
var mockConn = new TestSqlConnection(null);
@@ -131,26 +131,66 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public void GetCommandNullConnection()
{
// Setup: Create a row delete
- var columns = Common.GetColumns(false);
- var rs = Common.GetResultSet(columns, false);
- var etm = Common.GetStandardMetadata(columns);
- RowDelete rd = new RowDelete(0, rs, etm);
+ RowDelete rd = GetStandardRowDelete();
// If: I attempt to create a command with a null connection
// Then: It should throw an exception
Assert.Throws(() => rd.GetCommand(null));
}
+ [Fact]
+ public void GetEditRow()
+ {
+ // Setup: Create a row delete
+ var columns = Common.GetColumns(false);
+ var rs = Common.GetResultSet(columns, false);
+ var etm = Common.GetStandardMetadata(columns);
+ RowDelete rd = new RowDelete(0, rs, etm);
+
+ // If: I attempt to get an edit row
+ DbCellValue[] cells = rs.GetRow(0).ToArray();
+ EditRow er = rd.GetEditRow(cells);
+
+ // Then:
+ // ... The state should be dirty
+ Assert.True(er.IsDirty);
+ Assert.Equal(EditRow.EditRowState.DirtyDelete, er.State);
+
+ // ... The ID should be the same as the one provided
+ Assert.Equal(0, er.Id);
+
+ // ... The row should match the cells that were given
+ Assert.Equal(cells.Length, er.Cells.Length);
+ for (int i = 0; i < cells.Length; i++)
+ {
+ DbCellValue originalCell = cells[i];
+ DbCellValue outputCell = er.Cells[i];
+
+ Assert.Equal(originalCell.DisplayValue, outputCell.DisplayValue);
+ Assert.Equal(originalCell.IsNull, outputCell.IsNull);
+ // Note: No real need to check the RawObject property
+ }
+ }
+
+ [Fact]
+ public void GetEditNullRow()
+ {
+ // Setup: Create a row delete
+ RowDelete rd = GetStandardRowDelete();
+
+ // If: I attempt to get an edit row with a null cached row
+ // Then: I should get an exception
+ Assert.Throws(() => rd.GetEditRow(null));
+ }
+
[Fact]
public void SetCell()
{
- DbColumn[] columns = Common.GetColumns(true);
- ResultSet rs = Common.GetResultSet(columns, true);
- IEditTableMetadata etm = Common.GetStandardMetadata(columns, false);
+ // Setup: Create a row delete
+ RowDelete rd = GetStandardRowDelete();
// If: I set a cell on a delete row edit
// Then: It should throw as invalid operation
- RowDelete rd = new RowDelete(0, rs, etm);
Assert.Throws(() => rd.SetCell(0, null));
}
@@ -158,14 +198,19 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public void RevertCell()
{
// Setup: Create a row delete
- DbColumn[] cols = Common.GetColumns(false);
- ResultSet rs = Common.GetResultSet(cols, false);
- IEditTableMetadata etm = Common.GetStandardMetadata(cols);
- RowDelete rd = new RowDelete(0, rs, etm);
+ RowDelete rd = GetStandardRowDelete();
// If: I revert a cell on a delete row edit
// Then: It should throw
Assert.Throws(() => rd.RevertCell(0));
}
+
+ private RowDelete GetStandardRowDelete()
+ {
+ var cols = Common.GetColumns(false);
+ var rs = Common.GetResultSet(cols, false);
+ var etm = Common.GetStandardMetadata(cols);
+ return new RowDelete(0, rs, etm);
+ }
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs
index 19f98728..1da82c9c 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowEditBaseTests.cs
@@ -14,6 +14,7 @@ using Microsoft.SqlTools.ServiceLayer.EditData;
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Xunit;
@@ -267,6 +268,11 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
throw new NotImplementedException();
}
+ public override EditRow GetEditRow(DbCellValue[] cells)
+ {
+ throw new NotImplementedException();
+ }
+
public override string RevertCell(int columnId)
{
throw new NotImplementedException();
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs
index 2fe050d7..5a0ed1c2 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/RowUpdateTests.cs
@@ -5,6 +5,7 @@
using System;
using System.Data.Common;
+using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -12,6 +13,7 @@ using Microsoft.SqlTools.ServiceLayer.EditData;
using Microsoft.SqlTools.ServiceLayer.EditData.Contracts;
using Microsoft.SqlTools.ServiceLayer.EditData.UpdateManagement;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
using Xunit;
@@ -41,10 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
public void SetCell()
{
// Setup: Create a row update
- var columns = Common.GetColumns(false);
- var rs = Common.GetResultSet(columns, false);
- var etm = Common.GetStandardMetadata(columns);
- RowUpdate ru = new RowUpdate(0, rs, etm);
+ RowUpdate ru = GetStandardRowUpdate();
// If: I set a cell that can be updated
EditUpdateCellResult eucr = ru.SetCell(0, "col1");
@@ -234,15 +233,63 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
[Fact]
public void GetCommandNullConnection()
{
- // Setup: Create a row create
- var columns = Common.GetColumns(false);
- var rs = Common.GetResultSet(columns, false);
- var etm = Common.GetStandardMetadata(columns);
- RowUpdate rc = new RowUpdate(0, rs, etm);
+ // Setup: Create a row update
+ RowUpdate ru = GetStandardRowUpdate();
// If: I attempt to create a command with a null connection
// Then: It should throw an exception
- Assert.Throws(() => rc.GetCommand(null));
+ Assert.Throws(() => ru.GetCommand(null));
+ }
+
+ [Fact]
+ public void GetEditRow()
+ {
+ // Setup: Create a row update with a cell set
+ var columns = Common.GetColumns(false);
+ var rs = Common.GetResultSet(columns, false);
+ var etm = Common.GetStandardMetadata(columns);
+ RowUpdate ru = new RowUpdate(0, rs, etm);
+ ru.SetCell(0, "foo");
+
+ // If: I attempt to get an edit row
+ DbCellValue[] cells = rs.GetRow(0).ToArray();
+ EditRow er = ru.GetEditRow(cells);
+
+ // Then:
+ // ... The state should be dirty
+ Assert.True(er.IsDirty);
+ Assert.Equal(EditRow.EditRowState.DirtyUpdate, er.State);
+
+ // ... The ID should be the same as the one provided
+ Assert.Equal(0, er.Id);
+
+ // ... The row should match the cells that were given, except for the updated cell
+ Assert.Equal(cells.Length, er.Cells.Length);
+ for (int i = 1; i < cells.Length; i++)
+ {
+ DbCellValue originalCell = cells[i];
+ DbCellValue outputCell = er.Cells[i];
+
+ Assert.Equal(originalCell.DisplayValue, outputCell.DisplayValue);
+ Assert.Equal(originalCell.IsNull, outputCell.IsNull);
+ // Note: No real need to check the RawObject property
+ }
+
+ // ... The updated cell should match what it was set to
+ DbCellValue newCell = er.Cells[0];
+ Assert.Equal(newCell.DisplayValue, "foo");
+ Assert.Equal(newCell.IsNull, false);
+ }
+
+ [Fact]
+ public void GetEditNullRow()
+ {
+ // Setup: Create a row update
+ RowUpdate ru = GetStandardRowUpdate();
+
+ // If: I attempt to get an edit row with a null cached row
+ // Then: I should get an exception
+ Assert.Throws(() => ru.GetEditRow(null));
}
[Theory]
@@ -344,5 +391,13 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
// ... The cell should no longer be set
Assert.DoesNotContain(0, ru.cellUpdates.Keys);
}
+
+ private RowUpdate GetStandardRowUpdate()
+ {
+ var columns = Common.GetColumns(false);
+ var rs = Common.GetResultSet(columns, false);
+ var etm = Common.GetStandardMetadata(columns);
+ return new RowUpdate(0, rs, etm);
+ }
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs
index f9674c25..e83221ba 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/ServiceIntegrationTests.cs
@@ -64,6 +64,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
efv.Validate();
}
+
+
#endregion
#region Dispose Tests
@@ -215,6 +217,36 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
edit.Verify(e => e.SetCell(It.IsAny(), It.IsAny()), Times.Once);
}
+ [Fact]
+ public async Task GetRowsSuccess()
+ {
+ // Setup: Create an edit data service with a session
+ // Setup: Create an edit data service with a session
+ var eds = new EditDataService(null, null, null);
+ var session = GetDefaultSession();
+ eds.ActiveSessions[Constants.OwnerUri] = session;
+
+ // If: I validly ask for rows
+ var efv = new EventFlowValidator()
+ .AddResultValidation(esr =>
+ {
+ Assert.NotNull(esr);
+ Assert.NotEmpty(esr.Subset);
+ Assert.NotEqual(0, esr.RowCount);
+ })
+ .Complete();
+ await eds.HandleSubsetRequest(new EditSubsetParams
+ {
+ OwnerUri = Constants.OwnerUri,
+ RowCount = 10,
+ RowStartIndex = 0
+ }, efv.Object);
+
+ // Then:
+ // ... It should be successful
+ efv.Validate();
+ }
+
[Theory]
[InlineData(null, "table", "table")] // Null owner URI
[InlineData(Common.OwnerUri, null, "table")] // Null object name
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
index fb2092cf..59ed75f8 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/EditData/SessionTests.cs
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.EditData;
@@ -392,6 +393,172 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.EditData
#endregion
+ #region SubSet Tests
+
+ [Fact]
+ public async Task GetRowsNoEdits()
+ {
+ // Setup: Create a session with a proper query and metadata
+ Query q = QueryExecution.Common.GetBasicExecutedQuery();
+ ResultSet rs = q.Batches[0].ResultSets[0];
+ IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
+ EditSession s = new EditSession(rs, etm);
+
+ // If: I ask for 3 rows from session skipping the first
+ EditRow[] rows = await s.GetRows(1, 3);
+
+ // Then:
+ // ... I should get back 3 rows
+ Assert.Equal(3, rows.Length);
+
+ // ... Each row should...
+ for (int i = 0; i < rows.Length; i++)
+ {
+ EditRow er = rows[i];
+
+ // ... Have properly set IDs
+ Assert.Equal(i + 1, er.Id);
+
+ // ... Have cells equal to the cells in the result set
+ DbCellValue[] cachedRow = rs.GetRow(i + 1).ToArray();
+ Assert.Equal(cachedRow.Length, er.Cells.Length);
+ for (int j = 0; j < cachedRow.Length; j++)
+ {
+ Assert.Equal(cachedRow[j].DisplayValue, er.Cells[j].DisplayValue);
+ Assert.Equal(cachedRow[j].IsNull, er.Cells[j].IsNull);
+ }
+
+ // ... Be clean, since we didn't apply any updates
+ Assert.Equal(EditRow.EditRowState.Clean, er.State);
+ Assert.False(er.IsDirty);
+ }
+ }
+
+ [Fact]
+ public async Task GetRowsPendingUpdate()
+ {
+ // Setup:
+ // ... Create a session with a proper query and metadata
+ Query q = QueryExecution.Common.GetBasicExecutedQuery();
+ ResultSet rs = q.Batches[0].ResultSets[0];
+ IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
+ EditSession s = new EditSession(rs, etm);
+
+ // ... Add a cell update to it
+ s.UpdateCell(1, 0, "foo");
+
+ // If: I ask for 3 rows from the session, skipping the first, including the updated one
+ EditRow[] rows = await s.GetRows(1, 3);
+
+ // Then:
+ // ... I should get back 3 rows
+ Assert.Equal(3, rows.Length);
+
+ // ... The first row should reflect that there is an update pending
+ // (More in depth testing is done in the RowUpdate class tests)
+ var updatedRow = rows[0];
+ Assert.Equal(EditRow.EditRowState.DirtyUpdate, updatedRow.State);
+ Assert.Equal("foo", updatedRow.Cells[0].DisplayValue);
+
+ // ... The other rows should be clean
+ for (int i = 1; i < rows.Length; i++)
+ {
+ Assert.Equal(EditRow.EditRowState.Clean, rows[i].State);
+ }
+ }
+
+ [Fact]
+ public async Task GetRowsPendingDeletion()
+ {
+ // Setup:
+ // ... Create a session with a proper query and metadata
+ Query q = QueryExecution.Common.GetBasicExecutedQuery();
+ ResultSet rs = q.Batches[0].ResultSets[0];
+ IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
+ EditSession s = new EditSession(rs, etm);
+
+ // ... Add a row deletion
+ s.DeleteRow(1);
+
+ // If: I ask for 3 rows from the session, skipping the first, including the updated one
+ EditRow[] rows = await s.GetRows(1, 3);
+
+ // Then:
+ // ... I should get back 3 rows
+ Assert.Equal(3, rows.Length);
+
+ // ... The first row should reflect that there is an update pending
+ // (More in depth testing is done in the RowUpdate class tests)
+ var updatedRow = rows[0];
+ Assert.Equal(EditRow.EditRowState.DirtyDelete, updatedRow.State);
+ Assert.NotEmpty(updatedRow.Cells[0].DisplayValue);
+
+ // ... The other rows should be clean
+ for (int i = 1; i < rows.Length; i++)
+ {
+ Assert.Equal(EditRow.EditRowState.Clean, rows[i].State);
+ }
+ }
+
+ [Fact]
+ public async Task GetRowsPendingInsertion()
+ {
+ // Setup:
+ // ... Create a session with a proper query and metadata
+ Query q = QueryExecution.Common.GetBasicExecutedQuery();
+ ResultSet rs = q.Batches[0].ResultSets[0];
+ IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
+ EditSession s = new EditSession(rs, etm);
+
+ // ... Add a row creation
+ s.CreateRow();
+
+ // If: I ask for the rows including the new rows
+ EditRow[] rows = await s.GetRows(0, 6);
+
+ // Then:
+ // ... I should get back 6 rows
+ Assert.Equal(6, rows.Length);
+
+ // ... The last row should reflect that there's a new row
+ var updatedRow = rows[5];
+ Assert.Equal(EditRow.EditRowState.DirtyInsert, updatedRow.State);
+
+ // ... The other rows should be clean
+ for (int i = 0; i < rows.Length - 1; i++)
+ {
+ Assert.Equal(EditRow.EditRowState.Clean, rows[i].State);
+ }
+ }
+
+ [Fact]
+ public async Task GetRowsAllNew()
+ {
+ // Setup:
+ // ... Create a session with a query and metadata
+ Query q = QueryExecution.Common.GetBasicExecutedQuery();
+ ResultSet rs = q.Batches[0].ResultSets[0];
+ IEditTableMetadata etm = Common.GetStandardMetadata(rs.Columns);
+ EditSession s = new EditSession(rs, etm);
+
+ // ... Add a few row creations
+ s.CreateRow();
+ s.CreateRow();
+ s.CreateRow();
+
+ // If: I ask for the rows included the new rows
+ EditRow[] rows = await s.GetRows(5, 5);
+
+ // Then:
+ // ... I should get back 3 rows back
+ Assert.Equal(3, rows.Length);
+
+ // ... All the rows should be new
+ Assert.All(rows, r => Assert.Equal(EditRow.EditRowState.DirtyInsert, r.State));
+ }
+
+ #endregion
+
#region Script Edits Tests
[Theory]
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs
index 60929b49..b01792f2 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/QueryExecution/SubsetTests.cs
@@ -142,8 +142,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution
var subsetRequest = new EventFlowValidator()
.AddResultValidation(r =>
{
- // Then: Messages should be null and subset should not be null
- Assert.Null(r.Message);
+ // Then: Subset should not be null
Assert.NotNull(r.ResultSubset);
}).Complete();
await queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object);
@@ -159,12 +158,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution
var queryService = Common.GetPrimedExecutionService(null, true, false, workspaceService);
var subsetParams = new SubsetParams { OwnerUri = Constants.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
var subsetRequest = new EventFlowValidator()
- .AddResultValidation(r =>
- {
- // Then: Messages should not be null and the subset should be null
- Assert.NotNull(r.Message);
- Assert.Null(r.ResultSubset);
- }).Complete();
+ .AddErrorValidation(Assert.NotEmpty)
+ .Complete();
await queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object);
subsetRequest.Validate();
}
@@ -185,12 +180,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution
// ... And I then ask for a valid set of results from it
var subsetParams = new SubsetParams { OwnerUri = Constants.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
var subsetRequest = new EventFlowValidator()
- .AddResultValidation(r =>
- {
- // Then: There should not be a subset and message should not be null
- Assert.NotNull(r.Message);
- Assert.Null(r.ResultSubset);
- }).Complete();
+ .AddErrorValidation(Assert.NotEmpty)
+ .Complete();
await queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object);
subsetRequest.Validate();
}
@@ -210,12 +201,8 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution
// ... And I then ask for a set of results from it
var subsetParams = new SubsetParams { OwnerUri = Constants.OwnerUri, RowsCount = 1, ResultSetIndex = 0, RowsStartIndex = 0 };
var subsetRequest = new EventFlowValidator()
- .AddResultValidation(r =>
- {
- // Then: There should be an error message and no subset
- Assert.NotNull(r.Message);
- Assert.Null(r.ResultSubset);
- }).Complete();
+ .AddErrorValidation(Assert.NotEmpty)
+ .Complete();
await queryService.HandleResultSubsetRequest(subsetParams, subsetRequest.Object);
subsetRequest.Validate();
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/LongListTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/LongListTests.cs
index 5fe0a89d..e165507f 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/LongListTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Utility/LongListTests.cs
@@ -3,7 +3,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-using Microsoft.SqlTools.Utility;
+using System;
+using System.Linq;
+using Microsoft.SqlTools.ServiceLayer.Utility;
using Xunit;
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
@@ -13,27 +15,368 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Utility
///
public class LongListTests
{
- ///
- /// Add and remove and item in a LongList
- ///
[Fact]
- public void LongListTest()
+ public void LongListConstruction()
{
- var longList = new LongList();
- longList.Add('.');
- Assert.True(longList.Count == 1);
- longList.RemoveAt(0);
- Assert.True(longList.Count == 0);
+ // If: I construct a new long list
+ LongList ll = new LongList();
+
+ // Then:
+ // ... There should be no values in the list
+ Assert.Equal(0, ll.Count);
}
+ #region GetItem / Add Tests
+
+ [Theory]
+ [InlineData(-1L)] // Negative index
+ [InlineData(0L)] // Index equal to count of elements
+ [InlineData(100L)] // Index larger than elements
+ public void GetItemOutOfRange(long index)
+ {
+ // If: I construct a new long list
+ LongList ll = new LongList();
+
+ // Then:
+ // ... There should be no values in the list
+ Assert.Throws(() => ll[index]);
+ Assert.Throws(() => ll.GetItem(index));
+ }
+
+ [Theory]
+ [InlineData(0)] // Element at beginning
+ [InlineData(1)] // Element in middle
+ [InlineData(2)] // Element at end
+ public void GetItemNotExpanded(long index)
+ {
+ // If: I construct a new long list with a couple items in it
+ LongList ll = new LongList {0, 1, 2};
+
+ // Then: I can read back the value from the list
+ Assert.Equal(3, ll.Count);
+ Assert.Equal(index, ll[index]);
+ Assert.Equal(index, ll.GetItem(index));
+ }
+
+ [Fact]
+ public void GetItemExanded()
+ {
+ // If: I construct a new long list that is guaranteed to have been expanded
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int i = 0; i < 10; i++)
+ {
+ ll.Add(i);
+ }
+
+ // Then:
+ // ... All the added values should be accessible
+ Assert.Equal(10, ll.Count);
+ for (int i = 0; i < 10; i++)
+ {
+ Assert.Equal(i, ll[i]);
+ Assert.Equal(i, ll.GetItem(i));
+ }
+ }
+
+ #endregion
+
+ #region SetItem Tests
+
+ [Theory]
+ [InlineData(-1L)] // Negative index
+ [InlineData(0L)] // Index equal to count of elements
+ [InlineData(100L)] // Index larger than elements
+ public void SetItemOutOfRange(long index)
+ {
+ // If: I construct a new long list
+ LongList ll = new LongList();
+
+ // Then:
+ // ... There should be no values in the list
+ Assert.Throws(() => ll[index] = 8);
+ Assert.Throws(() => ll.SetItem(index, 8));
+ }
+
+ [Fact]
+ public void SetItemNotExpanded()
+ {
+ // If:
+ // ... I construct a new long list with a few items in it
+ // ... And I set all values to new values
+ LongList ll = new LongList {0, 1, 2};
+ for (int i = 0; i < ll.Count; i++)
+ {
+ ll.SetItem(i, 8);
+ }
+
+ // Then: All values in the list should be 8
+ Assert.All(ll, i => Assert.Equal(8, i));
+ }
+
+ [Fact]
+ public void SetItemIndexerNotExpanded()
+ {
+ // If:
+ // ... I construct a new long list with a few items in it
+ // ... And I set all values to new values
+ LongList ll = new LongList {0, 1, 2};
+ for (int i = 0; i < ll.Count; i++)
+ {
+ ll[i] = 8;
+ }
+
+ // Then: All values in the list should be 8
+ Assert.All(ll, i => Assert.Equal(8, i));
+ }
+
+ [Fact]
+ public void SetItemExpanded()
+ {
+ // If:
+ // ... I construct a new long list that is guaranteed to have been expanded
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int i = 0; i < 10; i++)
+ {
+ ll.Add(i);
+ }
+
+ // ... And reset all the values to 8
+ for (int i = 0; i < 10; i++)
+ {
+ ll.SetItem(i, 8);
+ }
+
+ // Then: All values in the list should be 8
+ Assert.All(ll, i => Assert.Equal(8, i));
+ }
+
+ [Fact]
+ public void SetItemIndexerExpanded()
+ {
+ // If:
+ // ... I construct a new long list that is guaranteed to have been expanded
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int i = 0; i < 10; i++)
+ {
+ ll.Add(i);
+ }
+
+ // ... And reset all the values to 8
+ for (int i = 0; i < 10; i++)
+ {
+ ll[i] = 8;
+ }
+
+ // Then: All values in the list should be 8
+ Assert.All(ll, i => Assert.Equal(8, i));
+ }
+
+ #endregion
+
+ #region RemoveAt Tests
+
+ [Theory]
+ [InlineData(-1L)] // Negative index
+ [InlineData(0L)] // Index equal to count of elements
+ [InlineData(100L)] // Index larger than elements
+ public void RemoveOutOfRange(long index)
+ {
+ // If: I construct a new long list
+ LongList ll = new LongList();
+
+ // Then:
+ // ... There should be no values in the list
+ Assert.Throws(() => ll.RemoveAt(index));
+ }
+
+ [Theory]
+ [InlineData(0)] // Remove at beginning of list
+ [InlineData(2)] // Remove from middle of list
+ [InlineData(4)] // Remove at end of list
+ public void RemoveAtNotExpanded(long index)
+ {
+ // If:
+ // ... I create a long list with a few elements in it (and one element that will be removed)
+ LongList ll = new LongList();
+ for (int i = 0; i < 5; i++)
+ {
+ ll.Add(i == index ? 1 : 8);
+ }
+
+ // ... And I delete an element
+ ll.RemoveAt(index);
+
+ // Then:
+ // ... The count should have subtracted
+ Assert.Equal(4, ll.Count);
+
+ // ... All values should be 8 since we removed the 1
+ Assert.All(ll, i => Assert.Equal(8, i));
+ }
+
+ [Fact]
+ public void RemoveAtExpanded()
+ {
+ // If:
+ // ... I create a long list that is guaranteed to be expanded
+ // (Created with 2x the values, evaluate the )
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int j = 0; j < 2; j++)
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ ll.Add(i);
+ }
+ }
+
+ // ... And I delete all of the first half of values
+ // (we're doing this backwards to make sure remove works at different points in the list)
+ for (int i = 9; i >= 0; i--)
+ {
+ ll.RemoveAt(i);
+ }
+
+ // Then:
+ // ... The second half of the values should still remain
+ for (int i = 0; i < 10; i++)
+ {
+ Assert.Equal(i, ll[i]);
+ }
+
+ // If:
+ // ... I then proceed to add elements onto the end again
+ for (int i = 0; i < 10; i++)
+ {
+ ll.Add(i);
+ }
+
+ // Then: All the elements should be there, in order
+ for (int j = 0; j < 2; j++)
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ int index = j * 10 + i;
+ Assert.Equal(i, ll[index]);
+ }
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Tests
+
+ [Fact]
+ public void GetEnumerator()
+ {
+ // Setup: Create a long list with a handful of elements
+ LongList ll = new LongList();
+ for (int i = 0; i < 5; i++)
+ {
+ ll.Add(i);
+ }
+
+ // If: I get iterate over the list via GetEnumerator
+ // Then: All the elements should be returned, in order
+ int val = 0;
+ foreach (int element in ll)
+ {
+ Assert.Equal(val++, element);
+ }
+ }
+
+ [Fact]
+ public void GetEnumeratorExpanded()
+ {
+ // Setup: Create a long list with a handful of elements
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int i = 0; i < 5; i++)
+ {
+ ll.Add(i);
+ }
+
+ // If: I get iterate over the list via GetEnumerator
+ // Then: All the elements should be returned, in order
+ int val = 0;
+ foreach (int element in ll)
+ {
+ Assert.Equal(val++, element);
+ }
+ }
+
+ [Theory]
+ [InlineData(-1)] // Negative
+ [InlineData(5)] // Equal to count
+ [InlineData(100)] // Far too large
+ public void LongSkipOutOfRange(long index)
+ {
+ // Setup: Create a long list with a handful of elements
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int i = 0; i < 5; i++)
+ {
+ ll.Add(i);
+ }
+
+ // If: I attempt to skip ahead by a value that is out of range
+ // Then: I should get an exception
+ // NOTE: We must do the .ToList in order to evaluate the LongSkip since it is implemented
+ // with a yield return
+ Assert.Throws(() => ll.LongSkip(index).ToArray());
+ }
+
+ [Theory]
+ [InlineData(0)] // Don't actually skip anything
+ [InlineData(2)] // Skip within the short list
+ public void LongSkip(long index)
+ {
+ // Setup: Create a long list with a handful of elements
+ LongList ll = new LongList();
+ for (int i = 0; i < 5; i++)
+ {
+ ll.Add(i);
+ }
+
+ // If: I skip ahead by a few elements and get all elements in an array
+ int[] values = ll.LongSkip(index).ToArray();
+
+ // Then: The elements including the skip start index should be in the output
+ for (int i = 0; i < values.Length; i++)
+ {
+ Assert.Equal(ll[i+index], values[i]);
+ }
+ }
+
+ [Theory]
+ [InlineData(0)] // Don't actually skip anything
+ [InlineData(1)] // Skip within the short list
+ [InlineData(3)] // Skip across expanded lists
+ public void LongSkipExpanded(long index)
+ {
+ // Setup: Create a long list with a handful of elements
+ LongList ll = new LongList {ExpandListSize = 2};
+ for (int i = 0; i < 5; i++)
+ {
+ ll.Add(i);
+ }
+
+ // If: I skip ahead by a few elements and get all elements in an array
+ int[] values = ll.LongSkip(index).ToArray();
+
+ // Then: The elements including the skip start index should be in the output
+ for (int i = 0; i < values.Length; i++)
+ {
+ Assert.Equal(ll[i+index], values[i]);
+ }
+ }
+
+ #endregion
+
///
/// Add and remove and item in a LongList causing an expansion
///
[Fact]
public void LongListExpandTest()
{
- var longList = new LongList();
- longList.ExpandListSize = 3;
+ var longList = new LongList {ExpandListSize = 3};
for (int i = 0; i < 6; ++i)
{
longList.Add(i);