ensure we never execute over ourselves (#3419)
* ensure we never execute over ourselves Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixups Signed-off-by: Jess Frazelle <github@jessfraz.com> * cleanup Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * weird logs Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix flake Signed-off-by: Jess Frazelle <github@jessfraz.com> * make faster Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect, Page } from '@playwright/test'
|
||||||
|
|
||||||
import { getUtils, setup, tearDown } from './test-utils'
|
import { getUtils, setup, tearDown } from './test-utils'
|
||||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||||
@ -346,55 +346,24 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
// expect zero errors in guter
|
// expect zero errors in guter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
// export the model
|
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
|
||||||
await expect(exportButton).toBeVisible()
|
|
||||||
|
|
||||||
// Click the export button
|
|
||||||
exportButton.click()
|
|
||||||
|
|
||||||
// Click the stl.
|
|
||||||
const stlOption = page.getByText('glTF')
|
|
||||||
await expect(stlOption).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Click the checkbox
|
|
||||||
const submitButton = page.getByText('Confirm Export')
|
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
|
||||||
// Look out for the toast message
|
|
||||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
|
||||||
|
|
||||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||||
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
const engineErrorToastMessage = page.getByText(`Nothing to export`)
|
||||||
|
|
||||||
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
|
||||||
|
|
||||||
// Try exporting again.
|
await clickExportButton(page)
|
||||||
// Click the export button
|
|
||||||
exportButton.click()
|
|
||||||
|
|
||||||
// Click the stl.
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
await expect(stlOption).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await clickExportButton(page)
|
||||||
|
|
||||||
// Click the checkbox
|
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
await expect(alreadyExportingToastMessage).toBeVisible()
|
await expect(alreadyExportingToastMessage).toBeVisible()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
// Expect it to succeed.
|
// Expect it to succeed.
|
||||||
await expect(exportingToastMessage).not.toBeVisible()
|
await expect(exportingToastMessage).not.toBeVisible()
|
||||||
await expect(errorToastMessage).not.toBeVisible()
|
await expect(errorToastMessage).not.toBeVisible()
|
||||||
@ -406,18 +375,7 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
await expect(alreadyExportingToastMessage).not.toBeVisible()
|
||||||
|
|
||||||
// Try exporting again.
|
// Try exporting again.
|
||||||
// Click the export button
|
await clickExportButton(page)
|
||||||
exportButton.click()
|
|
||||||
|
|
||||||
// Click the stl.
|
|
||||||
await expect(stlOption).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Click the checkbox
|
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -432,3 +390,24 @@ const sketch001 = startSketchAt([-0, -0])
|
|||||||
await expect(successToastMessage).toBeVisible()
|
await expect(successToastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
async function clickExportButton(page: Page) {
|
||||||
|
// export the model
|
||||||
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
|
await expect(exportButton).toBeVisible()
|
||||||
|
|
||||||
|
// Click the export button
|
||||||
|
exportButton.click()
|
||||||
|
|
||||||
|
// Click the stl.
|
||||||
|
const gltfOption = page.getByText('glTF')
|
||||||
|
await expect(gltfOption).toBeVisible()
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Click the checkbox
|
||||||
|
const submitButton = page.getByText('Confirm Export')
|
||||||
|
await expect(submitButton).toBeVisible()
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
}
|
||||||
|
@ -149,8 +149,6 @@ export const ModelingMachineProvider = ({
|
|||||||
},
|
},
|
||||||
'sketch exit execute': ({ store }) => {
|
'sketch exit execute': ({ store }) => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
// blocks entering a sketch until after exit sketch code has run
|
|
||||||
kclManager.isExecuting = true
|
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
|
|
||||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||||
|
@ -19,6 +19,16 @@ import { getNodeFromPath } from './queryAst'
|
|||||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
|
|
||||||
|
interface ExecuteArgs {
|
||||||
|
ast?: Program
|
||||||
|
zoomToFit?: boolean
|
||||||
|
executionId?: number
|
||||||
|
zoomOnRangeAndType?: {
|
||||||
|
range: SourceRange
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class KclManager {
|
export class KclManager {
|
||||||
private _ast: Program = {
|
private _ast: Program = {
|
||||||
body: [],
|
body: [],
|
||||||
@ -36,6 +46,7 @@ export class KclManager {
|
|||||||
private _lints: Diagnostic[] = []
|
private _lints: Diagnostic[] = []
|
||||||
private _kclErrors: KCLError[] = []
|
private _kclErrors: KCLError[] = []
|
||||||
private _isExecuting = false
|
private _isExecuting = false
|
||||||
|
private _executeIsStale: ExecuteArgs | null = null
|
||||||
private _wasmInitFailed = true
|
private _wasmInitFailed = true
|
||||||
|
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
@ -113,9 +124,25 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
set isExecuting(isExecuting) {
|
set isExecuting(isExecuting) {
|
||||||
this._isExecuting = isExecuting
|
this._isExecuting = isExecuting
|
||||||
|
// If we have finished executing, but the execute is stale, we should
|
||||||
|
// execute again.
|
||||||
|
if (!isExecuting && this.executeIsStale) {
|
||||||
|
const args = this.executeIsStale
|
||||||
|
this.executeIsStale = null
|
||||||
|
this.executeAst(args)
|
||||||
|
} else {
|
||||||
|
}
|
||||||
this._isExecutingCallback(isExecuting)
|
this._isExecutingCallback(isExecuting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get executeIsStale() {
|
||||||
|
return this._executeIsStale
|
||||||
|
}
|
||||||
|
|
||||||
|
set executeIsStale(executeIsStale) {
|
||||||
|
this._executeIsStale = executeIsStale
|
||||||
|
}
|
||||||
|
|
||||||
get wasmInitFailed() {
|
get wasmInitFailed() {
|
||||||
return this._wasmInitFailed
|
return this._wasmInitFailed
|
||||||
}
|
}
|
||||||
@ -202,16 +229,16 @@ export class KclManager {
|
|||||||
// This NEVER updates the code, if you want to update the code DO NOT add to
|
// This NEVER updates the code, if you want to update the code DO NOT add to
|
||||||
// this function, too many other things that don't want it exist.
|
// this function, too many other things that don't want it exist.
|
||||||
// just call to codeManager from wherever you want in other files.
|
// just call to codeManager from wherever you want in other files.
|
||||||
async executeAst(
|
async executeAst(args: ExecuteArgs = {}): Promise<void> {
|
||||||
ast: Program = this._ast,
|
if (this.isExecuting) {
|
||||||
zoomToFit?: boolean,
|
this.executeIsStale = args
|
||||||
executionId?: number,
|
// Exit early if we are already executing.
|
||||||
zoomOnRangeAndType?: {
|
return
|
||||||
range: SourceRange
|
|
||||||
type: string
|
|
||||||
}
|
}
|
||||||
): Promise<void> {
|
|
||||||
const currentExecutionId = executionId || Date.now()
|
const ast = args.ast || this.ast
|
||||||
|
|
||||||
|
const currentExecutionId = args.executionId || Date.now()
|
||||||
this._cancelTokens.set(currentExecutionId, false)
|
this._cancelTokens.set(currentExecutionId, false)
|
||||||
|
|
||||||
this.isExecuting = true
|
this.isExecuting = true
|
||||||
@ -229,12 +256,12 @@ export class KclManager {
|
|||||||
defaultSelectionFilter(programMemory, this.engineCommandManager)
|
defaultSelectionFilter(programMemory, this.engineCommandManager)
|
||||||
await this.engineCommandManager.waitForAllCommands()
|
await this.engineCommandManager.waitForAllCommands()
|
||||||
|
|
||||||
if (zoomToFit) {
|
if (args.zoomToFit) {
|
||||||
let zoomObjectId: string | undefined = ''
|
let zoomObjectId: string | undefined = ''
|
||||||
if (zoomOnRangeAndType) {
|
if (args.zoomOnRangeAndType) {
|
||||||
zoomObjectId = this.engineCommandManager?.mapRangeToObjectId(
|
zoomObjectId = this.engineCommandManager?.mapRangeToObjectId(
|
||||||
zoomOnRangeAndType.range,
|
args.zoomOnRangeAndType.range,
|
||||||
zoomOnRangeAndType.type
|
args.zoomOnRangeAndType.type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +286,7 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.isExecuting = false
|
this.isExecuting = false
|
||||||
|
|
||||||
// Check the cancellation token for this execution before applying side effects
|
// Check the cancellation token for this execution before applying side effects
|
||||||
if (this._cancelTokens.get(currentExecutionId)) {
|
if (this._cancelTokens.get(currentExecutionId)) {
|
||||||
this._cancelTokens.delete(currentExecutionId)
|
this._cancelTokens.delete(currentExecutionId)
|
||||||
@ -351,8 +379,7 @@ export class KclManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.ast = { ...ast }
|
this.ast = { ...ast }
|
||||||
this.isExecuting = true // executeAst sets this to false again
|
return this.executeAst({ zoomToFit })
|
||||||
return this.executeAst(ast, zoomToFit)
|
|
||||||
}
|
}
|
||||||
format() {
|
format() {
|
||||||
const originalCode = codeManager.code
|
const originalCode = codeManager.code
|
||||||
@ -430,12 +457,11 @@ export class KclManager {
|
|||||||
codeManager.updateCodeEditor(newCode)
|
codeManager.updateCodeEditor(newCode)
|
||||||
// Write the file to disk.
|
// Write the file to disk.
|
||||||
await codeManager.writeToFile()
|
await codeManager.writeToFile()
|
||||||
await this.executeAst(
|
await this.executeAst({
|
||||||
astWithUpdatedSource,
|
ast: astWithUpdatedSource,
|
||||||
optionalParams?.zoomToFit,
|
zoomToFit: optionalParams?.zoomToFit,
|
||||||
undefined,
|
zoomOnRangeAndType: optionalParams?.zoomOnRangeAndType,
|
||||||
optionalParams?.zoomOnRangeAndType
|
})
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
// When we don't re-execute, we still want to update the program
|
// When we don't re-execute, we still want to update the program
|
||||||
// memory with the new ast. So we will hit the mock executor
|
// memory with the new ast. So we will hit the mock executor
|
||||||
|
@ -136,7 +136,7 @@ beforeAll(async () => {
|
|||||||
console.error(ast)
|
console.error(ast)
|
||||||
return Promise.reject(ast)
|
return Promise.reject(ast)
|
||||||
}
|
}
|
||||||
await kclManager.executeAst(ast)
|
await kclManager.executeAst({ ast })
|
||||||
|
|
||||||
cacheToWriteToFileTemp[codeKey] = {
|
cacheToWriteToFileTemp[codeKey] = {
|
||||||
orderedCommands: engineCommandManager.orderedCommands,
|
orderedCommands: engineCommandManager.orderedCommands,
|
||||||
|
Reference in New Issue
Block a user