Skip to content

Commit

Permalink
Add and update tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
tjprescott committed Feb 8, 2023
1 parent 7e1ec77 commit 1a213fa
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 63 deletions.
17 changes: 11 additions & 6 deletions tools/apiview/emitters/cadl-apiview/src/apiview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,19 @@ export class ApiView {
this.navigationItems.push(item);
}

shouldEmitNamespace(ns: Namespace): boolean {
if (ns.name === "" && this.includeGlobalNamespace) {
shouldEmitNamespace(name: string): boolean {
if (name === "" && this.includeGlobalNamespace) {
return true;
}
if (!ns.name.startsWith(this.packageName)) {
if (name === this.packageName) {
return true;
}
// FIXME: This should actually ensure that it is a proper subnamespace
if (!name.startsWith(this.packageName)) {
return false;
}
return true;
const suffix = name.substring(this.packageName.length);
return suffix.startsWith(".");
}

emit(program: Program) {
Expand All @@ -335,7 +340,7 @@ export class ApiView {
allNamespaces = new Map([...allNamespaces].sort());

for (const [name, ns] of allNamespaces.entries()) {
if (!this.shouldEmitNamespace(ns)) {
if (!this.shouldEmitNamespace(name)) {
continue;
}
const nsModel = new NamespaceModel(name, ns, program);
Expand Down Expand Up @@ -845,7 +850,7 @@ export class ApiView {
this.blankLines(1);
}
this.endGroup();
this.newline();
this.blankLines(1);
this.namespaceStack.pop();
}

Expand Down
60 changes: 60 additions & 0 deletions tools/apiview/emitters/cadl-apiview/test/apiview-options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Diagnostic, logDiagnostics, resolvePath } from "@cadl-lang/compiler";
import { expectDiagnosticEmpty } from "@cadl-lang/compiler/testing";
import { strictEqual } from "assert";
import { apiViewFor, apiViewText, compare } from "./test-host.js";

describe("apiview-options: tests", () => {

it("omits namespaces that aren't proper subnamespaces", async () => {
const input = `
@Cadl.service( { title: "Test", version: "1" } )
namespace Azure.Test {
model Foo {};
}
namespace Azure.Test.Sub {
model SubFoo {};
};
namespace Azure.TestBad {
model BadFoo {};
};
`;
const expect = `
namespace Azure.Test {
model Foo {}
}
namespace Azure.Test.Sub {
model SubFoo {}
}
`
const apiview = await apiViewFor(input, {});
const actual = apiViewText(apiview);
compare(expect, actual, 9);
});

it("outputs the global namespace when --include-global-namespace is set", async () => {
const input = `
model SomeGlobal {};
@Cadl.service( { title: "Test", version: "1" } )
namespace Azure.Test {
model Foo {};
}
`;
const expect = `
model SomeGlobal {};
namespace Azure.Test {
model Foo {}
}
`
const apiview = await apiViewFor(input, {
"include-global-namespace": true
});
const actual = apiViewText(apiview);
compare(expect, actual, 9);
});

});
59 changes: 2 additions & 57 deletions tools/apiview/emitters/cadl-apiview/test/apiview.test.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,8 @@
import { Diagnostic, logDiagnostics, resolvePath } from "@cadl-lang/compiler";
import { expectDiagnosticEmpty } from "@cadl-lang/compiler/testing";
import assert, { fail, strictEqual } from "assert";
import { ApiViewDocument, ApiViewTokenKind } from "../src/apiview.js";
import { ApiViewEmitterOptions } from "../src/lib.js";
import { createApiViewTestRunner } from "./test-host.js";
import { apiViewFor, apiViewText, compare } from "./test-host.js";

describe("apiview: tests", () => {
async function apiViewFor(code: string, options: ApiViewEmitterOptions): Promise<ApiViewDocument> {
const runner = await createApiViewTestRunner({withVersioning: true});
const outPath = resolvePath("/apiview.json");
await runner.compile(code, {
noEmit: false,
emitters: { "@azure-tools/cadl-apiview": { ...options, "output-file": outPath } },
miscOptions: { "disable-linter": true },
});

const jsonText = runner.fs.get(outPath)!;
const apiview = JSON.parse(jsonText) as ApiViewDocument;
return apiview;
}

function apiViewText(apiview: ApiViewDocument): string[] {
const vals = new Array<string>;
for (const token of apiview.Tokens) {
switch (token.Kind) {
case ApiViewTokenKind.Newline:
vals.push("\n");
break;
default:
if (token.Value != undefined) {
vals.push(token.Value);
}
break;
}
}
return vals.join("").split("\n");
}

/** Compares an expected string to a subset of the actual output. */
function compare(expect: string, lines: string[], offset: number) {
// split the input into lines and ignore leading or trailing empty lines.
let expectedLines = expect.split("\n");
if (expectedLines[0].trim() == '') {
expectedLines = expectedLines.slice(1);
}
if (expectedLines[expectedLines.length - 1].trim() == '') {
expectedLines = expectedLines.slice(0, -1);
}
// remove any leading indentation
const indent = expectedLines[0].length - expectedLines[0].trimStart().length;
for (let x = 0; x < expectedLines.length; x++) {
expectedLines[x] = expectedLines[x].substring(indent);
}
const checkLines = lines.slice(offset, offset + expectedLines.length);
strictEqual(expectedLines.length, checkLines.length);
for (let x = 0; x < checkLines.length; x++) {
strictEqual(expectedLines[x], checkLines[x], `Actual differed from expected at line #${x + 1}\nACTUAL: '${checkLines[x]}'\nEXPECTED: '${expectedLines[x]}'`);
}
}

/** Validates that there are no repeat defintion IDs and that each line has only one definition ID. */
function validateDefinitionIds(apiview: ApiViewDocument) {
const definitionIds = new Set<string>();
Expand Down Expand Up @@ -188,6 +132,7 @@ describe("apiview: tests", () => {
}
alias Creature = Animal
}
`;
const apiview = await apiViewFor(input, {});
const actual = apiViewText(apiview);
Expand Down
71 changes: 71 additions & 0 deletions tools/apiview/emitters/cadl-apiview/test/test-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { VersioningTestLibrary } from "@cadl-lang/versioning/testing";
import { AzureCoreTestLibrary } from "@azure-tools/cadl-azure-core/testing";
import { ApiViewTestLibrary } from "../src/testing/index.js";
import "@azure-tools/cadl-apiview";
import { ApiViewEmitterOptions } from "../src/lib.js";
import { ApiViewDocument, ApiViewTokenKind } from "../src/apiview.js";
import { resolvePath } from "@cadl-lang/compiler";
import { strictEqual } from "assert";

export async function createApiViewTestHost() {
return createTestHost({
Expand All @@ -29,3 +33,70 @@ export async function createApiViewTestRunner({
}
});
}

export async function apiViewFor(code: string, options: ApiViewEmitterOptions): Promise<ApiViewDocument> {
const runner = await createApiViewTestRunner({withVersioning: true});
const outPath = resolvePath("/apiview.json");
await runner.compile(code, {
noEmit: false,
emitters: { "@azure-tools/cadl-apiview": { ...options, "output-file": outPath } },
miscOptions: { "disable-linter": true },
});

const jsonText = runner.fs.get(outPath)!;
const apiview = JSON.parse(jsonText) as ApiViewDocument;
return apiview;
}

export function apiViewText(apiview: ApiViewDocument): string[] {
const vals = new Array<string>;
for (const token of apiview.Tokens) {
switch (token.Kind) {
case ApiViewTokenKind.Newline:
vals.push("\n");
break;
default:
if (token.Value != undefined) {
vals.push(token.Value);
}
break;
}
}
return vals.join("").split("\n");
}

function getIndex(lines: string[]): number {
for (const line of lines) {
if (line.trim() !== "") {
return line.length - line.trimStart().length;
}
}
return 0;
}

/** Eliminates leading indentation and blank links that can mess with comparisons */
function trimLines(lines: string[]): string[] {
const trimmed: string[] = [];
const indent = getIndex(lines);
for (const line of lines) {
if (line.trim() == '') {
// skip blank lines
continue;
} else {
// remove any leading indentation
trimmed.push(line.substring(indent));
}
}
return trimmed;
}

/** Compares an expected string to a subset of the actual output. */
export function compare(expect: string, lines: string[], offset: number) {
// split the input into lines and ignore leading or trailing empty lines.
const expectedLines = trimLines(expect.split("\n"));
const checkLines = trimLines(lines.slice(offset));
strictEqual(expectedLines.length, checkLines.length);
for (let x = 0; x < checkLines.length; x++) {
strictEqual(expectedLines[x], checkLines[x], `Actual differed from expected at line #${x + 1}\nACTUAL: '${checkLines[x]}'\nEXPECTED: '${expectedLines[x]}'`);
}
}

0 comments on commit 1a213fa

Please sign in to comment.