// The following code contains unit test function that test the functionality
// of the above classes and functions.
// Non evaluated selftest.
function test0() {
let log = new Log();
new Task('0.1').run(log);
new Task('0.2', State.Failure).run(log);
new Task('0.3', State.Success).run(log);
let actual = log.toString();
let expected = '\
Task(0.1)\n\
└ Undefined\n\
Task(0.2)\n\
└ Failure\n\
Task(0.3)\n\
└ Success\n\
';
compareStringsAndReport('Test 0', actual, expected, true);
}
// Test 1 checks the CheckState task. It should return success if the state of the
// checked task matches the expected state, and failure otherwise.
function test1() {
let log = new Log();
new CheckState(new Task('1.1', State.Success).run(), State.Success).run(log);
new CheckState(new Task('1.2', State.Success).run(), State.Failure).run(log);
let actual = log.toString();
let expected = '\
CheckState(1.1,Success)\n\
└ Success\n\
CheckState(1.2,Failure)\n\
└ Failure\n\
';
compareStringsAndReport('Test 1', actual, expected);
}
// Test 2 checks the Selector composite. It should return success if at least
// one of its children returns success, and failure otherwise. The first
// selector should return failure because it has no children, while the
// second selector should return success because the second child returns success.
function test2() {
let log = new Log();
new Selector().run(log);
new Selector
(
[
new Task('2.1', State.Failure),
new Task('2.2', State.Success),
new Task('2.3', State.Success)
]
).run(log);
let actual = log.toString();
let expected = '\
Selector()\n\
└ Failure\n\
Selector()\n\
│ Task(2.1)\n\
│ └ Failure\n\
│ Task(2.2)\n\
│ └ Success\n\
└ Success\n\
';
compareStringsAndReport('Test 2', actual, expected);
}
// Test 3 checks the Sequence composite. It should return failure if at least
// one of its children returns failure, and success otherwise. The first
// sequence should return success because it has no children, while the second
// sequence should return failure because the third child returns failure.
function test3() {
let log = new Log();
new Sequence([]).run(log);
new Sequence
(
[
new Task('3.1', State.Success),
new Task('3.2', State.Success),
new Task('3.3', State.Failure),
new Task('3.4', State.Success),
new Task('3.5', State.Success)
]
).run(log);
let actual = log.toString();
let expected = '\
Sequence()\n\
└ Success\n\
Sequence()\n\
│ Task(3.1)\n\
│ └ Success\n\
│ Task(3.2)\n\
│ └ Success\n\
│ Task(3.3)\n\
│ └ Failure\n\
└ Failure\n\
';
compareStringsAndReport('Test 3', actual, expected);
}
// Test 4 checks the RandomSelector composite. It should return the state of a
// random child. Since all children return success, the random selector should
// always return success.
function test4() {
let log = new Log();
new RandomSelector
(
[
new Task('4', State.Success),
new Task('4', State.Success),
new Task('4', State.Success)
]
).run(log);
let actual = log.toString();
let expected = '\
RandomSelector()\n\
│ Task(4)\n\
│ └ Success\n\
└ Success\n\
';
compareStringsAndReport('Test 4', actual, expected);
}
// Test 5 checks the Inverter decorator. It should return the inverted state of
// its child. If the child returns success, the inverter should return failure,
// and vice versa.
function test5() {
let log = new Log();
new Inverter(new Task('5.1', State.Success)).run(log);
new Inverter(new Task('5.2', State.Failure)).run(log);
let actual = log.toString();
let expected = '\
Inverter()\n\
│ Task(5.1)\n\
│ └ Success\n\
└ Failure\n\
Inverter()\n\
│ Task(5.2)\n\
│ └ Failure\n\
└ Success\n\
';
compareStringsAndReport('Test 5', actual, expected);
}
// Test 6 checks the Succeeder decorator. It should always return success,
// irrespective of what the child node actually returned.
function test6() {
let log = new Log();
new Succeeder(new Task('6.1', State.Success)).run(log);
new Succeeder(new Task('6.2', State.Failure)).run(log);
let actual = log.toString();
let expected = '\
Succeeder()\n\
│ Task(6.1)\n\
│ └ Success\n\
└ Success\n\
Succeeder()\n\
│ Task(6.2)\n\
│ └ Failure\n\
└ Success\n\
';
compareStringsAndReport('Test 6', actual, expected);
}
// Test 7 checks the Repeater decorator. It should reprocess its child node each
// time its child returns a result. The first repeater should return success
// immediately because it has no child, while the second repeater should return
// success after running its child three times.
function test7() {
let log = new Log();
new Repeater().run(log);
new Repeater(new Task('7', State.Success), 3).run(log);
let actual = log.toString();
let expected = '\
Repeater(0)\n\
└ Success\n\
Repeater(3)\n\
│ Task(7)\n\
│ └ Success\n\
│ Task(7)\n\
│ └ Success\n\
│ Task(7)\n\
│ └ Success\n\
└ Success\n\
';
compareStringsAndReport('Test 7', actual, expected);
}
// Test 8 checks the Repeat_until_fail decorator. It should repeatedly run its child
// node until the child returns failure. The first repeat_until_fail should return
// success immediately because it has no child, while the second repeat_until_fail
// should return success after its child returns failure.
function test8() {
let log = new Log();
new Repeat_until_fail().run(log);
new Repeat_until_fail(new Task('8', State.Failure)).run(log);
let actual = log.toString();
let expected = '\
Repeat_until_fail()\n\
└ Success\n\
Repeat_until_fail()\n\
│ Task(8)\n\
│ └ Failure\n\
└ Success\n\
';
compareStringsAndReport('Test 8', actual, expected);
}
// Test 9 checks a complex behavior tree that combines several composites and
// decorators. The tree should return success because the second child of the
// selector returns success, even though the first child of the selector returns
// failure due to the inverters.
function test9() {
let log = new Log();
new Sequence
(
[
new Task('9.1', State.Success),
new Selector
(
[
new Sequence
(
[
new Inverter(new Task('9.2', State.Success)),
new Inverter(new Task('9.3', State.Success))
]
),
new Sequence
(
[
new Task('9.4', State.Success),
new Task('9.5', State.Success)
]
)
]
)
]
).run(log);
let actual = log.toString();
let expected = '\
Sequence()\n\
│ Task(9.1)\n\
│ └ Success\n\
│ Selector()\n\
│ │ Sequence()\n\
│ │ │ Inverter()\n\
│ │ │ │ Task(9.2)\n\
│ │ │ │ └ Success\n\
│ │ │ └ Failure\n\
│ │ └ Failure\n\
│ │ Sequence()\n\
│ │ │ Task(9.4)\n\
│ │ │ └ Success\n\
│ │ │ Task(9.5)\n\
│ │ │ └ Success\n\
│ │ └ Success\n\
│ └ Success\n\
└ Success\n\
';
compareStringsAndReport('Test 9', actual, expected);
}
// Same test as 9 but using named references to tasks.
function test10() {
let log = new Log();
let p10_1 = new Task('10.1', State.Success);
let p10_2 = new Task('10.2', State.Success);
let p10_3 = new Task('10.3', State.Success);
let p10_4 = new Task('10.4', State.Success);
let p10_5 = new Task('10.5', State.Success);
new Sequence
(
[
p10_1,
new Selector
(
[
new Sequence
(
[
new Inverter(p10_2),
new Inverter(p10_3)
]
),
new Sequence
(
[
p10_4,
p10_5
]
)
]
)
]
).run(log);
let actual = log.toString();
let expected = '\
Sequence()\n\
│ Task(10.1)\n\
│ └ Success\n\
│ Selector()\n\
│ │ Sequence()\n\
│ │ │ Inverter()\n\
│ │ │ │ Task(10.2)\n\
│ │ │ │ └ Success\n\
│ │ │ └ Failure\n\
│ │ └ Failure\n\
│ │ Sequence()\n\
│ │ │ Task(10.4)\n\
│ │ │ └ Success\n\
│ │ │ Task(10.5)\n\
│ │ │ └ Success\n\
│ │ └ Success\n\
│ └ Success\n\
└ Success\n\
';
compareStringsAndReport('Test 10', actual, expected);
}