Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Add support for coverage markup #139

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"onCommand:go.gopath",
"onCommand:go.test.cursor",
"onCommand:go.test.package",
"onCommand:go.test.file"
"onCommand:go.test.file",
"onCommand:go.test.coverage"
],
"main": "./out/src/goMain",
"contributes": {
Expand Down Expand Up @@ -94,6 +95,11 @@
"command": "go.test.file",
"title": "Go: Run tests in file",
"description": "Runs all unit tests in the current file."
},
{
"command": "go.test.coverage",
"title": "Go: Test coverage in file",
"description": "Displays test coverage in the current file."
}
],
"debuggers": [
Expand Down Expand Up @@ -209,6 +215,11 @@
"default": false,
"description": "[EXPERIMENTAL] Run formatting tool on save."
},
"go.coverOnSave": {
"type": "boolean",
"default": false,
"description": "Run 'go test -coverprofile' on save"
},
"go.testTimeout": {
"type": "string",
"default": "30s",
Expand Down
9 changes: 6 additions & 3 deletions src/goCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import cp = require('child_process');
import path = require('path');
import os = require('os');
import fs = require('fs');
import { getBinPath, getGoRuntimePath } from './goPath'
import { getBinPath, getGoRuntimePath } from './goPath';
import { getCoverage } from './goCover';

if (!getGoRuntimePath()) {
vscode.window.showInformationMessage("No 'go' binary could be found on PATH or in GOROOT.");
Expand All @@ -22,7 +23,7 @@ export interface ICheckResult {
severity: string;
}

export function check(filename: string, buildOnSave = true, lintOnSave = true, vetOnSave = true): Promise<ICheckResult[]> {
export function check(filename: string, buildOnSave = true, lintOnSave = true, vetOnSave = true, coverOnSave = false): Promise<ICheckResult[]> {
var gobuild = !buildOnSave ? Promise.resolve([]) : new Promise((resolve, reject) => {
var tmppath = path.normalize(path.join(os.tmpdir(), "go-code-check"))
var cwd = path.dirname(filename)
Expand Down Expand Up @@ -104,6 +105,8 @@ export function check(filename: string, buildOnSave = true, lintOnSave = true, v
}
});
});

var gocover = !coverOnSave ? Promise.resolve([]) : getCoverage(filename);

return Promise.all([gobuild, golint, govet]).then(resultSets => [].concat.apply([], resultSets));
return Promise.all([gobuild, golint, govet, gocover]).then(resultSets => [].concat.apply([], resultSets));
}
100 changes: 100 additions & 0 deletions src/goCover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/

'use strict';

import vscode = require('vscode');
import cp = require('child_process');
import path = require('path');
import os = require('os');
import fs = require('fs');
import { getBinPath, getGoRuntimePath } from './goPath';
import rl = require('readline');

if (!getGoRuntimePath()) {
vscode.window.showInformationMessage("No 'go' binary could be found on PATH or in GOROOT.");
}

export function coverageCurrentFile() {
var editor = vscode.window.activeTextEditor;
if (!editor) {
vscode.window.showInformationMessage("No editor is active.");
return;
}
getCoverage(editor.document.uri.fsPath);
}

export function getCoverage(filename: string): Promise<any[]> {
return new Promise((resolve, reject) => {
var tmppath = path.normalize(path.join(os.tmpdir(), "go-code-cover"))
var cwd = path.dirname(filename)
var args = ["test", "-coverprofile=" + tmppath];
cp.execFile(getGoRuntimePath(), args, { cwd: cwd }, (err, stdout, stderr) => {
try {
if (err && (<any>err).code == "ENOENT") {
vscode.window.showInformationMessage("Could not generate coverage report. Install Go from http://golang.org/dl/.");
return resolve([]);
}
var ret = [];

var lines = rl.createInterface({
input: fs.createReadStream(tmppath),
output: undefined
});

var coveredRange = [],
uncoveredRange =[],
uncoveredHighLight = vscode.window.createTextEditorDecorationType({
// Red
backgroundColor: 'rgba(128,64,64,0.5)',
isWholeLine: false
}), coveredHighLight = vscode.window.createTextEditorDecorationType({
// Green
backgroundColor: 'rgba(64,128,64,0.5)',
isWholeLine: false
});

lines.on('line', function(data: string) {
// go test coverageprofile generates output:
// filename:StartLine.StartColumn,EndLine.EndColumn Hits IsCovered
var fileRange = data.match(/([^:]+)\:([\d]+)\.([\d]+)\,([\d]+)\.([\d]+)\s([\d]+)\s([\d]+)/);
if (fileRange) {
// If line matches active file
if (filename.endsWith(fileRange[1])) {
var range = {
range: new vscode.Range(
// Start Line converted to zero based
parseInt(fileRange[2]) - 1,
// Start Column converted to zero based
parseInt(fileRange[3]) - 1,
// End Line converted to zero based
parseInt(fileRange[4]) - 1,
// End Column converted to zero based
parseInt(fileRange[5]) - 1
)
};
// If is Covered
if (parseInt(fileRange[7]) === 1) {
coveredRange.push(range);
}
// Not Covered
else {
uncoveredRange.push(range);
}
}
}
});
lines.on('close', function(data) {
// Highlight lines in current editor.
vscode.window.activeTextEditor.setDecorations(uncoveredHighLight, uncoveredRange);
vscode.window.activeTextEditor.setDecorations(coveredHighLight, coveredRange);
resolve(ret);
});
} catch (e) {
vscode.window.showInformationMessage(e.msg);
reject(e);
}
});
});
}
7 changes: 6 additions & 1 deletion src/goMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { check, ICheckResult } from './goCheck';
import { setupGoPathAndOfferToInstallTools } from './goInstallTools'
import { GO_MODE } from './goMode'
import { showHideStatus } from './goStatus'
import { coverageCurrentFile } from './goCover';
import { testAtCursor, testCurrentPackage, testCurrentFile } from './goTest'

let diagnosticCollection: vscode.DiagnosticCollection;
Expand Down Expand Up @@ -59,6 +60,10 @@ export function activate(ctx: vscode.ExtensionContext): void {
let goConfig = vscode.workspace.getConfiguration('go');
testCurrentFile(goConfig['testTimeout']);
}));

ctx.subscriptions.push(vscode.commands.registerCommand("go.test.coverage", () => {
coverageCurrentFile();
}));

vscode.languages.setLanguageConfiguration(GO_MODE.language, {
indentationRules: {
Expand Down Expand Up @@ -121,7 +126,7 @@ function runBuilds(document: vscode.TextDocument, goConfig: vscode.WorkspaceConf
}

var uri = document.uri;
check(uri.fsPath, goConfig['buildOnSave'], goConfig['lintOnSave'], goConfig['vetOnSave']).then(errors => {
check(uri.fsPath, goConfig['buildOnSave'], goConfig['lintOnSave'], goConfig['vetOnSave'], goConfig['coverOnSave']).then(errors => {
diagnosticCollection.clear();

let diagnosticMap: Map<vscode.Uri, vscode.Diagnostic[]> = new Map();;
Expand Down