Integration test

This commit is contained in:
Dave Bartolomeo
2022-07-28 22:20:22 -04:00
parent 40e0027074
commit 476ea7aef0
5 changed files with 3257 additions and 0 deletions

View File

@@ -2,6 +2,7 @@ import * as assert from 'assert';
import * as path from 'path';
import * as vscode from 'vscode';
import * as determiningSelectedQueryTest from './determining-selected-query-test';
import * as sourcemapTest from './sourcemap.test';
describe('launching with a minimal workspace', async () => {
@@ -38,3 +39,4 @@ describe('launching with a minimal workspace', async () => {
});
determiningSelectedQueryTest.run();
sourcemapTest.run();

View File

@@ -0,0 +1,71 @@
import { fail } from 'assert';
import { commands, Selection, window, workspace } from 'vscode';
import * as path from 'path';
import * as assert from 'assert';
import { expect } from 'chai';
import { tmpDir } from '../../helpers';
import * as fs from 'fs-extra';
export function run() {
/**
* Integration tests for queries
*/
describe('SourceMap', function() {
this.timeout(200000);
it('should jump to QL code', async () => {
try {
this.timeout(60000);
const root = workspace.workspaceFolders![0].uri.fsPath;
const srcFiles = {
summary: path.join(root, 'log-summary', 'evaluator-log.summary'),
summaryMap: path.join(root, 'log-summary', 'evaluator-log.summary.map')
};
// We need to modify the source map so that its paths point to the actual location of the
// workspace root on this machine. We'll copy the summary and its source map to a temp
// directory, modify the source map their, and open that summary.
const tempFiles = await copyFilesToTempDirectory(srcFiles);
// The checked-in sourcemap has placeholders of the form `${root}`, which we need to replace
// with the actual root directory.
const mapText = await fs.readFile(tempFiles.summaryMap, 'utf-8');
// Always use forward slashes, since they work everywhere.
const slashRoot = root.replaceAll('\\', '/');
const newMapText = mapText.replaceAll('${root}', slashRoot);
await fs.writeFile(tempFiles.summaryMap, newMapText);
const summaryDocument = await workspace.openTextDocument(tempFiles.summary);
assert(summaryDocument.languageId === 'ql-summary');
const summaryEditor = await window.showTextDocument(summaryDocument);
summaryEditor.selection = new Selection(356, 10, 356, 10);
await commands.executeCommand('codeQL.gotoQL');
const newEditor = window.activeTextEditor;
expect(newEditor).to.be.not.undefined;
const newDocument = newEditor!.document;
expect(path.basename(newDocument.fileName)).to.equal('Namespace.qll');
const newSelection = newEditor!.selection;
expect(newSelection.start.line).to.equal(60);
expect(newSelection.start.character).to.equal(2);
} catch (e) {
console.error('Test Failed');
fail(e as Error);
}
});
async function copyFilesToTempDirectory<T extends Record<string, string>>(files: T): Promise<T> {
const tempDir = path.join(tmpDir.name, 'log-summary');
await fs.ensureDir(tempDir);
const result: Record<string, string> = {};
for (const key in files) {
const srcPath = files[key];
const destPath = path.join(tempDir, path.basename(srcPath));
await fs.copy(srcPath, destPath);
result[key] = destPath;
}
return result as T;
}
});
}

View File

@@ -0,0 +1,237 @@
/**
* Provides classes for modeling namespaces, `using` directives and `using` declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Type
import semmle.code.cpp.metrics.MetricNamespace
/**
* A C++ namespace. For example the (single) namespace `A` in the following
* code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
* Note that namespaces are somewhat nebulous entities, as they do not in
* general have a single well-defined location in the source code. The
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
* and should be used when a location is required. For example, the `std::`
* namespace is particularly nebulous, as parts of it are defined across a
* wide range of headers. As a more extreme example, the global namespace
* is never explicitly declared, but might correspond to a large proportion
* of the source code.
*/
class Namespace extends NameQualifyingElement, @namespace {
/**
* Gets the location of the namespace. Most namespaces do not have a
* single well-defined source location, so a dummy location is returned,
* unless the namespace has exactly one declaration entry.
*/
override Location getLocation() {
if strictcount(this.getADeclarationEntry()) = 1
then result = this.getADeclarationEntry().getLocation()
else result instanceof UnknownDefaultLocation
}
/** Gets the simple name of this namespace. */
override string getName() { namespaces(underlyingElement(this), result) }
/** Holds if this element is named `name`. */
predicate hasName(string name) { name = this.getName() }
/** Holds if this namespace is anonymous. */
predicate isAnonymous() { this.hasName("(unnamed namespace)") }
/** Gets the name of the parent namespace, if it exists. */
private string getParentName() {
result = this.getParentNamespace().getName() and
result != ""
}
/** Gets the qualified name of this namespace. For example: `a::b`. */
string getQualifiedName() {
if exists(this.getParentName())
then result = this.getParentNamespace().getQualifiedName() + "::" + this.getName()
else result = this.getName()
}
/** Gets the parent namespace, if any. */
Namespace getParentNamespace() {
namespacembrs(unresolveElement(result), underlyingElement(this))
or
not namespacembrs(_, underlyingElement(this)) and result instanceof GlobalNamespace
}
/** Gets a child declaration of this namespace. */
Declaration getADeclaration() { namespacembrs(underlyingElement(this), unresolveElement(result)) }
/** Gets a child namespace of this namespace. */
Namespace getAChildNamespace() {
namespacembrs(underlyingElement(this), unresolveElement(result))
}
/** Holds if the namespace is inline. */
predicate isInline() { namespace_inline(underlyingElement(this)) }
/** Holds if this namespace may be from source. */
override predicate fromSource() { this.getADeclaration().fromSource() }
/** Gets the metric namespace. */
MetricNamespace getMetrics() { result = this }
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
string getFriendlyName() { result = this.getQualifiedName() }
final override string toString() { result = this.getFriendlyName() }
/** Gets a declaration of (part of) this namespace. */
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
/** Gets a file which declares (part of) this namespace. */
File getAFile() { result = this.getADeclarationEntry().getLocation().getFile() }
}
/**
* A declaration of (part of) a C++ namespace. This corresponds to a single
* `namespace N { ... }` occurrence in the source code. For example the two
* mentions of `A` in the following code:
* ```
* namespace A
* {
* // ...
* }
*
* // ...
*
* namespace A
* {
* // ...
* }
* ```
*/
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
/**
* Get the namespace that this declaration entry corresponds to. There
* is a one-to-many relationship between `Namespace` and
* `NamespaceDeclarationEntry`.
*/
Namespace getNamespace() {
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
}
override string toString() { result = this.getNamespace().getFriendlyName() }
/**
* Gets the location of the token preceding the namespace declaration
* entry's body.
*
* For named declarations, such as "namespace MyStuff { ... }", this will
* give the "MyStuff" token.
*
* For anonymous declarations, such as "namespace { ... }", this will
* give the "namespace" token.
*/
override Location getLocation() { namespace_decls(underlyingElement(this), _, result, _) }
/**
* Gets the location of the namespace declaration entry's body. For
* example: the "{ ... }" in "namespace N { ... }".
*/
Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) }
override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" }
}
/**
* A C++ `using` directive or `using` declaration.
*/
class UsingEntry extends Locatable, @using {
override Location getLocation() { usings(underlyingElement(this), _, result) }
}
/**
* A C++ `using` declaration. For example:
* ```
* using std::string;
* ```
*/
class UsingDeclarationEntry extends UsingEntry {
UsingDeclarationEntry() {
not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
}
/**
* Gets the declaration that is referenced by this using declaration. For
* example, `std::string` in `using std::string`.
*/
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using " + this.getDeclaration().getDescription() }
}
/**
* A C++ `using` directive. For example:
* ```
* using namespace std;
* ```
*/
class UsingDirectiveEntry extends UsingEntry {
UsingDirectiveEntry() {
exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _))
}
/**
* Gets the namespace that is referenced by this using directive. For
* example, `std` in `using namespace std`.
*/
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
}
/**
* Holds if `g` is an instance of `GlobalNamespace`. This predicate
* is used suppress a warning in `GlobalNamespace.getADeclaration()`
* by providing a fake use of `this`.
*/
private predicate suppressWarningForUnused(GlobalNamespace g) { any() }
/**
* The C/C++ global namespace.
*/
class GlobalNamespace extends Namespace {
GlobalNamespace() { this.hasName("") }
override Declaration getADeclaration() {
suppressWarningForUnused(this) and
result.isTopLevel() and
not namespacembrs(_, unresolveElement(result))
}
/** Gets a child namespace of the global namespace. */
override Namespace getAChildNamespace() {
suppressWarningForUnused(this) and
not namespacembrs(unresolveElement(result), _)
}
override Namespace getParentNamespace() { none() }
override string getFriendlyName() { result = "(global namespace)" }
}
/**
* The C++ `std::` namespace.
*/
class StdNamespace extends Namespace {
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long