Skip to content

Commit

Permalink
Added Exit specs and fixed exit details arg
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikolas Howard committed Feb 28, 2024
1 parent cf068a5 commit dc312de
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 11 deletions.
2 changes: 1 addition & 1 deletion dist/bundle.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dist/bundle.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dist/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/attributes/callbacks/Exit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ export default class Exit extends Callback {
}

// Call the callback function
callbackFuncInvoker([{ value: { succeeded: isSuccess, aborted: isAborted } }, ...this.args]);
callbackFuncInvoker([{ succeeded: isSuccess, aborted: isAborted }, ...this.args]);
};
}
6 changes: 3 additions & 3 deletions test/attributes/callbacks/Entry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe("An Entry callback node attribute", () => {
assert.throws(
() => new BehaviourTree(definition, {}),
Error,
"expected agent function or registered function name identifier argument for attribute"
"invalid definition: expected agent function or registered function name identifier argument for attribute"
);
});

Expand All @@ -28,7 +28,7 @@ describe("An Entry callback node attribute", () => {
assert.throws(
() => new BehaviourTree(definition, {}),
Error,
"expected 'call' property for attribute 'entry' to be a non-empty string for 'action' node at depth '1'"
"invalid definition: expected 'call' property for attribute 'entry' to be a non-empty string for 'action' node at depth '1'"
);
});
});
Expand Down Expand Up @@ -60,7 +60,7 @@ describe("An Entry callback node attribute", () => {
assert.isTrue(agent.onEntry.calledTwice);
});

it("(MDSL)", () => {
it("(JSON)", () => {
const definition: RootNodeDefinition = {
type: "root",
entry: {
Expand Down
234 changes: 233 additions & 1 deletion test/attributes/callbacks/Exit.spec.ts
Original file line number Diff line number Diff line change
@@ -1 +1,233 @@
describe("An Exit callback node attribute", () => {});
import { assert } from "chai";
import sinon from "sinon";

import { BehaviourTree, State } from "../../../src/index";
import { RootNodeDefinition } from "../../../src/BehaviourTreeDefinition";

describe("An Exit callback node attribute", () => {
describe("on tree initialisation", () => {
describe("will error if no function name is defined", () => {
it("(MDSL)", () => {
const definition = "root { action [noop] exit() }";
assert.throws(
() => new BehaviourTree(definition, {}),
Error,
"invalid definition: expected agent function or registered function name identifier argument for attribute"
);
});

it("(JSON)", () => {
const definition: RootNodeDefinition = {
type: "root",
child: {
type: "action",
call: "noop",
exit: {} as any
}
};
assert.throws(
() => new BehaviourTree(definition, {}),
Error,
"invalid definition: expected 'call' property for attribute 'exit' to be a non-empty string for 'action' node at depth '1'"
);
});
});
});

describe("when the node is updated as part of a tree step will call the exit function when the node moves out of the RUNNING state", () => {
describe("and to a SUCCEEDED state", () => {
it("(MDSL)", () => {
const definition = `root exit(onExit, "root") { action [someAction] exit(onExit, "action") }`;
const agent = {
someAction: () => State.RUNNING,
onExit: sinon.stub()
};
const tree = new BehaviourTree(definition, agent);

tree.step();

assert.isNotTrue(agent.onExit.called);
assert.isTrue(tree.isRunning());

agent.someAction = () => State.SUCCEEDED;

tree.step();

assert.isFalse(tree.isRunning());
assert.isTrue(agent.onExit.calledTwice);
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: true, aborted: false }), "root"));
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: true, aborted: false }), "action"));
});

it("(JSON)", () => {
const definition: RootNodeDefinition = {
type: "root",
exit: {
call: "onExit",
args: ["root"]
},
child: {
type: "action",
exit: {
call: "onExit",
args: ["action"]
},
call: "someAction"
}
};
const agent = {
someAction: () => State.RUNNING,
onExit: sinon.stub()
};
const tree = new BehaviourTree(definition, agent);

tree.step();

assert.isNotTrue(agent.onExit.called);
assert.isTrue(tree.isRunning());

agent.someAction = () => State.SUCCEEDED;

tree.step();

assert.isFalse(tree.isRunning());
assert.isTrue(agent.onExit.calledTwice);
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: true, aborted: false }), "root"));
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: true, aborted: false }), "action"));
});
});

describe("and to a FAILED state when the node execution", () => {
describe("was not aborted", () => {
it("(MDSL)", () => {
const definition = `root exit(onExit, "root") { action [someAction] exit(onExit, "action") }`;
const agent = {
someAction: () => State.RUNNING,
onExit: sinon.stub()
};
const tree = new BehaviourTree(definition, agent);

tree.step();

assert.isNotTrue(agent.onExit.called);
assert.isTrue(tree.isRunning());

agent.someAction = () => State.FAILED;

tree.step();

assert.isFalse(tree.isRunning());
assert.isTrue(agent.onExit.calledTwice);
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: false }), "root"));
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: false }), "action"));
});

it("(JSON)", () => {
const definition: RootNodeDefinition = {
type: "root",
exit: {
call: "onExit",
args: ["root"]
},
child: {
type: "action",
exit: {
call: "onExit",
args: ["action"]
},
call: "someAction"
}
};
const agent = {
someAction: () => State.RUNNING,
onExit: sinon.stub()
};
const tree = new BehaviourTree(definition, agent);

tree.step();

assert.isNotTrue(agent.onExit.called);
assert.isTrue(tree.isRunning());

agent.someAction = () => State.FAILED;

tree.step();

assert.isFalse(tree.isRunning());
assert.isTrue(agent.onExit.calledTwice);
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: false }), "root"));
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: false }), "action"));
});
});

describe("was aborted", () => {
it("(MDSL)", () => {
const definition = `root exit(onExit, "root") { action [someAction] exit(onExit, "action") while(someCondition) }`;
const agent = {
someAction: () => State.RUNNING,
someCondition: () => true,
onExit: sinon.stub()
};
const tree = new BehaviourTree(definition, agent);

tree.step();

assert.isNotTrue(agent.onExit.called);
assert.isTrue(tree.isRunning());

// Cause the running action node to be aborted on the next tree step.
agent.someCondition = () => false;

tree.step();

assert.isFalse(tree.isRunning());
assert.isTrue(agent.onExit.calledTwice);
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: false }), "root"));
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: true }), "action"));
});

it("(JSON)", () => {
const definition: RootNodeDefinition = {
type: "root",
exit: {
call: "onExit",
args: ["root"]
},
child: {
type: "action",
exit: {
call: "onExit",
args: ["action"]
},
while: {
call: "someCondition"
},
call: "someAction"
}
};
const agent = {
someAction: () => State.RUNNING,
someCondition: () => true,
onExit: sinon.stub()
};
const tree = new BehaviourTree(definition, agent);

tree.step();

assert.isNotTrue(agent.onExit.called);
assert.isTrue(tree.isRunning());

// Cause the running action node to be aborted on the next tree step.
agent.someCondition = () => false;

tree.step();

assert.isFalse(tree.isRunning());
assert.isTrue(agent.onExit.calledTwice);
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: false }), "root"));
assert.isTrue(agent.onExit.calledWith(sinon.match({ succeeded: false, aborted: true }), "action"));
});
});
});
});
});

0 comments on commit dc312de

Please sign in to comment.