Pyrogenesis  trunk
SimulationDocs.h
Go to the documentation of this file.
1 /* Copyright (C) 2017 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19 
20 @page writing-components How to write components
21 
22 <i>See the <a href="http://trac.wildfiregames.com/wiki/TDD_Simulation">Trac wiki</a> for more documentation about this system.</i>
23 
24 <!--
25  egrep '@(sub)*section' source/simulation2/docs/SimulationDocs.h|sed 's/@//; s/section/- @ref/; s/^sub/ /g; s/\(- \S* \S*\).*$/\1/'
26 -->
27 
28 - @ref defining-cpp-interfaces
29 - @ref script-wrapper
30 - @ref script-conversions
31 - @ref defining-cpp-components
32  - @ref messages
33  - @ref component-creation
34  - @ref schema
35  - @ref system-components
36 - @ref allowing-js-interfaces
37 - @ref defining-js-components
38 - @ref defining-js-interfaces
39 - @ref defining-cpp-message
40 - @ref defining-js-message
41 - @ref communication
42  - @ref message-passing
43  - @ref query-interface
44 - @ref testing
45  - @ref testing-cpp
46  - @ref testing-js
47 
48 @section defining-cpp-interfaces Defining interfaces in C++
49 
50 Think of a name for the component. We'll use "Example" in this example; replace
51 it with your chosen name in all the filenames and code samples below.
52 
53 (If you copy-and-paste from the examples below, be aware that the
54 <a href="http://trac.wildfiregames.com/wiki/Coding_Conventions">coding conventions</a>
55 require indentation with tabs, not spaces, so make sure you get it right.)
56 
57 Create the file @b simulation2/components/ICmpExample.h:
58 
59 @include ICmpExample.h
60 
61 This defines the interface that C++ code will use to access components.
62 
63 Create the file @b simulation2/components/ICmpExample.cpp:
64 
65 @include ICmpExample.cpp
66 
67 This defines a JavaScript wrapper, so that scripts can access methods of components
68 implementing that interface. See @ref script-wrapper for details.
69 
70 This wrapper should only contain methods that are safe to access from simulation scripts:
71 they must not crash (even with invalid or malicious inputs), they must return deterministic
72 results, etc.
73 Methods that are intended for use solely by C++ should not be listed here.
74 
75 Every interface must define a script wrapper with @c BEGIN_INTERFACE_WRAPPER,
76 though in some cases they might be empty and not define any methods.
77 
78 Now update the file simulation2/TypeList.h and add
79 
80 @code
81 INTERFACE(Example)
82 @endcode
83 
84 TypeList.h is used for various purposes - it will define the interface ID number @c IID_Example
85 (in both C++ and JS), and it will hook the new interface into the interface registration system.
86 
87 Remember to run the @c update-workspaces script after adding or removing any source files,
88 so that they will be added to the makefiles or VS projects.
89 
90 
91 
92 @section script-wrapper Interface method script wrappers
93 
94 Interface methods are defined with the macro:
95 
96  <code>DEFINE_INTERFACE_METHOD_<var>NumberOfArguments</var>(<var>"MethodName"</var>,
97  <var>ReturnType</var>, ICmpExample, <var>MethodName</var>, <var>ArgType0</var>, <var>ArgType1</var>, ...)</code>
98 
99 corresponding to the C++ method
100 <code><var>ReturnType</var> ICmpExample::<var>MethodName</var>(<var>ArgType0</var>, <var>ArgType1</var>, ...)</code>
101 
102 For methods exposed to scripts like this, the arguments should be simple types and pass-by-value.
103 E.g. use <code>std::wstring</code> arguments, not <code>const std::wstring&</code>.
104 
105 The arguments and return types will be automatically converted between C++ and JS values.
106 To do this, @c ToJSVal<ReturnType> and @c FromJSVal<ArgTypeN> must be defined (if they
107 haven't already been defined for another method), as described below.
108 
109 The two <var>MethodName</var>s don't have to be the same - in rare cases you might want to expose it as
110 @c DoWhatever to scripts but link it to the @c ICmpExample::DoWhatever_wrapper() method
111 which does some extra conversions or checks or whatever.
112 
113 There's a small limit to the number of arguments that are currently supported - if you need more,
114 first try to save yourself some pain by using fewer arguments, otherwise you'll need to add a new
115 macro into simulation2/system/InterfaceScripted.h and increase @ref SCRIPT_INTERFACE_MAX_ARGS in scriptinterface/ScriptInterface.h.
116 (Not sure if anything else needs changing.)
117 
118 
119 
120 @section script-conversions Script type conversions
121 
122 In most cases you can skip this section.
123 But if you define a script-accessible method with new types without having defined conversions,
124 you'll probably get mysterious linker errors that mention @c ToJSVal or @c FromJSVal.
125 First, work out where the conversion should be defined.
126 Basic data types (integers, STL containers, etc) go in scriptinterface/ScriptConversions.cpp.
127 Non-basic data types from the game engine typically go in simulation2/scripting/EngineScriptConversions.cpp.
128 (They could go in different files if that turns out to be cleaner - it doesn't matter where they're
129 defined as long as the linker finds them).
130 
131 To convert from a C++ type @c T to a JS value, define:
132 
133 @code
134 template<> jsval ScriptInterface::ToJSVal<T>(JSContext* cx, T const& val)
135 {
136  ...
137 }
138 @endcode
139 
140 Use the standard <a href="https://developer.mozilla.org/en/JSAPI_Reference">SpiderMonkey JSAPI functions</a>
141 to do the conversion (possibly calling @c ToJSVal recursively).
142 On error, you should return @c JSVAL_VOID (JS's @c undefined value) and probably report an error message somehow.
143 Be careful about JS garbage collection (don't let it collect the objects you're constructing before you return them).
144 
145 To convert from a JS value to a C++ type @c T, define:
146 
147 @code
148 template<> bool ScriptInterface::FromJSVal<T>(JSContext* cx, jsval v, T& out)
149 {
150  ...
151 }
152 @endcode
153 
154 On error, return @c false (doesn't matter what you do with @c out).
155 On success, return @c true and put the value in @c out.
156 Still need to be careful about garbage collection (@c v is rooted, but it might have getters
157 that execute arbitrary code and return unrooted values when you access properties,
158 so don't let them be collected before you've finished using them).
159 
160 
161 
162 @section defining-cpp-components Defining component types in C++
163 
164 Now we want to implement the @c Example interface.
165 We need a name for the component type - if there's only ever going to be one implementation of the interface,
166 we might as well call it @c Example too.
167 If there's going to be more than one, they should have distinct names like @c ExampleStatic and @c ExampleMobile etc.
168 
169 Create @b simulation2/components/CCmpExample.cpp:
170 
171 \include CCmpExample.cpp
172 
173 The only optional methods are @c HandleMessage and @c GetSchema - all others must be defined.
174 
175 Update the file simulation2/TypeList.h and add:
176 
177 @code
178 COMPONENT(Example)
179 @endcode
180 
181 
182 @subsection messages Message handling
183 
184 First you need to register for all the message types you want to receive, in @c ClassInit:
185 
186 @code
187 static void ClassInit(CComponentManager& componentManager)
188 {
189  componentManager.SubscribeToMessageType(CID_Example, MT_Update);
190  ...
191 }
192 @endcode
193 
194 (@c CID_Example is derived from the name of the component type, @em not the name of the interface.)
195 
196 You can also use SubscribeGloballyToMessageType, to intercept messages sent with PostMessage
197 that are targeted at a @em different entity. (Typically this is used by components that want
198 to hear about all MT_Destroy messages.)
199 
200 Then you need to respond to the messages in @c HandleMessage:
201 
202 @code
203 virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
204 {
205  switch (msg.GetType())
206  {
207  case MT_Update:
208  {
209  const CMessageUpdate& msgData = static_cast<const CMessageUpdate&> (msg);
210  Update(msgData.turnLength); // or whatever processing you want to do
211  break;
212  }
213  }
214 }
215 @endcode
216 
217 The CMessage structures are defined in simulation2/MessageTypes.h. Be very careful that you're casting @c msg to the right type.
218 
219 
220 @subsection component-creation Component creation
221 
222 Component type instances go through one of two lifecycles:
223 
224 @code
225 CCmpExample();
226 Init(paramNode);
227 // any sequence of HandleMessage and Serialize and interface methods
228 Deinit();
229 ~CCmpExample();
230 @endcode
231 
232 @code
233 CCmpExample();
234 Deserialize(paramNode, deserialize);
235 // any sequence of HandleMessage and Serialize and interface methods
236 Deinit();
237 ~CCmpExample();
238 @endcode
239 
240 The order of <code>Init</code>/<code>Deserialize</code>/<code>Deinit</code> between entities is mostly undefined,
241 so they must not rely on other entities or components already existing; @em except that the @c SYSTEM_ENTITY is
242 created before anything else and therefore may be used, and that the components for a single entity will be
243 processed in the order determined by TypeList.h.
244 
245 In a typical component:
246 
247 - The constructor should do very little, other than perhaps initialising some member variables -
248  usually the default constructor is fine so there's no need to write one.
249 - @c Init should parse the @c paramNode (the data from the entity template) and store any needed data in member variables.
250 - @c Deserialize should often explicitly call @c Init first (to load the original template data), and then read any instance-specific data from the deserializer.
251 - @c Deinit should clean up any resources allocated by @c Init / @c Deserialize.
252 - The destructor should clean up any resources allocated by the constructor - usually there's no need to write one.
253 
254 
255 @subsection schema Component XML schemas
256 
257 The @c paramNode passed to @c Init is constructed from XML entity template definition files.
258 Components should define a schema, which is used for several purposes:
259 
260 - Documentation of the XML structure expected by the component.
261 - Automatic error checking that the XML matches the expectation, so the component doesn't have to do error checking itself.
262 - (Hopefully at some point in the future) Automatic generation of editing tool UI.
263 
264 @c GetSchema must return a Relax NG fragment, which will be used to construct a single global schema file.
265 (You can run the game with the @c -dumpSchema command-line argument to see the schema. Do not forget to also specify the used mods with @c -mod=<mod>.).
266 The <a href="http://relaxng.org/tutorial-20011203.html">official tutorial</a> describes most of the details
267 of the RNG language.
268 
269 In simple cases, you would write something like:
270 @code
271 static std::string GetSchema()
272 {
273  return
274  "<element name='Name'><text/></element>"
275  "<element name='Height'><data type='nonNegativeInteger'/></element>"
276  "<optional>"
277  "<element name='Eyes'><empty/></element>"
278  "</optional>";
279  }
280 }
281 @endcode
282 i.e. a single string (C++ automatically concatenates the quoted lines) which defines a list of elements,
283 corresponding to an entity template XML file like:
284 @code
285 <Entity>
286  <Example>
287  <Name>Barney</Name>
288  <Height>235</Height>
289  <Eyes/>
290  </Example>
291  <!-- ... other components ... -->
292 </Entity>
293 @endcode
294 
295 In the schema, each <code>&lt;element></code> has a name and some content.
296 The content will typically be one of:
297 - <code>&lt;empty/></code>
298 - <code>&lt;text/></code>
299 - <code>&lt;data type='boolean'/></code>
300 - <code>&lt;data type='decimal'/></code>
301 - <code>&lt;data type='integer'/></code>
302 - <code>&lt;data type='nonNegativeInteger'/></code>
303 - <code>&lt;data type='positiveInteger'/></code>
304 - <code>&lt;ref name='decimal'/></code>
305 - <code>&lt;ref name='nonNegativeDecimal'/></code>
306 - <code>&lt;ref name='positiveDecimal'/></code>
307 
308 
309 The <code>&lt;data&gt;</code> elements are native elements, while the <code>&lt;ref&gt;</code> elements are elements added for our engine. These non-native elements allow the definition of an operation that depends on the parent template. Possible operations are "add" and "mul", and can be applied as the example below.
310 
311 Say the parent template is
312 @code
313 <Entity>
314  <Example>
315  <Name>Semi-Humanoids</Name>
316  <Height>9000</Height>
317  <Eyes/>
318  </Example>
319  <!-- ... other components ... -->
320 </Entity>
321 @endcode
322 and the child template appears like
323 @code
324 <Entity>
325  <Example>
326  <Name>Barney</Name>
327  <Height op="add">5</Height>
328  <Eyes/>
329  </Example>
330  <!-- ... other components ... -->
331 </Entity>
332 @endcode
333 then Barney would have a height of 9005.
334 
335 Elements can be wrapped in <code>&lt;optional></code>.
336 Groups of elements can be wrapped in <code>&lt;choice></code> to allow only one of them.
337 The content of an <code>&lt;element></code> can be further nested elements, but note that
338 elements may be reordered when loading an entity template:
339 if you specify a sequence of elements it should be wrapped in <code>&lt;interleave></code>,
340 so the schema checker will ignore reorderings of the sequence.
341 
342 For early development of a new component, you can set the schema to <code>&lt;ref name='anything'/></code> to allow any content.
343 If you don't define @c GetSchema, then the default is <code>&lt;empty/></code> (i.e. there must be no elements).
344 
345 
346 @subsection system-components System components
347 
348 System components are global singleton components of the @c SYSTEM_ENTITY.
349 These are added to it in @c CComponentManager::AddSystemComponents, and
350 are passed an empty @c paramNode on @c Init.
351 
352 JS system components can be registered using:
353 
354 @code
355 Engine.RegisterSystemComponentType(IID_ExampleSystem, "ExampleSystem", ExampleSystem);
356 @endcode
357 
358 
359 @section allowing-js-interfaces Allowing interfaces to be implemented in JS
360 
361 If we want to allow both C++ and JS implementations of @c ICmpExample,
362 we need to define a special component type that proxies all the C++ methods to the script.
363 Add the following to @b ICmpExample.cpp:
364 
365 @code
366 #include "simulation2/scripting/ScriptComponent.h"
367 
368 // ...
369 
370 class CCmpExampleScripted : public ICmpExample
371 {
372 public:
373  DEFAULT_SCRIPT_WRAPPER(ExampleScripted)
374 
375  virtual int DoWhatever(int x, int y)
376  {
377  return m_Script.Call<int>("DoWhatever", x, y);
378  }
379 };
380 
381 REGISTER_COMPONENT_SCRIPT_WRAPPER(ExampleScripted)
382 @endcode
383 
384 Then add to TypeList.h:
385 
386 @code
387 COMPONENT(ExampleScripted)
388 @endcode
389 
390 @c m_Script.Call takes the return type as a template argument,
391 then the name of the JS function to call and the list of parameters.
392 You could do extra conversion work before calling the script, if necessary.
393 You need to make sure the types are handled by @c ToJSVal and @c FromJSVal (as discussed before) as appropriate.
394 
395 
396 
397 @section defining-js-components Defining component types in JS
398 
399 Now we want a JS implementation of ICmpExample.
400 Think up a new name for this component, like @c ExampleTwo (but more imaginative).
401 Then write @b binaries/data/mods/public/simulation/components/ExampleTwo.js:
402 
403 @code
404 function ExampleTwo() {}
405 
406 ExampleTwo.prototype.Schema = "<ref name='anything'/>";
407 
408 ExampleTwo.prototype.Init = function() {
409  ...
410 };
411 
412 ExampleTwo.prototype.Deinit = function() {
413  ...
414 };
415 
416 ExampleTwo.prototype.OnUpdate = function(msg) {
417  ...
418 };
419 
420 Engine.RegisterComponentType(IID_Example, "ExampleTwo", ExampleTwo);
421 @endcode
422 
423 This uses JS's @em prototype system to create what is effectively a class, called @c ExampleTwo.
424 (If you wrote <code>new ExampleTwo()</code>, then JS would construct a new object which inherits from
425 @c ExampleTwo.prototype, and then would call the @c ExampleTwo function with @c this set to the new object.
426 "Inherit" here means that if you read a property (or method) of the object, which is not defined in the object,
427 then it will be read from the prototype instead.)
428 
429 @c Engine.RegisterComponentType tells the engine to start using the JS class @c ExampleTwo,
430 exposed (in template files etc) with the name "ExampleTwo", and implementing the interface ID @c IID_Example
431 (i.e. the ICmpExample interface).
432 
433 The @c Init and @c Deinit functions are optional. Unlike C++, there are no @c Serialize/Deserialize functions -
434 each JS component instance is automatically serialized and restored.
435 (This automatic serialization restricts what you can store as properties in the object - e.g. you cannot store function closures,
436 because they're too hard to serialize. This will serialize Strings, numbers, bools, null, undefined, arrays of serializable
437 values whose property names are purely numeric, objects whose properties are serializable values. Cyclic structures are allowed.)
438 
439 Instead of @c ClassInit and @c HandleMessage, you simply add functions of the form <code>On<var>MessageType</var></code>.
440 (If you want the equivalent of SubscribeGloballyToMessageType, then use <code>OnGlobal<var>MessageType</var></code> instead.)
441 When you call @c RegisterComponentType, it will find all such functions and automatically subscribe to the messages.
442 The @c msg parameter is usually a straightforward mapping of the relevant CMessage class onto a JS object
443 (e.g. @c OnUpdate can read @c msg.turnLength).
444 
445 
446 
447 @section defining-js-interfaces Defining interface types in JS
448 
449 If an interface is only ever used by JS components, and never implemented or called directly by C++ components,
450 then you don't need to do all of the work with defining ICmpExample.
451 Simply create a file @b binaries/data/mods/public/simulation/components/interfaces/Example.js:
452 
453 @code
454 Engine.RegisterInterface("Example");
455 @endcode
456 
457 You can then use @c IID_Example in JS components.
458 
459 (There's no strict requirement to have a single .js file per interface definition,
460 it's just a convention that allows mods to easily extend the game with new interfaces.)
461 
462 
463 
464 @section defining-cpp-message Defining a new message type in C++
465 
466 Think of a name. We'll use @c Example again. (The name should typically be a present-tense verb, possibly
467 with a prefix to make its meaning clearer: "Update", "TurnStart", "RenderSubmit", etc).
468 
469 Add to TypeList.h:
470 
471 @code
472 MESSAGE(Example)
473 @endcode
474 
475 Add to MessageTypes.h:
476 
477 @code
478 class CMessageExample : public CMessage
479 {
480 public:
481  DEFAULT_MESSAGE_IMPL(Example)
482 
483  CMessageExample(int x, int y) :
484  x(x), y(y)
485  {
486  }
487 
488  int x;
489  int y;
490 };
491 @endcode
492 
493 containing the data fields that are associated with the message. (In some cases there may be no fields.)
494 
495 (If there are too many message types, MessageTypes.h could be split into multiple files with better organisation.
496 But for now everything is put in there.)
497 
498 Now you have to add C++/JS conversions into MessageTypeConversions.cpp, so scripts can send and receive messages:
499 
500 @code
501 jsval CMessageExample::ToJSVal(ScriptInterface& scriptInterface) const
502 {
503  TOJSVAL_SETUP();
504  SET_MSG_PROPERTY(x);
505  SET_MSG_PROPERTY(y);
506  return OBJECT_TO_JSVAL(obj);
507 }
508 
509 CMessage* CMessageExample::FromJSVal(ScriptInterface& scriptInterface, jsval val)
510 {
511  FROMJSVAL_SETUP();
512  GET_MSG_PROPERTY(int, x);
513  GET_MSG_PROPERTY(int, y);
514  return new CMessageExample(x, y);
515 }
516 @endcode
517 
518 (You can use the JS API directly in here, but these macros simplify the common case of a single object
519 with a set of scalar fields.)
520 
521 If you don't want to support scripts sending/receiving the message, you can implement stub functions instead:
522 
523 @code
524 jsval CMessageExample::ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const
525 {
526  return JSVAL_VOID;
527 }
528 
529 CMessage* CMessageExample::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
530 {
531  return NULL;
532 }
533 @endcode
534 
535 
536 
537 @section defining-js-message Defining a new message type in JS
538 
539 If a message will only be sent and received by JS components, it can be defined purely in JS.
540 For example, add to the file @b interfaces/Example.js:
541 
542 @code
543 // Message of the form { "foo": 1, "bar": "baz" }
544 // sent whenever the example component wants to demonstrate the message feature.
545 Engine.RegisterMessageType("Example");
546 @endcode
547 
548 Note that the only specification of the structure of the message is in comments -
549 there is no need to tell the engine what properties it will have.
550 
551 This message type can then be used from JS exactly like the @c CMessageExample defined in C++.
552 
553 
554 
555 @section communication Component communication
556 
557 @subsection message-passing Message passing
558 
559 For one-to-many communication, you can send indirect messages to components.
560 
561 From C++, use CComponentManager::PostMessage to send a message to a specific entity, and
562 CComponentManager::BroadcastMessage to send to all entities.
563 (In all cases, messages will only be received by components that subscribed to the corresponding message type).
564 
565 @code
566 CMessageExample msg(10, 20);
567 GetSimContext().GetComponentManager().PostMessage(ent, msg);
568 GetSimContext().GetComponentManager().BroadcastMessage(msg);
569 @endcode
570 
571 From JS, use @ref CComponentManager::Script_PostMessage "Engine.PostMessage" and
572 @ref CComponentManager::Script_BroadcastMessage "Engine.BroadcastMessage", using the
573 @c MT_* constants to identify the message type:
574 
575 @code
576 Engine.PostMessage(ent, MT_Example, { x: 10, y: 20 });
577 Engine.BroadcastMessage(MT_Example, { x: 10, y: 20 });
578 @endcode
579 
580 Messages will be received and processed synchronously, before the PostMessage/BroadcastMessage calls return.
581 
582 @subsection query-interface Retrieving interfaces
583 
584 You can also directly retrieve the component implementing a given interface for a given entity,
585 to call methods on it directly.
586 
587 In C++, use CmpPtr (see its class documentation for details):
588 
589 @code
590 #include "simulation2/components/ICmpPosition.h"
591 ...
592 CmpPtr<ICmpPosition> cmpPosition(context, ent);
593 if (!cmpPosition)
594  // do something to avoid dereferencing null pointers
595 cmpPosition->MoveTo(x, y);
596 @endcode
597 
598 In JS, use @ref CComponentManager::Script_QueryInterface "Engine.QueryInterface":
599 
600 @code
601 var cmpPosition = Engine.QueryInterface(ent, IID_Position);
602 cmpPosition.MoveTo(x, y);
603 @endcode
604 
605 (The use of @c cmpPosition in JS will throw an exception if it's null, so there's no need
606 for explicit checks unless you expect the component may legitimately not exist and you want
607 to handle it gracefully.)
608 
609 
610 
611 @section testing Testing components
612 
613 Tests are critical for ensuring and maintaining code quality, so all non-trivial components should
614 have test cases. The first part is testing each component in isolation, to check the following aspects:
615 
616 - Initialising the component state from template data.
617 - Responding to method calls to modify and retrieve state.
618 - Responding to broadcast/posted messages.
619 - Serializing and deserializing, for saved games and networking.
620 
621 To focus on these, the communication and interaction with other components is explicitly not tested here
622 (though it should be tested elsewhere).
623 The code for the tested component is loaded, but all other components are replaced with <i>mock objects</i>
624 that implement the expected interfaces but with dummy implementations (ignoring calls, returning constants, etc).
625 The details differ depending on what language the component is written in:
626 
627 
628 @subsection testing-cpp Testing C++ components
629 
630 Create the file @b simulation2/components/tests/test_Example.h, and copy it from something like test_CommandQueue.h.
631 In particular, you need the @c setUp and @c tearDown functions to initialise CXeromyces, and you should use
632 ComponentTestHelper to set up the test environment and construct the component for you.
633 Then just use the component, and use CxxTest's @c TS_* macros to check things, and use
634 ComponentTestHelper::Roundtrip to test serialization roundtripping.
635 
636 Define mock component objects similarly to MockTerrain. Put it in ComponentTest.h if it's usable by many
637 component tests, or in the test_*.h file if it's specific to one test.
638 Instantiate a mock object on the stack, and use ComponentTestHelper::AddMock to make it accessible
639 by QueryInterface.
640 
641 @subsection testing-js Testing JS components
642 
643 Create the file @b binaries/data/mods/public/simulation/components/tests/test_ExampleTwo.js, and write
644 
645 @code
646 Engine.LoadComponentScript("ExampleTwo.js");
647 var cmp = ConstructComponent(1, "ExampleTwo");
648 @endcode
649 
650 where @c ExampleTwo.js is the component script to test, @c 1 is the entity ID, @c "ExampleTwo" is the component name.
651 Then call methods on @c cmp to test it, using the @c TS_* functions defined in
652 @b binaries/data/tests/test_setup.js for common assertions.
653 
654 Create mock objects like
655 
656 @code
657 AddMock(1, IID_Position, {
658  GetPosition: function() {
659  return {x:1, y:2, z:3};
660  },
661 });
662 @endcode
663 
664 giving the entity ID, interface ID, and an object that emulates as much of the interface as is needed
665 for the test.
666 
667 */