Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: give each test suite an indepedent workspace #403

Merged
merged 6 commits into from
May 17, 2024
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ client/server
.vscode-test
notes
target
test/activeWorkspace
!test/activeWorkspace/.gitkeep
flix.jar
*.vsix
2 changes: 1 addition & 1 deletion .vscode-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const version = process.env.VSCODE_VERSION
module.exports = defineConfig({
version,
files: ['test/out/**/*.test.js'],
workspaceFolder: 'test/testWorkspace',
workspaceFolder: 'test/activeWorkspace/',

mocha: {
// Downloading compiler takes a long time
Expand Down
1 change: 1 addition & 0 deletions test/activeWorkspace/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Ensure that this empty directory is checked in
12 changes: 3 additions & 9 deletions test/src/codeActions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,17 @@

import * as assert from 'assert'
import * as vscode from 'vscode'
import { getTestDocUri, activate, open, copyFile, tryDeleteFile } from './util'
import { getTestDocUri, activate, open } from './util'

suite('Code actions', () => {
const docUriLatent = getTestDocUri('latent/UnusedFunction.flix')
const docUri = getTestDocUri('src/UnusedFunction.flix')

suiteSetup(async () => {
await activate()
})
teardown(async () => {
await tryDeleteFile(docUri)
await activate('codeActions')
await open(docUri)
})

test('Should propose prefixing unused def with underscore', async () => {
await copyFile(docUriLatent, docUri)
await open(docUri)

const position = new vscode.Position(1, 8)
const range = new vscode.Range(position, position)
const r = (await vscode.commands.executeCommand(
Expand Down
2 changes: 1 addition & 1 deletion test/src/codeLenses.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ suite('Code lenses', () => {
const areaDocUri = getTestDocUri('src/Area.flix')

suiteSetup(async () => {
await activate()
await activate('codeLenses')
})

test('Should propose running main function', async () => {
Expand Down
8 changes: 2 additions & 6 deletions test/src/completions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,20 @@

import * as assert from 'assert'
import * as vscode from 'vscode'
import { getTestDocUri, activate, open, tryDeleteFile, addFile, typeText } from './util'
import { getTestDocUri, activate, open, typeText, addFile } from './util'

suite('Completions', () => {
const docUri = getTestDocUri('src/Temp.flix')

suiteSetup(async () => {
await activate()
})
teardown(async () => {
await tryDeleteFile(docUri)
await activate('completions')
})

test('Should propose completing mod', async () => {
await addFile(docUri, '')
await open(docUri)
await typeText('mo')

// TODO: Figure out why this returns all possible keywords, which is not what is shown in the editor
const position = new vscode.Position(0, 2)
const r = (await vscode.commands.executeCommand(
'vscode.executeCompletionItemProvider',
Expand Down
2 changes: 1 addition & 1 deletion test/src/diagnostics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ suite('Diagnostics', () => {
let tempDocUri: vscode.Uri | null = null

suiteSetup(async () => {
await activate()
await activate('diagnostics')
})
teardown(async () => {
if (tempDocUri !== null) {
Expand Down
2 changes: 1 addition & 1 deletion test/src/disconnect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { activate, getTestDocUri, sleep } from './util'

suite('Server disconnect', () => {
suiteSetup(async () => {
await activate()
await activate('disconnect')
})

test('When server is disconnected a reconnection should happen automatically', async () => {
Expand Down
11 changes: 3 additions & 8 deletions test/src/files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,17 @@ suite('File manipulation', () => {
const doc2Uri = getTestDocUri('src/Area.flix')
let doc2Content: Uint8Array

const jarUri = getTestDocUri('lib/external/SquareArea.jar')
let jarContent: Uint8Array

const fpkgUri = getTestDocUri('lib/circleArea.fpkg')
let fpkgContent: Uint8Array

suiteSetup(async () => {
await activate()
await activate('files')
doc2Content = await vscode.workspace.fs.readFile(doc2Uri)
jarContent = await vscode.workspace.fs.readFile(jarUri)
fpkgContent = await vscode.workspace.fs.readFile(fpkgUri)
})
teardown(async () => {
// Restore the original content of the file after each test
setup(async () => {
// Restore the original content of the files before each test
await addFile(doc2Uri, doc2Content)
await addFile(jarUri, jarContent)
await addFile(fpkgUri, fpkgContent)
})

Expand Down
17 changes: 5 additions & 12 deletions test/src/findReferences.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ suite('Find references', () => {
const areaDocUri = getTestDocUri('src/Area.flix')

suiteSetup(async () => {
await activate()
await activate('findReferences')
})

test('Should find references to enum case', async () => {
test.skip('Should find references to enum case', async () => {
const position = new vscode.Position(3, 9)
const r = (await vscode.commands.executeCommand(
'vscode.executeReferenceProvider',
Expand All @@ -39,11 +39,8 @@ suite('Find references', () => {
const mainReference = r.find(l => l.uri.path.endsWith(mainDocUri.path))
const areaReference = r.find(l => l.uri.path.endsWith(areaDocUri.path))

assert.notStrictEqual(mainReference, undefined)
assert.notStrictEqual(areaReference, undefined)

assert.deepStrictEqual(mainReference?.range, new vscode.Range(3, 9, 3, 22))
assert.deepStrictEqual(areaReference?.range, new vscode.Range(6, 13, 6, 25))
assert.deepStrictEqual(areaReference?.range, new vscode.Range(5, 13, 5, 25))
})

test('Should find references to def', async () => {
Expand All @@ -57,15 +54,11 @@ suite('Find references', () => {
assert.strictEqual(r.length, 3)

const defReference = r.find(l => l.uri.path.endsWith(areaDocUri.path) && l.range.start.line === 3)
const testReference = r.find(l => l.uri.path.endsWith(areaDocUri.path) && l.range.start.line === 13)
const testReference = r.find(l => l.uri.path.endsWith(areaDocUri.path) && l.range.start.line === 12)
const mainReference = r.find(l => l.uri.path.endsWith(mainDocUri.path))

assert.notStrictEqual(defReference, undefined)
assert.notStrictEqual(testReference, undefined)
assert.notStrictEqual(mainReference, undefined)

assert.deepStrictEqual(defReference?.range, new vscode.Range(3, 4, 3, 8))
assert.deepStrictEqual(testReference?.range, new vscode.Range(13, 39, 13, 43))
assert.deepStrictEqual(testReference?.range, new vscode.Range(12, 39, 12, 43))
assert.deepStrictEqual(mainReference?.range, new vscode.Range(10, 12, 10, 16))
})
})
2 changes: 1 addition & 1 deletion test/src/hover.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ suite('Hover info', () => {
const brokenDocUri = getTestDocUri('src/Broken.flix')

suiteSetup(async () => {
await activate()
await activate('hover')
await open(docUri)
})
teardown(async () => {
Expand Down
75 changes: 68 additions & 7 deletions test/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,64 @@ import * as path from 'path'
import * as vscode from 'vscode'

/**
* Activates the extension in the `testWorkspace` directory.
* Activates the extension and copies the contents of the given test workspace directory into the extive workspace.
*
* @param testWorkspaceName The name of the workspace directory to copy, e.g. `codeActions`.
*/
export async function activate() {
export async function activate(testWorkspaceName: string) {
// The extensionId is `publisher.name` from package.json
const ext = vscode.extensions.getExtension('flix.flix')
if (ext === undefined) {
throw new Error('Failed to activate extension')
}

await copyWorkspace(testWorkspaceName)

// This includes the time it takes for the compiler to download
// The time it takes for the compiler to start will be awaited in the first command sent to the extension
await ext.activate()
}

/**
* Clears the test workspace, and copies the contents of the given test workspace directory into the active workspace.
*
* @param testWorkspaceName The name of the workspace directory to copy, e.g. `codeActions`.
*/
async function copyWorkspace(testWorkspaceName: string) {
vscode.commands.executeCommand('workbench.action.closeAllEditors')

const activeWorkspaceUri = vscode.workspace.workspaceFolders![0].uri

/** Recursively clears all safe files from the given directory. */
async function clearDir(uri: vscode.Uri) {
const contents = await vscode.workspace.fs.readDirectory(uri)

// Recurse into subdirectories
const dirs = contents.filter(([_, type]) => type === vscode.FileType.Directory)
const dirUris = dirs.map(([name, _]) => vscode.Uri.joinPath(uri, name))
await Promise.allSettled(dirUris.map(clearDir))

const files = contents.filter(([_, type]) => type !== vscode.FileType.Directory)
const fileNames = files.map(([name, _]) => name)

// Delete all files except .gitkeep and flix.jar
const namesToKeep = ['.gitkeep', 'flix.jar']

// Be careful, and only delete files with known extensions
const extensionsToDelete = ['flix', 'toml', 'jar', 'fpkg', 'txt']

const namesToDelete = fileNames.filter(
name => !namesToKeep.includes(name) && extensionsToDelete.includes(name.split('.').at(-1)),
)
const urisToDelete = namesToDelete.map(name => vscode.Uri.joinPath(uri, name))
await Promise.allSettled(urisToDelete.map(deleteFile))
}
await clearDir(activeWorkspaceUri)

const testWorkspacePath = path.resolve(__dirname, '../testWorkspaces', testWorkspaceName)
await copyDirContents(vscode.Uri.file(testWorkspacePath), activeWorkspaceUri)
}

/**
* Opens the document at `docUri` in the main editor.
*/
Expand All @@ -48,7 +92,7 @@ export async function typeText(text: string) {
}

function getTestDocPath(p: string) {
return path.resolve(__dirname, '../testWorkspace', p)
return path.resolve(__dirname, '../activeWorkspace', p)
}
export function getTestDocUri(p: string) {
return vscode.Uri.file(getTestDocPath(p))
Expand All @@ -68,8 +112,12 @@ async function processFileChange() {
// Wait for the file system watcher to pick up the change
await sleep(1000)

// Wait for the compiler to process the change
await vscode.commands.executeCommand('flix.allJobsFinished')
try {
// Wait for the compiler to process the change
await vscode.commands.executeCommand('flix.allJobsFinished')
} catch {
// Compiler is not running
}

// Wait for the diagnostics to be updated
await sleep(1000)
Expand All @@ -83,11 +131,24 @@ export async function addFile(uri: vscode.Uri, content: string | Uint8Array) {
await processFileChange()
}

/**
* Copies the contents of the given folder `from` to the folder `to`, leaving non-overlapping files intact.
*/
export async function copyDirContents(from: vscode.Uri, to: vscode.Uri) {
const contents = await vscode.workspace.fs.readDirectory(from)
const names = contents.map(([name, _]) => name)

const uris = names.map(name => ({ from: vscode.Uri.joinPath(from, name), to: vscode.Uri.joinPath(to, name) }))

await Promise.allSettled(uris.map(({ from, to }) => copyFile(from, to)))
await processFileChange()
}

/**
* Copy the file from `from` to `to`, and wait for the compiler to process this.
*/
export async function copyFile(from: vscode.Uri, to: vscode.Uri) {
await vscode.workspace.fs.copy(from, to)
await vscode.workspace.fs.copy(from, to, { overwrite: true })
await processFileChange()
}

Expand All @@ -97,7 +158,7 @@ export async function copyFile(from: vscode.Uri, to: vscode.Uri) {
* Throws if the file does not exist.
*/
export async function deleteFile(uri: vscode.Uri) {
await vscode.workspace.fs.delete(uri, { useTrash: true })
await vscode.workspace.fs.delete(uri)
await processFileChange()
}

Expand Down
Binary file removed test/testWorkspace/lib/external/SquareArea.jar
Binary file not shown.
12 changes: 0 additions & 12 deletions test/testWorkspace/lib/external/SquareArea.jar.txt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,3 @@ description = "test"
version = "0.1.0"
flix = "0.44.0"
authors = ["John Doe <[email protected]>"]

[dependencies]
"github:flix/museum" = "1.4.0"

[mvn-dependencies]
"org.apache.commons:commons-lang3" = "3.12.0"
6 changes: 6 additions & 0 deletions test/testWorkspaces/codeLenses/flix.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "vscode-flix-test"
description = "test"
version = "0.1.0"
flix = "0.44.0"
authors = ["John Doe <[email protected]>"]
13 changes: 13 additions & 0 deletions test/testWorkspaces/codeLenses/src/Area.flix
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

/// Computes the area of the given shape using
/// pattern matching and basic arithmetic.
def area(s: Shape): Int32 = {
match s {
case Shape.Circle(r) => 3 * r * r
case Shape.Square(w) => w * w
case Shape.Rectangle(h, w) => h * w
}
}

@Test
def testSquareArea(): Bool = Assert.eq(area(Shape.Square(5)), 25)
6 changes: 6 additions & 0 deletions test/testWorkspaces/completions/flix.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "vscode-flix-test"
description = "test"
version = "0.1.0"
flix = "0.44.0"
authors = ["John Doe <[email protected]>"]
6 changes: 6 additions & 0 deletions test/testWorkspaces/diagnostics/flix.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "vscode-flix-test"
description = "test"
version = "0.1.0"
flix = "0.44.0"
authors = ["John Doe <[email protected]>"]
Loading
Loading