Fix ids for kurt so front end re-uses same ones on executions (#4780)
* updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * working test; Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * Update src/wasm-lib/tests/executor/main.rs Co-authored-by: Jonathan Tran <jonnytran@gmail.com> * Update src/wasm-lib/tests/executor/main.rs Co-authored-by: Jonathan Tran <jonnytran@gmail.com> * fix race condition * fix whoopsie * fix tsc * for some dumb ass reason the model executes twice on load Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com> Co-authored-by: Jonathan Tran <jonnytran@gmail.com> Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
This commit is contained in:
@ -112,10 +112,10 @@ test.describe('Editor tests', () => {
|
|||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||||
).toHaveCount(1)
|
).toHaveCount(2)
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-message-type="execution-done"]')
|
page.locator('[data-message-type="execution-done"]')
|
||||||
).toHaveCount(1)
|
).toHaveCount(2)
|
||||||
|
|
||||||
// Add whitespace to the end of the code.
|
// Add whitespace to the end of the code.
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
@ -133,10 +133,10 @@ test.describe('Editor tests', () => {
|
|||||||
// Make sure we didn't clear the scene.
|
// Make sure we didn't clear the scene.
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-message-type="execution-done"]')
|
page.locator('[data-message-type="execution-done"]')
|
||||||
).toHaveCount(2)
|
).toHaveCount(3)
|
||||||
await expect(
|
await expect(
|
||||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||||
).toHaveCount(1)
|
).toHaveCount(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||||
|
@ -311,8 +311,6 @@ export class KclManager {
|
|||||||
// Do not send send scene commands if the program was interrupted, go to clean up
|
// Do not send send scene commands if the program was interrupted, go to clean up
|
||||||
if (!isInterrupted) {
|
if (!isInterrupted) {
|
||||||
this.addDiagnostics(await lintAst({ ast: ast }))
|
this.addDiagnostics(await lintAst({ ast: ast }))
|
||||||
|
|
||||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
|
||||||
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
||||||
|
|
||||||
if (args.zoomToFit) {
|
if (args.zoomToFit) {
|
||||||
@ -358,7 +356,13 @@ export class KclManager {
|
|||||||
this.lastSuccessfulProgramMemory = execState.memory
|
this.lastSuccessfulProgramMemory = execState.memory
|
||||||
}
|
}
|
||||||
this.ast = { ...ast }
|
this.ast = { ...ast }
|
||||||
|
// updateArtifactGraph relies on updated executeState/programMemory
|
||||||
|
await this.engineCommandManager.updateArtifactGraph(this.ast)
|
||||||
this._executeCallback()
|
this._executeCallback()
|
||||||
|
if (!isInterrupted) {
|
||||||
|
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||||
|
}
|
||||||
|
|
||||||
this.engineCommandManager.addCommandLog({
|
this.engineCommandManager.addCommandLog({
|
||||||
type: 'execution-done',
|
type: 'execution-done',
|
||||||
data: null,
|
data: null,
|
||||||
|
@ -66,9 +66,7 @@ export async function executeAst({
|
|||||||
? enginelessExecutor(ast, programMemoryOverride)
|
? enginelessExecutor(ast, programMemoryOverride)
|
||||||
: _executor(ast, engineCommandManager))
|
: _executor(ast, engineCommandManager))
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands(
|
await engineCommandManager.waitForAllCommands()
|
||||||
programMemoryOverride !== undefined
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
|
@ -247,7 +247,7 @@ extrude003 = extrude(-15, sketch003)`
|
|||||||
selectedSegmentSnippet,
|
selectedSegmentSnippet,
|
||||||
expectedExtrudeSnippet
|
expectedExtrudeSnippet
|
||||||
)
|
)
|
||||||
})
|
}, 5_000)
|
||||||
})
|
})
|
||||||
|
|
||||||
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||||
@ -477,7 +477,7 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> chamfer({ length: 5, tags: [seg01] }, %)`
|
|> chamfer({ length = 5, tags = [seg01] }, %)`
|
||||||
const segmentSnippets = ['line([-20, 0], %)']
|
const segmentSnippets = ['line([-20, 0], %)']
|
||||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-10, 10], %)
|
|> startProfileAt([-10, 10], %)
|
||||||
@ -487,8 +487,8 @@ extrude001 = extrude(-15, sketch001)
|
|||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(-15, sketch001)
|
extrude001 = extrude(-15, sketch001)
|
||||||
|> chamfer({ length: 5, tags: [seg01] }, %)
|
|> chamfer({ length = 5, tags = [seg01] }, %)
|
||||||
|> ${edgeTreatmentType}({ ${parameterName}: 3, tags: [seg02] }, %)`
|
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||||
|
|
||||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||||
code,
|
code,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { defaultSourceRange, SourceRange } from 'lang/wasm'
|
import { defaultSourceRange, Program, SourceRange } from 'lang/wasm'
|
||||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { exportSave } from 'lib/exportSave'
|
import { exportSave } from 'lib/exportSave'
|
||||||
@ -2099,30 +2099,23 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
* When an execution takes place we want to wait until we've got replies for all of the commands
|
* When an execution takes place we want to wait until we've got replies for all of the commands
|
||||||
* When this is done when we build the artifact map synchronously.
|
* When this is done when we build the artifact map synchronously.
|
||||||
*/
|
*/
|
||||||
async waitForAllCommands(useFakeExecutor = false) {
|
waitForAllCommands() {
|
||||||
await Promise.all(Object.values(this.pendingCommands).map((a) => a.promise))
|
return Promise.all(
|
||||||
setTimeout(() => {
|
Object.values(this.pendingCommands).map((a) => a.promise)
|
||||||
// the ast is wrong without this one tick timeout.
|
)
|
||||||
// an example is `Solids should be select and deletable` e2e test will fail
|
}
|
||||||
// because the out of date ast messes with selections
|
updateArtifactGraph(ast: Program) {
|
||||||
// TODO: race condition
|
|
||||||
if (!this?.kclManager) return
|
|
||||||
this.artifactGraph = createArtifactGraph({
|
this.artifactGraph = createArtifactGraph({
|
||||||
orderedCommands: this.orderedCommands,
|
orderedCommands: this.orderedCommands,
|
||||||
responseMap: this.responseMap,
|
responseMap: this.responseMap,
|
||||||
ast: this.kclManager.ast,
|
ast,
|
||||||
})
|
})
|
||||||
if (useFakeExecutor) {
|
// TODO check if these still need to be deferred once e2e tests are working again.
|
||||||
// mock executions don't produce an artifactGraph, so this will always be empty
|
|
||||||
// skipping the below logic to wait for the next real execution
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.artifactGraph.size) {
|
if (this.artifactGraph.size) {
|
||||||
this.deferredArtifactEmptied(null)
|
this.deferredArtifactEmptied(null)
|
||||||
} else {
|
} else {
|
||||||
this.deferredArtifactPopulated(null)
|
this.deferredArtifactPopulated(null)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1895,11 +1895,19 @@ impl ExecutorContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if cache_result.clear_scene && !self.is_mock() {
|
if cache_result.clear_scene && !self.is_mock() {
|
||||||
|
// Pop the execution state, since we are starting fresh.
|
||||||
|
let mut id_generator = exec_state.id_generator.clone();
|
||||||
|
// We do not pop the ids, since we want to keep the same id generator.
|
||||||
|
// This is for the front end to keep track of the ids.
|
||||||
|
id_generator.next_id = 0;
|
||||||
|
*exec_state = ExecState {
|
||||||
|
id_generator,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
// We don't do this in mock mode since there is no engine connection
|
// We don't do this in mock mode since there is no engine connection
|
||||||
// anyways and from the TS side we override memory and don't want to clear it.
|
// anyways and from the TS side we override memory and don't want to clear it.
|
||||||
self.reset_scene(exec_state, Default::default()).await?;
|
self.reset_scene(exec_state, Default::default()).await?;
|
||||||
// Pop the execution state, since we are starting fresh.
|
|
||||||
*exec_state = Default::default();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use the top-level file's path.
|
// TODO: Use the top-level file's path.
|
||||||
|
@ -69,7 +69,7 @@ async fn do_execute_and_snapshot(
|
|||||||
Ok((exec_state, img))
|
Ok((exec_state, img))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn new_context(
|
pub async fn new_context(
|
||||||
units: UnitLength,
|
units: UnitLength,
|
||||||
with_auth: bool,
|
with_auth: bool,
|
||||||
project_directory: Option<PathBuf>,
|
project_directory: Option<PathBuf>,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
mod cache;
|
mod cache;
|
||||||
|
|
||||||
use kcl_lib::{
|
use kcl_lib::{
|
||||||
test_server::{execute_and_snapshot, execute_and_snapshot_no_auth},
|
test_server::{execute_and_snapshot, execute_and_snapshot_no_auth, new_context},
|
||||||
UnitLength,
|
UnitLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2001,3 +2001,62 @@ async fn kcl_test_error_no_auth_websocket() {
|
|||||||
.to_string()
|
.to_string()
|
||||||
.contains("Please send the following object over this websocket"));
|
.contains("Please send the following object over this websocket"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_ids_stable_between_executions() {
|
||||||
|
let code = r#"sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([61.74, 206.13], %)
|
||||||
|
|> xLine(305.11, %, $seg01)
|
||||||
|
|> yLine(-291.85, %)
|
||||||
|
|> xLine(-segLen(seg01), %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(40.14, %)
|
||||||
|
|> shell({
|
||||||
|
faces: [seg01],
|
||||||
|
thickness: 3.14,
|
||||||
|
}, %)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let ctx = new_context(UnitLength::Mm, true, None).await.unwrap();
|
||||||
|
let old_program = kcl_lib::Program::parse_no_errs(code).unwrap();
|
||||||
|
// Execute the program.
|
||||||
|
let mut exec_state = Default::default();
|
||||||
|
let cache_info = kcl_lib::CacheInformation {
|
||||||
|
old: None,
|
||||||
|
new_ast: old_program.ast.clone(),
|
||||||
|
};
|
||||||
|
ctx.run(cache_info, &mut exec_state).await.unwrap();
|
||||||
|
|
||||||
|
// Get the id_generator from the first execution.
|
||||||
|
let id_generator = exec_state.id_generator.clone();
|
||||||
|
|
||||||
|
let code = r#"sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([62.74, 206.13], %)
|
||||||
|
|> xLine(305.11, %, $seg01)
|
||||||
|
|> yLine(-291.85, %)
|
||||||
|
|> xLine(-segLen(seg01), %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(40.14, %)
|
||||||
|
|> shell({
|
||||||
|
faces: [seg01],
|
||||||
|
thickness: 3.14,
|
||||||
|
}, %)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Execute a slightly different program again.
|
||||||
|
let program = kcl_lib::Program::parse_no_errs(code).unwrap();
|
||||||
|
let cache_info = kcl_lib::CacheInformation {
|
||||||
|
old: Some(kcl_lib::OldAstState {
|
||||||
|
ast: old_program.ast.clone(),
|
||||||
|
exec_state: exec_state.clone(),
|
||||||
|
settings: ctx.settings.clone(),
|
||||||
|
}),
|
||||||
|
new_ast: program.ast.clone(),
|
||||||
|
};
|
||||||
|
// Execute the program.
|
||||||
|
ctx.run(cache_info, &mut exec_state).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(id_generator, exec_state.id_generator);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user