Files
EVEngine/test/editor_action_timeline_script.cpp
T

423 lines
26 KiB
C++

#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
#include "common/Module.h"
#include <simplesquirrel/simplesquirrel.hpp>
#include <chrono>
#include <cmath>
#include <filesystem>
#include <string>
TEST_CASE("editor.actionTimeline.scriptUsesCanonicalTransactionsAndWorkspace") {
ssq::VM vm(2048, ssq::Libs::ALL);
eve::ModuleManager::expose(vm);
vm.run(vm.compileSource(R"(
editor <- eve.Editor();
workspace <- editor.newWorkspace("test.combat", "Test Combat Editor");
timelineAsset <- {
schema="eve.action.timeline", schemaVersion=1,
actionId="test:light-attack", durationNs=1000000000,
animationUri="asset://test/attack.glb#Attack",
metadata={},
tracks=[{
id="test-track:gameplay", label="Gameplay", kind="gameplay",
muted=false, locked=false, notifies=[],
states=[{
id="test-state:hitbox", type="combat:hitbox-window",
startNs=200000000, endNs=500000000,
payload={hitbox="weapon.main"}
}]
}]
};
created <- eve.ActionEditorModule().create("test.asset.light-attack", timelineAsset);
actionEditor <- created.value;
externalDamage <- actionEditor.setRuntimeBlockExternallyHandled("combat:damage", true);
externalUnknown <- actionEditor.setRuntimeBlockExternallyHandled("test:unknown", true);
previewHostAvailable <- actionEditor.hasPreviewHost();
previewRefresh <- actionEditor.refreshPreview();
runtimeRateBeforeStart <- actionEditor.setRuntimeRate(1.5);
sectionProgressBeforeStart <- actionEditor.evaluateRuntimeSectionProgress(0, 0.5);
sectionSyncJumpBeforeStart <- actionEditor.syncRuntimeSectionAndJump(0, 0.25);
clearSectionSyncBeforeStart <- actionEditor.clearRuntimeSectionSync(0);
testAnimation <- eve.Animation();
testSkeleton <- testAnimation.newSkeleton();
testBoneMask <- testAnimation.newBoneMask(testSkeleton);
testPose <- testAnimation.newPose(testSkeleton.getBoneCount());
setBoneMaskBeforeStart <- actionEditor.setRuntimeBoneMask(testBoneMask);
clearBoneMaskBeforeStart <- actionEditor.clearRuntimeBoneMask();
setLayerWeightBeforeStart <- actionEditor.setRuntimeLayerWeight(0.5);
setLayerAdditiveBeforeStart <- actionEditor.setRuntimeLayerAdditive(true);
runtimeLayerWeightBeforeStart <- actionEditor.getRuntimeLayerWeight();
runtimeLayerAdditiveBeforeStart <- actionEditor.getRuntimeLayerAdditive();
runtimePoseOverBaseBeforeStart <- actionEditor.getRuntimePoseOverBase(testPose);
runtimeStateBeforeStart <- actionEditor.getRuntimeState();
runtimeElapsedBeforeStart <- actionEditor.getRuntimeElapsedSeconds();
runtimeDurationBeforeStart <- actionEditor.getRuntimeDurationSeconds();
runtimeLayerBeforeStart <- actionEditor.getRuntimeLayer();
runtimeSlotBeforeStart <- actionEditor.getRuntimeSlot();
configured <- actionEditor.configureWorkspace(workspace);
workspacePanelCount <- workspace.getPanelCount();
viewport <- actionEditor.setViewport(800.0, 36.0, 120.0);
beforeStart <- actionEditor.getStateStart("test-state:hitbox");
itemCenterX <- (actionEditor.getItemMinX(0) + actionEditor.getItemMaxX(0)) * 0.5;
itemCenterY <- (actionEditor.getItemMinY(0) + actionEditor.getItemMaxY(0)) * 0.5;
pointerDownResult <- actionEditor.pointerDown(itemCenterX, itemCenterY, false);
pointerMoveResult <- actionEditor.pointerMove(itemCenterX + 80.0);
pointerUpResult <- actionEditor.pointerUp(itemCenterX + 80.0);
movedStart <- actionEditor.getStateStart("test-state:hitbox");
revisionAfterDrag <- actionEditor.getRevision();
undoResult <- actionEditor.undo();
restoredStart <- actionEditor.getStateStart("test-state:hitbox");
inspectorTiming <- actionEditor.setItemTiming("test-state:hitbox", 0.1, 0.6);
inspectorStart <- actionEditor.getItemStart("test-state:hitbox");
inspectorEnd <- actionEditor.getItemEnd("test-state:hitbox");
invalidInspectorTiming <- actionEditor.setItemTiming("test-state:hitbox", 0.7, 0.2);
detailsResult <- actionEditor.editItemDetails(
"test-state:hitbox", "combat:hitbox-window", "{\"hitbox\":\"weapon.alt\"}");
payloadAfterDetails <- actionEditor.getItemPayloadJson("test-state:hitbox");
payloadTextPatch <- actionEditor.setItemPayloadText(
"test-state:hitbox", "hitbox", "weapon.typed");
typedHitbox <- actionEditor.getItemPayloadText(
"test-state:hitbox", "hitbox", "missing");
payloadVectorPatch <- actionEditor.setItemPayloadVector3(
"test-state:hitbox", "positionOffset", 1.0, 2.0, 3.0);
typedPositionY <- actionEditor.getItemPayloadVector(
"test-state:hitbox", "positionOffset", 1, -1.0);
payloadTextListPatch <- actionEditor.setItemPayloadTextList(
"test-state:hitbox", "relatedAssets", "asset://audio/a.wav; asset://audio/b.wav");
typedTextList <- actionEditor.getItemPayloadTextList("test-state:hitbox", "relatedAssets");
invalidPayloadPatch <- actionEditor.setItemPayloadText(
"test-state:hitbox", "hitbox", "");
shapeMismatch <- actionEditor.editItemDetails(
"test-state:hitbox", "combat:damage", "{\"damageType\":\"slash\",\"amount\":2}");
invalidPayload <- actionEditor.editItemDetails(
"test-state:hitbox", "combat:hitbox-window", "{}");
disableResult <- actionEditor.setItemEnabled("test-state:hitbox", false);
disabledValue <- actionEditor.getItemEnabled("test-state:hitbox");
restoreEnabledResult <- actionEditor.undo();
restoredEnabledValue <- actionEditor.getItemEnabled("test-state:hitbox");
removeItemResult <- actionEditor.removeItem("test-state:hitbox");
itemCountAfterRemove <- actionEditor.getItemCount();
restoreRemovedResult <- actionEditor.undo();
itemCountAfterRestore <- actionEditor.getItemCount();
missResult <- actionEditor.pointerDown(700.0, 20.0, false);
draggingAfterMiss <- actionEditor.isDragging();
seekResult <- actionEditor.seekX(700.0);
emptySpaceSeekTime <- actionEditor.getPreviewTime();
actionEditor.seekSeconds(0.0);
actionEditor.play();
advanced <- actionEditor.update(1.0);
previewEventCount <- actionEditor.getEventCount();
snapshotResult <- actionEditor.snapshot();
timelineAbilitySplitA <- actionEditor.addSectionSplit(0.2);
timelineAbilitySplitB <- actionEditor.addSectionSplit(0.6);
snapshotJsonResult <- actionEditor.snapshotJson();
combatRuntime <- eve.Combat().newRuntime().value;
timelineAbility <- combatRuntime.registerTimelineAbility(
"combat-ability:authored-light", snapshotJsonResult.value, 0.25,
"per-execution", "exclusive-replaceable", "Ability.Combat.Attack.Light");
duplicateTimelineAbility <- combatRuntime.registerTimelineAbility(
"combat-ability:authored-light", snapshotJsonResult.value, 0.25,
"per-execution", "exclusive-replaceable", "Ability.Combat.Attack.Light");
invalidTimelineAbility <- combatRuntime.registerTimelineAbility(
"combat-ability:invalid-policy", snapshotJsonResult.value, 0.25,
"per-execution", "invalid", "Ability.Combat.Attack.Light");
invalidTimelineGrant <- combatRuntime.grantAbility("fighter:player", "combat-ability:invalid-policy");
timelineGrant <- combatRuntime.grantAbility("fighter:player", "combat-ability:authored-light");
timelineMatches <- combatRuntime.matchingAbilities("fighter:player", "Ability.Combat.Attack.Light");
timelineActivation <- combatRuntime.activateAbility(timelineGrant.value.grantId, 1);
actionEditor.setSectionSplit(0, 0.3);
actionEditor.setSectionSplit(1, 0.8);
replacementJson <- actionEditor.snapshotJson();
replacedTimelineAbility <- combatRuntime.replaceTimelineAbility(
"combat-ability:authored-light", replacementJson.value, 0.0,
"per-execution", "exclusive-replaceable", "Ability.Combat.Attack.Light");
timelineAbilityAdvance <- combatRuntime.advanceAbilities(2, 1.0);
timelineActiveAfterAdvance <- timelineAbilityAdvance.value.activeCount;
replacementActivation <- combatRuntime.activateAbility(timelineGrant.value.grantId, 3);
replacementAdvance <- combatRuntime.advanceAbilities(4, 0.5);
timelineAbilityAction <- timelineAbility.value.actionId;
timelineAbilityDuration <- timelineAbility.value.durationSeconds;
timelineAbilitySections <- timelineAbility.value.sectionCount;
timelineMatchedGrant <- timelineMatches.value[0];
timelineGrantedId <- timelineGrant.value.grantId;
replacementAbilityAction <- replacedTimelineAbility.value.actionId;
replacementStillActive <- replacementAdvance.value.activeCount;
actionEditor.removeSectionSplit(1);
actionEditor.removeSectionSplit(0);
instantTypeCount <- actionEditor.getInsertableTypeCount(false);
stateTypeCount <- actionEditor.getInsertableTypeCount(true);
damageTypeIndex <- -1;
invulnerabilityTypeIndex <- -1;
for (local i = 0; i < instantTypeCount; ++i)
if (actionEditor.getInsertableType(false, i) == "combat:damage") damageTypeIndex = i;
for (local i = 0; i < stateTypeCount; ++i)
if (actionEditor.getInsertableType(true, i) == "combat:invulnerability-window")
invulnerabilityTypeIndex = i;
damageTypeLabel <- actionEditor.getInsertableTypeLabel(false, damageTypeIndex);
actionEditor.seekSeconds(0.7);
insertedNotify <- actionEditor.addNotifyAtCursor(
"test-track:gameplay", "combat:damage", "{\"damageType\":\"slash\",\"amount\":3}");
insertedState <- actionEditor.addStateAtCursor(
"test-track:gameplay", "combat:invulnerability-window", 0.1, "{}");
invalidInsertedNotify <- actionEditor.addNotifyAtCursor(
"test-track:gameplay", "combat:damage", "{}");
itemCountAfterInsert <- actionEditor.getItemCount();
initialTrackLocked <- actionEditor.getTrackLocked(0);
lockTrack <- actionEditor.setTrackLocked("test-track:gameplay", true);
lockedTrackValue <- actionEditor.getTrackLocked(0);
unlockTrack <- actionEditor.setTrackLocked("test-track:gameplay", false);
renameTrack <- actionEditor.renameTrack("test-track:gameplay", "Combat Events");
renamedTrackLabel <- actionEditor.getTrackLabel(0);
copyTrackResult <- actionEditor.copyTrack("test-track:gameplay");
pasteTrackResult <- actionEditor.pasteTrack();
trackCountAfterPaste <- actionEditor.getTrackCount();
removePastedTrack <- actionEditor.removeTrack(pasteTrackResult.value);
trackCountAfterTrackCleanup <- actionEditor.getTrackCount();
addSectionResult <- actionEditor.addAnimationSection(
"test-section:attack", "asset://test/attack.glb#Attack", 0.0, 0.4, 0.1);
editSectionResult <- actionEditor.editAnimationSection(
"test-section:attack", 0.05, 0.45, 0.08, "asset://test/attack.glb#AttackEdited");
editSectionSourceResult <- actionEditor.editAnimationSectionSource(
"test-section:attack", 0.1, 0.3, "linear");
sectionUri <- actionEditor.getAnimationSectionUri(0);
sectionStart <- actionEditor.getAnimationSectionStart(0);
sectionBlend <- actionEditor.getAnimationSectionBlendIn(0);
sectionSourceStart <- actionEditor.getAnimationSectionSourceStart(0);
sectionCurve <- actionEditor.getAnimationSectionBlendCurve(0);
removeSectionResult <- actionEditor.removeAnimationSection("test-section:attack");
sectionCountAfterCleanup <- actionEditor.getAnimationSectionCount();
montageSettingsResult <- actionEditor.setMontageSettings(
1.5, true, true, 2, 0.12, 0.18, -0.03, true, false, false);
montageRate <- actionEditor.getMontageBasePlayRate();
montageLooping <- actionEditor.getMontageLooping();
montageFootIk <- actionEditor.getMontageFootIk();
montageLayer <- actionEditor.getMontageAnimationLayer();
montageBlendOffset <- actionEditor.getMontageBlendOutOffset();
montageRootRotation <- actionEditor.getMontageRootMotionRotation();
invalidMontageSettings <- actionEditor.setMontageSettings(
0.0, true, true, 2, 0.12, 0.18, -0.03, true, false, false);
undoMontageSettings <- actionEditor.undo();
restoredMontageRate <- actionEditor.getMontageBasePlayRate();
splitCountBefore <- actionEditor.getSectionSplitCount();
addSplit <- actionEditor.addSectionSplit(0.25);
splitTimeAfterAdd <- actionEditor.getSectionSplitTime(0);
moveSplit <- actionEditor.setSectionSplit(0, 0.35);
splitTimeAfterMove <- actionEditor.getSectionSplitTime(0);
duplicateSplit <- actionEditor.addSectionSplit(0.35);
removeSplit <- actionEditor.removeSectionSplit(0);
splitCountAfterRemove <- actionEditor.getSectionSplitCount();
invalidResult <- eve.ActionEditorModule().create("test.asset.invalid", {
schema="eve.action.timeline", schemaVersion=1,
actionId="test:invalid", durationNs=-1,
animationUri="", metadata={}, tracks=[]
});
)"));
CHECK(vm.find("created").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("created").toTable().get<std::string>("ownership"), std::string("owned"));
CHECK(vm.find("externalDamage").toTable().get<bool>("ok"));
CHECK(!vm.find("externalUnknown").toTable().get<bool>("ok"));
CHECK(!vm.find("previewHostAvailable").toBool());
CHECK(vm.find("previewRefresh").toTable().get<bool>("ok"));
CHECK(!vm.find("runtimeRateBeforeStart").toTable().get<bool>("ok"));
CHECK(!vm.find("sectionProgressBeforeStart").toTable().get<bool>("ok"));
CHECK(!vm.find("sectionSyncJumpBeforeStart").toTable().get<bool>("ok"));
CHECK(!vm.find("clearSectionSyncBeforeStart").toTable().get<bool>("ok"));
CHECK(!vm.find("setBoneMaskBeforeStart").toTable().get<bool>("ok"));
CHECK(!vm.find("clearBoneMaskBeforeStart").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("runtimeStateBeforeStart").toString(), std::string("not-started"));
CHECK_EQ(vm.find("runtimeElapsedBeforeStart").toFloat(), 0.0f);
CHECK_EQ(vm.find("runtimeDurationBeforeStart").toFloat(), 0.0f);
CHECK_EQ(vm.find("runtimeLayerBeforeStart").toInt(), -1);
CHECK_EQ(vm.find("runtimeSlotBeforeStart").toInt(), -1);
CHECK(vm.find("configured").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("workspacePanelCount").toInt(), 4);
CHECK(vm.find("viewport").toTable().get<bool>("ok"));
CHECK(vm.find("pointerDownResult").toTable().get<bool>("ok"));
CHECK(vm.find("pointerMoveResult").toTable().get<bool>("ok"));
CHECK(vm.find("pointerUpResult").toTable().get<bool>("ok"));
CHECK(vm.find("movedStart").toFloat() > vm.find("beforeStart").toFloat());
CHECK_EQ(vm.find("revisionAfterDrag").toInt(), 1);
CHECK(vm.find("undoResult").toTable().get<bool>("ok"));
CHECK_LT(std::abs(vm.find("restoredStart").toFloat() - vm.find("beforeStart").toFloat()), 0.0001F);
CHECK(vm.find("inspectorTiming").toTable().get<bool>("ok"));
CHECK_LT(std::abs(vm.find("inspectorStart").toFloat() - 0.1F), 0.0001F);
CHECK_LT(std::abs(vm.find("inspectorEnd").toFloat() - 0.6F), 0.0001F);
CHECK(!vm.find("invalidInspectorTiming").toTable().get<bool>("ok"));
CHECK(vm.find("detailsResult").toTable().get<bool>("ok"));
CHECK_NE(vm.find("payloadAfterDetails").toString().find("weapon.alt"), std::string::npos);
CHECK(vm.find("payloadTextPatch").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("typedHitbox").toString(), std::string("weapon.typed"));
CHECK(vm.find("payloadVectorPatch").toTable().get<bool>("ok"));
CHECK(std::fabs(vm.find("typedPositionY").toFloat() - 2.0f) < 1e-5f);
CHECK(vm.find("payloadTextListPatch").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("typedTextList").toString(),
std::string("asset://audio/a.wav; asset://audio/b.wav"));
CHECK(!vm.find("invalidPayloadPatch").toTable().get<bool>("ok"));
CHECK(!vm.find("shapeMismatch").toTable().get<bool>("ok"));
CHECK(!vm.find("invalidPayload").toTable().get<bool>("ok"));
CHECK(vm.find("disableResult").toTable().get<bool>("ok"));
CHECK(!vm.find("disabledValue").toBool());
CHECK(vm.find("restoreEnabledResult").toTable().get<bool>("ok"));
CHECK(vm.find("restoredEnabledValue").toBool());
CHECK(vm.find("removeItemResult").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("itemCountAfterRemove").toInt(), 0);
CHECK(vm.find("restoreRemovedResult").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("itemCountAfterRestore").toInt(), 1);
CHECK(vm.find("missResult").toTable().get<bool>("ok"));
CHECK(!vm.find("draggingAfterMiss").toBool());
CHECK(vm.find("seekResult").toTable().get<bool>("ok"));
CHECK(vm.find("emptySpaceSeekTime").toFloat() > 0.8F);
CHECK(vm.find("advanced").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("previewEventCount").toInt(), 2);
CHECK(vm.find("snapshotResult").toTable().get<bool>("ok"));
CHECK(vm.find("snapshotJsonResult").toTable().get<bool>("ok"));
CHECK(vm.find("timelineAbilitySplitA").toTable().get<bool>("ok"));
CHECK(vm.find("timelineAbilitySplitB").toTable().get<bool>("ok"));
CHECK(vm.find("timelineAbility").toTable().get<bool>("ok"));
CHECK(!vm.find("duplicateTimelineAbility").toTable().get<bool>("ok"));
CHECK(!vm.find("invalidTimelineAbility").toTable().get<bool>("ok"));
CHECK(!vm.find("invalidTimelineGrant").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("timelineAbilityAction").toString(), std::string("test:light-attack"));
CHECK_LT(std::abs(vm.find("timelineAbilityDuration").toFloat() - 1.0f), 1e-5f);
CHECK_EQ(vm.find("timelineAbilitySections").toInt(), 3);
CHECK_EQ(vm.find("timelineMatchedGrant").toInt(), vm.find("timelineGrantedId").toInt());
CHECK(vm.find("timelineActivation").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("timelineActiveAfterAdvance").toInt(), 0);
CHECK(vm.find("replacementJson").toTable().get<bool>("ok"));
CHECK(vm.find("replacedTimelineAbility").toTable().get<bool>("ok"));
CHECK(vm.find("replacementActivation").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("replacementAbilityAction").toString(), std::string("test:light-attack"));
CHECK_EQ(vm.find("replacementStillActive").toInt(), 1);
CHECK(vm.find("instantTypeCount").toInt() > 0);
CHECK(vm.find("stateTypeCount").toInt() > 0);
CHECK(vm.find("damageTypeIndex").toInt() >= 0);
CHECK(vm.find("invulnerabilityTypeIndex").toInt() >= 0);
CHECK_NE(vm.find("damageTypeLabel").toString().find("Apply Damage"), std::string::npos);
CHECK(vm.find("insertedNotify").toTable().get<bool>("ok"));
CHECK(vm.find("insertedState").toTable().get<bool>("ok"));
CHECK(!vm.find("invalidInsertedNotify").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("itemCountAfterInsert").toInt(), 3);
CHECK(!vm.find("initialTrackLocked").toBool());
CHECK(vm.find("lockTrack").toTable().get<bool>("ok"));
CHECK(vm.find("lockedTrackValue").toBool());
CHECK(vm.find("unlockTrack").toTable().get<bool>("ok"));
CHECK(vm.find("renameTrack").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("renamedTrackLabel").toString(), std::string("Combat Events"));
CHECK(vm.find("copyTrackResult").toTable().get<bool>("ok"));
CHECK(vm.find("pasteTrackResult").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("trackCountAfterPaste").toInt(), 2);
CHECK(vm.find("removePastedTrack").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("trackCountAfterTrackCleanup").toInt(), 1);
CHECK(vm.find("addSectionResult").toTable().get<bool>("ok"));
CHECK(vm.find("editSectionResult").toTable().get<bool>("ok"));
CHECK(vm.find("editSectionSourceResult").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("sectionUri").toString(), std::string("asset://test/attack.glb#AttackEdited"));
CHECK(std::fabs(vm.find("sectionStart").toFloat() - 0.05f) < 1e-5f);
CHECK(std::fabs(vm.find("sectionBlend").toFloat() - 0.08f) < 1e-5f);
CHECK(std::fabs(vm.find("sectionSourceStart").toFloat() - 0.1f) < 1e-5f);
CHECK_EQ(vm.find("sectionCurve").toString(), std::string("linear"));
CHECK(vm.find("removeSectionResult").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("sectionCountAfterCleanup").toInt(), 0);
CHECK(vm.find("montageSettingsResult").toTable().get<bool>("ok"));
CHECK(std::fabs(vm.find("montageRate").toFloat() - 1.5f) < 1e-5f);
CHECK(vm.find("montageLooping").toBool());
CHECK(vm.find("montageFootIk").toBool());
CHECK_EQ(vm.find("montageLayer").toInt(), 2);
CHECK(std::fabs(vm.find("montageBlendOffset").toFloat() + 0.03f) < 1e-5f);
CHECK(!vm.find("montageRootRotation").toBool());
CHECK_EQ(vm.find("splitCountBefore").toInt(), 0);
CHECK(vm.find("addSplit").toTable().get<bool>("ok"));
CHECK(std::fabs(vm.find("splitTimeAfterAdd").toFloat() - 0.25f) < 1e-5f);
CHECK(vm.find("moveSplit").toTable().get<bool>("ok"));
CHECK(std::fabs(vm.find("splitTimeAfterMove").toFloat() - 0.35f) < 1e-5f);
CHECK(!vm.find("duplicateSplit").toTable().get<bool>("ok"));
CHECK(vm.find("removeSplit").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("splitCountAfterRemove").toInt(), 0);
CHECK(!vm.find("invalidMontageSettings").toTable().get<bool>("ok"));
CHECK(vm.find("undoMontageSettings").toTable().get<bool>("ok"));
CHECK(std::fabs(vm.find("restoredMontageRate").toFloat() - 1.0f) < 1e-5f);
CHECK(!vm.find("invalidResult").toTable().get<bool>("ok"));
}
TEST_CASE("editor.actionTimeline.scriptPersistsDocumentAndReportsDirtyState") {
const auto root = std::filesystem::temp_directory_path() /
("eve_action_script_document_" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
std::error_code cleanupError;
std::filesystem::remove_all(root, cleanupError);
{
ssq::VM vm(2048, ssq::Libs::ALL);
eve::ModuleManager::expose(vm);
const std::string source = std::string(R"(
timelineAsset <- {
schema="eve.action.timeline", schemaVersion=1,
actionId="test:persistent", durationNs=1000000000,
animationUri="asset://test/attack.glb#Attack", metadata={},
tracks=[{
id="test-track:gameplay", label="Gameplay", kind="gameplay",
muted=false, locked=false, notifies=[], states=[]
}]
};
editor <- eve.Editor();
module <- eve.ActionEditorModule();
opened <- module.openDocument(")") + root.generic_string() + R"(", "test.asset.persistent", "Persistent Action",
"content://Actions/Persistent.action", timelineAsset);
actionEditor <- opened.value;
backed <- actionEditor.isDocumentBacked();
initiallyDirty <- actionEditor.isDirty();
renameResult <- actionEditor.renameTrack("test-track:gameplay", "Gameplay Saved");
dirtyAfterEdit <- actionEditor.isDirty();
saveResult <- actionEditor.saveDocument();
dirtyAfterSave <- actionEditor.isDirty();
reopened <- module.openDocument(")" + root.generic_string() + R"(", "test.asset.reopened", "Reopened Action",
"content://Actions/Persistent.action", timelineAsset);
reopenedLabel <- reopened.value.getTrackLabel(0);
)";
vm.run(vm.compileSource(source.c_str()));
CHECK(vm.find("opened").toTable().get<bool>("ok"));
CHECK(vm.find("backed").toBool());
CHECK(!vm.find("initiallyDirty").toBool());
CHECK(vm.find("renameResult").toTable().get<bool>("ok"));
CHECK(vm.find("dirtyAfterEdit").toBool());
CHECK(vm.find("saveResult").toTable().get<bool>("ok"));
CHECK(!vm.find("dirtyAfterSave").toBool());
CHECK(vm.find("reopened").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("reopenedLabel").toString(), std::string("Gameplay Saved"));
const std::string catalogSource = std::string(R"(
catalogCreated <- module.createAssetCatalog(")") + root.generic_string() + R"(");
catalog <- catalogCreated.value;
catalogRegister <- catalog.registerDocument(
"test.asset.persistent", "content://Actions/Persistent.action");
catalogRefresh <- catalog.refresh("persistent");
catalogCount <- catalog.getAssetCount();
catalogGuid <- catalog.getAssetGuid(0);
catalogUri <- catalog.getAssetUri(0);
catalogTitle <- catalog.getAssetTitle(0);
catalogGeneration <- catalog.getGeneration();
catalogEmptySearch <- catalog.refresh("missing-query");
catalogEmptyCount <- catalog.getAssetCount();
)";
vm.run(vm.compileSource(catalogSource.c_str()));
CHECK(vm.find("catalogCreated").toTable().get<bool>("ok"));
CHECK(vm.find("catalogRegister").toTable().get<bool>("ok"));
CHECK(vm.find("catalogRefresh").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("catalogCount").toInt(), 1);
CHECK_EQ(vm.find("catalogGuid").toString(), std::string("test.asset.persistent"));
CHECK_EQ(vm.find("catalogUri").toString(), std::string("content://Actions/Persistent.action"));
CHECK_EQ(vm.find("catalogTitle").toString(), std::string("Persistent"));
CHECK(vm.find("catalogGeneration").toInt() > 0);
CHECK(vm.find("catalogEmptySearch").toTable().get<bool>("ok"));
CHECK_EQ(vm.find("catalogEmptyCount").toInt(), 0);
}
CHECK(std::filesystem::is_regular_file(root / "Content" / "Actions" / "Persistent.action"));
std::filesystem::remove_all(root, cleanupError);
}