Query Runner Tests (#10252)

* rework some code and write an inital test

* fix strict

* add more to standard test

* add to existing workflow test

* fix tests

* simplify the code

* add more tests

* remove bad import

* fix compile

* fix timestampiong
This commit is contained in:
Anthony Dresser
2020-05-06 13:38:12 -07:00
committed by GitHub
parent 4199cec393
commit df5df38a55
25 changed files with 856 additions and 430 deletions

View File

@@ -7,6 +7,7 @@ import 'vs/css!./media/gridPanel';
import { attachTableStyler } from 'sql/platform/theme/common/styler';
import QueryRunner, { QueryGridDataProvider } from 'sql/workbench/services/query/common/queryRunner';
import { ResultSetSummary, IColumn } from 'sql/workbench/services/query/common/query';
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
import { Table } from 'sql/base/browser/ui/table/table';
import { ScrollableSplitView, IView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview';
@@ -21,8 +22,6 @@ import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugi
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
import * as azdata from 'azdata';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -123,7 +122,7 @@ export class GridPanel extends Disposable {
}
this.reset();
}));
this.addResultSet(this.runner.batchSets.reduce<azdata.ResultSetSummary[]>((p, e) => {
this.addResultSet(this.runner.batchSets.reduce<ResultSetSummary[]>((p, e) => {
if (this.configurationService.getValue<boolean>('sql.results.streaming')) {
p = p.concat(e.resultSetSummaries);
} else {
@@ -141,8 +140,8 @@ export class GridPanel extends Disposable {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
private onResultSet(resultSet: azdata.ResultSetSummary | azdata.ResultSetSummary[]) {
let resultsToAdd: azdata.ResultSetSummary[];
private onResultSet(resultSet: ResultSetSummary | ResultSetSummary[]) {
let resultsToAdd: ResultSetSummary[];
if (!Array.isArray(resultSet)) {
resultsToAdd = [resultSet];
} else {
@@ -170,8 +169,8 @@ export class GridPanel extends Disposable {
}
}
private updateResultSet(resultSet: azdata.ResultSetSummary | azdata.ResultSetSummary[]) {
let resultsToUpdate: azdata.ResultSetSummary[];
private updateResultSet(resultSet: ResultSetSummary | ResultSetSummary[]) {
let resultsToUpdate: ResultSetSummary[];
if (!Array.isArray(resultSet)) {
resultsToUpdate = [resultSet];
} else {
@@ -203,7 +202,7 @@ export class GridPanel extends Disposable {
}
}
private addResultSet(resultSet: azdata.ResultSetSummary[]) {
private addResultSet(resultSet: ResultSetSummary[]) {
let tables: GridTable<any>[] = [];
for (let set of resultSet) {
@@ -316,7 +315,7 @@ export class GridPanel extends Disposable {
export interface IDataSet {
rowCount: number;
columnInfo: azdata.IDbColumn[];
columnInfo: IColumn[];
}
export abstract class GridTableBase<T> extends Disposable implements IView {
@@ -363,7 +362,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
constructor(
state: GridTableState,
protected _resultSet: azdata.ResultSetSummary,
protected _resultSet: ResultSetSummary,
protected contextMenuService: IContextMenuService,
protected instantiationService: IInstantiationService,
protected editorService: IEditorService,
@@ -394,7 +393,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
abstract get gridDataProvider(): IGridDataProvider;
public get resultSet(): azdata.ResultSetSummary {
public get resultSet(): ResultSetSummary {
return this._resultSet;
}
@@ -587,7 +586,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
// handle if a showplan link was clicked
if (column && (column.isXml || column.isJson)) {
this.gridDataProvider.getRowData(event.cell.row, 1).then(async d => {
let value = d.resultSubset.rows[0][event.cell.cell - 1];
let value = d.rows[0][event.cell.cell - 1];
let content = value.displayValue;
const input = this.untitledEditorService.create({ mode: column.isXml ? 'xml' : 'json', initialValue: content });
@@ -598,7 +597,7 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
}
}
public updateResult(resultSet: azdata.ResultSetSummary) {
public updateResult(resultSet: ResultSetSummary) {
this._resultSet = resultSet;
if (this.table && this.visible) {
this.dataProvider.length = resultSet.rowCount;
@@ -655,10 +654,10 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
private loadData(offset: number, count: number): Thenable<T[]> {
return this.gridDataProvider.getRowData(offset, count).then(response => {
if (!response.resultSubset) {
if (!response) {
return [];
}
return response.resultSubset.rows.map(r => {
return response.rows.map(r => {
let dataWithSchema = {};
// skip the first column since its a number column
for (let i = 1; i < this.columns.length; i++) {
@@ -756,7 +755,7 @@ class GridTable<T> extends GridTableBase<T> {
private _gridDataProvider: IGridDataProvider;
constructor(
private _runner: QueryRunner,
resultSet: azdata.ResultSetSummary,
resultSet: ResultSetSummary,
state: GridTableState,
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService instantiationService: IInstantiationService,

View File

@@ -4,9 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/messagePanel';
import QueryRunner, { IQueryMessage } from 'sql/workbench/services/query/common/queryRunner';
import { ISelectionData } from 'azdata';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
import { IQueryMessage } from 'sql/workbench/services/query/common/query';
import { ITreeRenderer, IDataSource, ITreeNode, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { generateUuid } from 'vs/base/common/uuid';
@@ -32,6 +31,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { QueryEditor } from 'sql/workbench/contrib/query/browser/queryEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree';
import { IRange } from 'vs/editor/common/core/range';
export interface IResultMessageIntern {
id?: string;
@@ -39,8 +39,7 @@ export interface IResultMessageIntern {
isError: boolean;
time?: string | Date;
message: string;
selection?: ISelectionData;
range?: IRange;
}
export interface IMessagePanelMessage {
@@ -49,7 +48,7 @@ export interface IMessagePanelMessage {
}
export interface IMessagePanelBatchMessage extends IMessagePanelMessage {
selection: ISelectionData;
range: IRange;
time: string;
}
@@ -267,7 +266,7 @@ class MessagePanelDelegate extends CachedListVirtualDelegate<IResultMessageInter
getTemplateId(element: IResultMessageIntern): string {
if (element instanceof Model) {
return TemplateIds.MODEL;
} else if (element.selection) {
} else if (element.range) {
return TemplateIds.BATCH;
} else if (element.isError) {
return TemplateIds.ERROR;
@@ -322,14 +321,13 @@ class BatchMessageRenderer implements ITreeRenderer<IResultMessageIntern, void,
}
templateData.timeStamp.innerText = (node.element.time as Date).toLocaleTimeString();
templateData.message.innerText = node.element.message;
if (node.element.selection) {
const selection = { endColumn: node.element.selection.endColumn + 1, endLineNumber: node.element.selection.endLine + 1, startColumn: node.element.selection.startColumn + 1, startLineNumber: node.element.selection.startLine + 1 };
if (node.element.range) {
templateData.disposable.add(addStandardDisposableGenericMouseDownListner(templateData.message, () => {
let editor = this.editorService.activeEditorPane as QueryEditor;
const codeEditor = <ICodeEditor>editor.getControl();
codeEditor.focus();
codeEditor.setSelection(selection);
codeEditor.revealRangeInCenterIfOutsideViewport(selection);
codeEditor.setSelection(node.element.range);
codeEditor.revealRangeInCenterIfOutsideViewport(node.element.range);
}));
}
}

View File

@@ -14,7 +14,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import Severity from 'vs/base/common/severity';
import { append, $ } from 'vs/base/browser/dom';
import { ISelectionData, QueryExecutionOptions } from 'azdata';
import { QueryExecutionOptions } from 'azdata';
import {
IConnectionManagementService,
IConnectionParams,
@@ -43,6 +43,7 @@ import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilit
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IQueryManagementService } from 'sql/workbench/services/query/common/queryManagement';
import { ILogService } from 'vs/platform/log/common/log';
import { IRange } from 'vs/editor/common/core/range';
/**
* Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -101,12 +102,12 @@ export abstract class QueryTaskbarAction extends Action {
* Connects the given editor to it's current URI.
* Public for testing only.
*/
protected connectEditor(editor: QueryEditor, runQueryOnCompletion?: RunQueryOnConnectionMode, selection?: ISelectionData): void {
protected connectEditor(editor: QueryEditor, runQueryOnCompletion?: RunQueryOnConnectionMode, range?: IRange): void {
let params: INewConnectionParams = {
input: editor.input,
connectionType: ConnectionType.editor,
runQueryOnCompletion: runQueryOnCompletion ? runQueryOnCompletion : RunQueryOnConnectionMode.none,
querySelection: selection
queryRange: range
};
this.connectionManagementService.showConnectionDialog(params);
}
@@ -241,8 +242,8 @@ export class RunQueryAction extends QueryTaskbarAction {
if (this.isConnected(editor)) {
// if the selection isn't empty then execute the selection
// otherwise, either run the statement or the script depending on parameter
let selection: ISelectionData = editor.getSelection(false);
if (runCurrentStatement && selection && this.isCursorPosition(selection)) {
let selection = editor.getSelection();
if (runCurrentStatement && selection) {
editor.input.runQueryStatement(selection);
} else {
// get the selection again this time with trimming
@@ -251,11 +252,6 @@ export class RunQueryAction extends QueryTaskbarAction {
}
}
}
protected isCursorPosition(selection: ISelectionData) {
return selection.startLine === selection.endLine
&& selection.startColumn === selection.endColumn;
}
}
/**

View File

@@ -23,7 +23,6 @@ import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor
import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview';
import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ISelectionData } from 'azdata';
import { IActionViewItem, IAction } from 'vs/base/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
@@ -36,6 +35,7 @@ import { QueryResultsEditor } from 'sql/workbench/contrib/query/browser/queryRes
import * as queryContext from 'sql/workbench/contrib/query/common/queryContext';
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import * as actions from 'sql/workbench/contrib/query/browser/queryActions';
import { IRange } from 'vs/editor/common/core/range';
const QUERY_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'queryEditorViewState';
@@ -499,7 +499,7 @@ export class QueryEditor extends BaseEditor {
* Returns the underlying SQL editor's text selection in a 0-indexed format. Returns undefined if there
* is no selected text.
*/
public getSelection(checkIfRange: boolean = true): ISelectionData {
public getSelection(checkIfRange: boolean = true): IRange {
if (this.currentTextEditor && this.currentTextEditor.getControl()) {
let vscodeSelection = this.currentTextEditor.getControl().getSelection();
@@ -508,13 +508,7 @@ export class QueryEditor extends BaseEditor {
!(vscodeSelection.getStartPosition().lineNumber === vscodeSelection.getEndPosition().lineNumber &&
vscodeSelection.getStartPosition().column === vscodeSelection.getEndPosition().column);
if (!checkIfRange || isRange) {
let sqlToolsServiceSelection: ISelectionData = {
startLine: vscodeSelection.getStartPosition().lineNumber - 1,
startColumn: vscodeSelection.getStartPosition().column - 1,
endLine: vscodeSelection.getEndPosition().lineNumber - 1,
endColumn: vscodeSelection.getEndPosition().column - 1,
};
return sqlToolsServiceSelection;
return vscodeSelection;
}
}
@@ -522,7 +516,7 @@ export class QueryEditor extends BaseEditor {
return undefined;
}
public getAllSelection(): ISelectionData {
public getAllSelection(): IRange {
if (this.currentTextEditor && this.currentTextEditor.getControl()) {
let control = this.currentTextEditor.getControl();
let codeEditor: ICodeEditor = <ICodeEditor>control;
@@ -530,13 +524,12 @@ export class QueryEditor extends BaseEditor {
let model = codeEditor.getModel();
let totalLines = model.getLineCount();
let endColumn = model.getLineMaxColumn(totalLines);
let selection: ISelectionData = {
startLine: 0,
startColumn: 0,
endLine: totalLines - 1,
endColumn: endColumn - 1,
return {
startLineNumber: 1,
startColumn: 1,
endLineNumber: totalLines,
endColumn: endColumn,
};
return selection;
}
}
return undefined;

View File

@@ -5,8 +5,6 @@
import { Emitter, Event } from 'vs/base/common/event';
import { ISelectionData } from 'azdata';
import {
IConnectionParams,
INewConnectionParams,
@@ -34,6 +32,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { IRange } from 'vs/editor/common/core/range';
suite('SQL QueryAction Tests', () => {
@@ -222,9 +221,9 @@ suite('SQL QueryAction Tests', () => {
let countCalledShowDialog: number = 0;
let countCalledRunQuery: number = 0;
let showDialogConnectionParams: INewConnectionParams = undefined;
let runQuerySelection: ISelectionData = undefined;
let selectionToReturnInGetSelection: ISelectionData = undefined;
let predefinedSelection: ISelectionData = { startLine: 1, startColumn: 2, endLine: 3, endColumn: 4 };
let runQuerySelection: IRange = undefined;
let selectionToReturnInGetSelection: IRange = undefined;
let predefinedSelection: IRange = { startLineNumber: 1, startColumn: 2, endLineNumber: 3, endColumn: 4 };
// ... Mock "getSelection" in QueryEditor
const workbenchinstantiationService = workbenchInstantiationService();
@@ -234,11 +233,11 @@ suite('SQL QueryAction Tests', () => {
let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Loose, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object);
queryInput.setup(x => x.uri).returns(() => testUri);
queryInput.setup(x => x.runQuery(TypeMoq.It.isAny())).callback((selection: ISelectionData) => {
queryInput.setup(x => x.runQuery(TypeMoq.It.isAny())).callback((selection: IRange) => {
runQuerySelection = selection;
countCalledRunQuery++;
});
queryInput.setup(x => x.runQuery(undefined)).callback((selection: ISelectionData) => {
queryInput.setup(x => x.runQuery(undefined)).callback((selection: IRange) => {
runQuerySelection = selection;
countCalledRunQuery++;
});
@@ -277,7 +276,7 @@ suite('SQL QueryAction Tests', () => {
assert.equal(countCalledShowDialog, 1, 'run should call showDialog');
assert.equal(countCalledRunQuery, 0, 'run should not call runQuery');
assert.equal(showDialogConnectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor');
assert.equal(showDialogConnectionParams.querySelection, undefined, 'querySelection should be undefined');
assert.equal(showDialogConnectionParams.queryRange, undefined, 'querySelection should be undefined');
////// If I call run on RunQueryAction while disconnected and with a defined selection
isConnected = false;
@@ -288,11 +287,11 @@ suite('SQL QueryAction Tests', () => {
assert.equal(countCalledShowDialog, 2, 'run should call showDialog again');
assert.equal(countCalledRunQuery, 0, 'run should not call runQuery');
assert.equal(showDialogConnectionParams.connectionType, ConnectionType.editor, 'connectionType should be queryEditor');
assert.notEqual(showDialogConnectionParams.querySelection, undefined, 'There should not be an undefined selection in runQuery');
assert.equal(showDialogConnectionParams.querySelection.startLine, selectionToReturnInGetSelection.startLine, 'startLine should match');
assert.equal(showDialogConnectionParams.querySelection.startColumn, selectionToReturnInGetSelection.startColumn, 'startColumn should match');
assert.equal(showDialogConnectionParams.querySelection.endLine, selectionToReturnInGetSelection.endLine, 'endLine should match');
assert.equal(showDialogConnectionParams.querySelection.endColumn, selectionToReturnInGetSelection.endColumn, 'endColumn should match');
assert.notEqual(showDialogConnectionParams.queryRange, undefined, 'There should not be an undefined selection in runQuery');
assert.equal(showDialogConnectionParams.queryRange.startLineNumber, selectionToReturnInGetSelection.startLineNumber, 'startLine should match');
assert.equal(showDialogConnectionParams.queryRange.startColumn, selectionToReturnInGetSelection.startColumn, 'startColumn should match');
assert.equal(showDialogConnectionParams.queryRange.endLineNumber, selectionToReturnInGetSelection.endLineNumber, 'endLine should match');
assert.equal(showDialogConnectionParams.queryRange.endColumn, selectionToReturnInGetSelection.endColumn, 'endColumn should match');
////// If I call run on RunQueryAction while connected and with an undefined selection
isConnected = true;
@@ -313,9 +312,9 @@ suite('SQL QueryAction Tests', () => {
assert.equal(countCalledShowDialog, 2, 'run should not call showDialog');
assert.equal(countCalledRunQuery, 2, 'run should call runQuery again');
assert.notEqual(runQuerySelection, undefined, 'There should not be an undefined selection in runQuery');
assert.equal(runQuerySelection.startLine, selectionToReturnInGetSelection.startLine, 'startLine should match');
assert.equal(runQuerySelection.startLineNumber, selectionToReturnInGetSelection.startLineNumber, 'startLine should match');
assert.equal(runQuerySelection.startColumn, selectionToReturnInGetSelection.startColumn, 'startColumn should match');
assert.equal(runQuerySelection.endLine, selectionToReturnInGetSelection.endLine, 'endLine should match');
assert.equal(runQuerySelection.endLineNumber, selectionToReturnInGetSelection.endLineNumber, 'endLine should match');
assert.equal(runQuerySelection.endColumn, selectionToReturnInGetSelection.endColumn, 'endColumn should match');
});