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 expect(
|
||||
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||
).toHaveCount(1)
|
||||
).toHaveCount(2)
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(1)
|
||||
).toHaveCount(2)
|
||||
|
||||
// Add whitespace to the end of the code.
|
||||
await u.codeLocator.click()
|
||||
@ -133,10 +133,10 @@ test.describe('Editor tests', () => {
|
||||
// Make sure we didn't clear the scene.
|
||||
await expect(
|
||||
page.locator('[data-message-type="execution-done"]')
|
||||
).toHaveCount(2)
|
||||
).toHaveCount(3)
|
||||
await expect(
|
||||
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 ({
|
||||
|
@ -311,8 +311,6 @@ export class KclManager {
|
||||
// Do not send send scene commands if the program was interrupted, go to clean up
|
||||
if (!isInterrupted) {
|
||||
this.addDiagnostics(await lintAst({ ast: ast }))
|
||||
|
||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||
setSelectionFilterToDefault(execState.memory, this.engineCommandManager)
|
||||
|
||||
if (args.zoomToFit) {
|
||||
@ -358,7 +356,13 @@ export class KclManager {
|
||||
this.lastSuccessfulProgramMemory = execState.memory
|
||||
}
|
||||
this.ast = { ...ast }
|
||||
// updateArtifactGraph relies on updated executeState/programMemory
|
||||
await this.engineCommandManager.updateArtifactGraph(this.ast)
|
||||
this._executeCallback()
|
||||
if (!isInterrupted) {
|
||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||
}
|
||||
|
||||
this.engineCommandManager.addCommandLog({
|
||||
type: 'execution-done',
|
||||
data: null,
|
||||
|
@ -66,9 +66,7 @@ export async function executeAst({
|
||||
? enginelessExecutor(ast, programMemoryOverride)
|
||||
: _executor(ast, engineCommandManager))
|
||||
|
||||
await engineCommandManager.waitForAllCommands(
|
||||
programMemoryOverride !== undefined
|
||||
)
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
|
||||
return {
|
||||
logs: [],
|
||||
|
@ -247,7 +247,7 @@ extrude003 = extrude(-15, sketch003)`
|
||||
selectedSegmentSnippet,
|
||||
expectedExtrudeSnippet
|
||||
)
|
||||
})
|
||||
}, 5_000)
|
||||
})
|
||||
|
||||
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
@ -477,7 +477,7 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)`
|
||||
|> chamfer({ length = 5, tags = [seg01] }, %)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
@ -487,8 +487,8 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName}: 3, tags: [seg02] }, %)`
|
||||
|> chamfer({ length = 5, tags = [seg01] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
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 { Models } from '@kittycad/lib'
|
||||
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 this is done when we build the artifact map synchronously.
|
||||
*/
|
||||
async waitForAllCommands(useFakeExecutor = false) {
|
||||
await Promise.all(Object.values(this.pendingCommands).map((a) => a.promise))
|
||||
setTimeout(() => {
|
||||
// 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
|
||||
// TODO: race condition
|
||||
if (!this?.kclManager) return
|
||||
waitForAllCommands() {
|
||||
return Promise.all(
|
||||
Object.values(this.pendingCommands).map((a) => a.promise)
|
||||
)
|
||||
}
|
||||
updateArtifactGraph(ast: Program) {
|
||||
this.artifactGraph = createArtifactGraph({
|
||||
orderedCommands: this.orderedCommands,
|
||||
responseMap: this.responseMap,
|
||||
ast: this.kclManager.ast,
|
||||
ast,
|
||||
})
|
||||
if (useFakeExecutor) {
|
||||
// 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
|
||||
}
|
||||
// TODO check if these still need to be deferred once e2e tests are working again.
|
||||
if (this.artifactGraph.size) {
|
||||
this.deferredArtifactEmptied(null)
|
||||
} else {
|
||||
this.deferredArtifactPopulated(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1895,11 +1895,19 @@ impl ExecutorContext {
|
||||
};
|
||||
|
||||
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
|
||||
// anyways and from the TS side we override memory and don't want to clear it.
|
||||
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.
|
||||
|
@ -69,7 +69,7 @@ async fn do_execute_and_snapshot(
|
||||
Ok((exec_state, img))
|
||||
}
|
||||
|
||||
async fn new_context(
|
||||
pub async fn new_context(
|
||||
units: UnitLength,
|
||||
with_auth: bool,
|
||||
project_directory: Option<PathBuf>,
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod cache;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
@ -2001,3 +2001,62 @@ async fn kcl_test_error_no_auth_websocket() {
|
||||
.to_string()
|
||||
.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