future.html 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Virtual World Framework</title>
  5. <script type="text/javascript" src="qunit.js"></script>
  6. <script type="text/javascript" src="../lib/async.js"></script>
  7. <script type="text/javascript" src="../lib/crypto.js"></script>
  8. <script type="text/javascript" src="../lib/md5.js"></script>
  9. <script type="text/javascript" src="../lib/alea.js"></script>
  10. <script type="text/javascript" src="../lib/mash.js"></script>
  11. <script type="text/javascript" src="../lib/vwf.js"></script>
  12. <script type="text/javascript" src="../lib/require.js"></script>
  13. <script type="text/javascript">
  14. require( {
  15. baseUrl: "../lib",
  16. paths: {
  17. jquery: "jquery-1.10.2.min",
  18. },
  19. }, [
  20. "domReady",
  21. "utility.js",
  22. "jquery",
  23. "vwf/configuration",
  24. "vwf/kernel/model",
  25. "vwf/model/javascript",
  26. "vwf/model/object",
  27. "vwf/model/stage/log",
  28. "vwf/kernel/view",
  29. "vwf/view/document",
  30. "vwf/kernel/utility",
  31. "vwf/utility",
  32. "logger",
  33. ], function( ready, testUtility ) {
  34. // Test the kernel "future" interface.
  35. ready( function() {
  36. vwf.initialize(
  37. /* models */ [ "vwf/model/javascript", "vwf/model/object" ],
  38. /* views */ [ "vwf/view/document" ]
  39. );
  40. var kernel = require( "vwf/kernel/model" ).create( vwf ); // connect through the kernel's model interface
  41. // Time passage from future actions and the periodic interval.
  42. asyncTest( "Time passage", function() {
  43. createFixture( function( fixtureID, cleanup ) {
  44. // Calculate times. We need to find two system tick times and additional times between
  45. // the system ticks. This calculation will need to be adjusted if the single-user tick
  46. // interval changes.
  47. var interval = 10; // assumed periodic tick interval
  48. var timeStart = ( Math.floor( Math.round( vwf.time() * 1000 ) / interval ) *
  49. interval + interval ) / 1000; // next periodic tick
  50. var timeStop = ( Math.floor( Math.round( vwf.time() * 1000 ) / interval ) *
  51. interval + interval + interval ) / 1000; // the following periodic tick
  52. var time1 = fixed3( timeStart + 0.001 );
  53. var time2 = fixed3( timeStart + 0.002 );
  54. var time3 = fixed3( timeStart + 0.003 );
  55. timeStart = fixed3( timeStart );
  56. timeStop = fixed3( timeStop );
  57. // Create a function to drop tags by timestamp in this.property.
  58. kernel.execute( fixtureID, "this.mark = function( tag ) { " +
  59. "var time = this.time.toFixed(3); " +
  60. "this.property[time] || ( this.property[time] = [] ); " +
  61. "this.property[time].push( tag ); " +
  62. "}" );
  63. kernel.execute( fixtureID, "this.property = {}" );
  64. // Schedule the actions.
  65. kernel.execute( fixtureID, "", undefined, time1 ); // nothing
  66. kernel.execute( fixtureID, "this.mark( 'one' )", undefined, time2 ); // one thing
  67. kernel.execute( fixtureID, "this.mark( 'two' )", undefined, time3 ); // two things
  68. kernel.execute( fixtureID, "this.mark( 'three' )", undefined, time3 );
  69. kernel.execute( fixtureID, "", undefined, timeStop ); // nothing, exactly on the tick
  70. // Wait and test.
  71. testUtility.runFutureAssertions( function( time ) {
  72. vwf.execute( fixtureID, "this.mark( 'tick' )" ); // tick, just before assertions
  73. }, [
  74. { absolute: timeStart, assertion: function() {
  75. deepEqual( vwf.execute( fixtureID, "this.property" )[ timeStart ], [ "tick" ], "tick at the periodic interval" ) } },
  76. { absolute: time1, assertion: function() {
  77. deepEqual( vwf.execute( fixtureID, "this.property" )[ time1 ], [ "tick" ], "tick when a future action executes" ) } },
  78. { absolute: time2, assertion: function() {
  79. deepEqual( vwf.execute( fixtureID, "this.property" )[ time2 ], [ "tick", "one" ], "tick is before the future action" ) } },
  80. { absolute: time3, assertion: function() {
  81. deepEqual( vwf.execute( fixtureID, "this.property" )[ time3 ], [ "tick", "two", "three" ], "one tick per group of future actions" ) } },
  82. { absolute: timeStop, assertion: function() {
  83. deepEqual( vwf.execute( fixtureID, "this.property" )[ timeStop ], [ "tick" ], "one tick with the future action at the periodic interval time" ) } },
  84. ], cleanup );
  85. } );
  86. } );
  87. // Future JavaScript API
  88. asyncTest( "Future JavaScript", function() {
  89. createFixture( function( fixtureID, cleanup ) {
  90. vwf.setProperty( fixtureID, "property", undefined );
  91. var time0 = fixed3( vwf.time() );
  92. var time1 = fixed3( time0 + 0.011 );
  93. var time2 = fixed3( time0 + 0.012 );
  94. var time3 = fixed3( time0 + 0.013 );
  95. vwf.execute( fixtureID, "this.in( " + "0.011" + " ).property = 'in'" );
  96. vwf.execute( fixtureID, "this.at( " + time2 + " ).property = 'at'" );
  97. vwf.execute( fixtureID, "this.future( " + "0.013" + " ).property = 'future'" );
  98. testUtility.runFutureAssertions( [
  99. { absolute: time1, assertion: function() {
  100. equal( vwf.getProperty( fixtureID, "property" ), "in", "relative" ) } },
  101. { absolute: time2, assertion: function() {
  102. equal( vwf.getProperty( fixtureID, "property" ), "at", "absolute" ) } },
  103. { absolute: time3, assertion: function() {
  104. equal( vwf.getProperty( fixtureID, "property" ), "future", "relative alias" ) } },
  105. ], cleanup );
  106. } );
  107. } );
  108. // Immediate actions directly to the kernel
  109. asyncTest( "Immediate kernel", function() {
  110. createFixture( function( fixtureID, cleanup ) {
  111. kernel.setProperty( fixtureID, "property", "property", undefined );
  112. equal( vwf.getProperty( fixtureID, "property" ), "property", "immediate property" );
  113. kernel.callMethod( fixtureID, "method", [ "method" ], undefined );
  114. equal( vwf.getProperty( fixtureID, "property" ), "method", "immediate method" );
  115. kernel.fireEvent( fixtureID, "event", [ "event" ], undefined );
  116. equal( vwf.getProperty( fixtureID, "property" ), "event", "immediate event" );
  117. cleanup();
  118. start();
  119. } );
  120. } );
  121. // Immediate actions through JavaScript
  122. asyncTest( "Immediate JavaScript", function() {
  123. createFixture( function( fixtureID, cleanup ) {
  124. vwf.execute( fixtureID, "this.property = 'property'" );
  125. equal( vwf.getProperty( fixtureID, "property" ), "property", "immediate property" );
  126. vwf.execute( fixtureID, "this.method( 'method' )" );
  127. equal( vwf.getProperty( fixtureID, "property" ), "method", "immediate method" );
  128. vwf.execute( fixtureID, "this.event( 'event' )" );
  129. equal( vwf.getProperty( fixtureID, "property" ), "event", "immediate event" );
  130. cleanup();
  131. start();
  132. } );
  133. } );
  134. // Future actions directly to the kernel
  135. asyncTest( "Future kernel", function() {
  136. createFixture( function( fixtureID, cleanup ) {
  137. vwf.setProperty( fixtureID, "property", undefined );
  138. equal( vwf.getProperty( fixtureID, "property" ), undefined, "future initialized" );
  139. kernel.setProperty( fixtureID, "property", "property", -0.001 );
  140. kernel.callMethod( fixtureID, "method", [ "method" ], -0.002 );
  141. kernel.fireEvent( fixtureID, "event", [ "event" ], -0.003 );
  142. equal( vwf.getProperty( fixtureID, "property" ), undefined, "future queued" );
  143. testUtility.runFutureAssertions( [
  144. { relative: 0.001, assertion: function() {
  145. equal( vwf.getProperty( fixtureID, "property" ), "property", "future property" ) } },
  146. { relative: 0.002, assertion: function() {
  147. equal( vwf.getProperty( fixtureID, "property" ), "method", "future method" ) } },
  148. { relative: 0.003, assertion: function() {
  149. equal( vwf.getProperty( fixtureID, "property" ), "event", "future event" ) } },
  150. ], cleanup );
  151. } );
  152. } );
  153. // Future actions through JavaScript
  154. asyncTest( "Future JavaScript", function() {
  155. createFixture( function( fixtureID, cleanup ) {
  156. vwf.setProperty( fixtureID, "property", undefined );
  157. equal( vwf.getProperty( fixtureID, "property" ), undefined, "future initialized" );
  158. vwf.execute( fixtureID, "this.future( 0.001 ).property = 'property'" );
  159. vwf.execute( fixtureID, "this.future( 0.002 ).method( 'method' )" );
  160. vwf.execute( fixtureID, "this.future( 0.003 ).event( 'event' )" );
  161. equal( vwf.getProperty( fixtureID, "property" ), undefined, "future queued" );
  162. testUtility.runFutureAssertions( [
  163. { relative: 0.001, assertion: function() {
  164. equal( vwf.getProperty( fixtureID, "property" ), "property", "future property" ) } },
  165. { relative: 0.002, assertion: function() {
  166. equal( vwf.getProperty( fixtureID, "property" ), "method", "future method" ) } },
  167. { relative: 0.003, assertion: function() {
  168. equal( vwf.getProperty( fixtureID, "property" ), "event", "future event" ) } },
  169. ], cleanup );
  170. } );
  171. } );
  172. // Future inherited actions through JavaScript direct properties
  173. asyncTest( "Future JavaScript inherited direct", function() {
  174. createFixtureDerivedBase( function( derivedID, baseID, cleanup ) {
  175. equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived initialized" );
  176. equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived initialized" ); // inherited
  177. equal( vwf.getProperty( baseID, "base" ), "base", "base in base" );
  178. vwf.execute( derivedID, "this.future( 0.001 ).derived = 'derived updated'" );
  179. vwf.execute( derivedID, "this.future( 0.002 ).base = 'base updated'" ); // inherit from base, assign to derived
  180. equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived queued" );
  181. equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived queued" ); // still inherited
  182. equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" );
  183. testUtility.runFutureAssertions( [
  184. { relative: 0.001, assertion: function() {
  185. equal( vwf.getProperty( derivedID, "derived" ), "derived updated", "derived in derived executed" ) } },
  186. { relative: 0.002, assertion: function() {
  187. equal( vwf.getProperty( derivedID, "base" ), "base updated", "base in derived executed" ) // no longer inherited
  188. equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" ) } },
  189. ], cleanup );
  190. } );
  191. } );
  192. // Future inherited actions through JavaScript collection properties
  193. asyncTest( "Future JavaScript inherited collection", function() {
  194. createFixtureDerivedBase( function( derivedID, baseID, cleanup ) {
  195. equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived initialized" );
  196. equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived initialized" ); // inherited
  197. equal( vwf.getProperty( baseID, "base" ), "base", "base in base" );
  198. vwf.execute( derivedID, "this.future( 0.001 ).properties.derived = 'derived updated'" );
  199. vwf.execute( derivedID, "this.future( 0.002 ).properties.base = 'base updated'" ); // inherit from base, assign to derived
  200. equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived queued" );
  201. equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived queued" ); // still inherited
  202. equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" );
  203. testUtility.runFutureAssertions( [
  204. { relative: 0.001, assertion: function() {
  205. equal( vwf.getProperty( derivedID, "derived" ), "derived updated", "derived in derived executed" ) } },
  206. { relative: 0.002, assertion: function() {
  207. equal( vwf.getProperty( derivedID, "base" ), "base updated", "base in derived executed" ) // no longer inherited
  208. equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" ) } },
  209. ], cleanup );
  210. } );
  211. } );
  212. // -- Interactions between reflector messages and future messages ------------------------
  213. // The queue is must retain a well-defined order when future actions and reflector actions
  214. // share the same time, even when reflector messages have not arrived at the client yet.
  215. //
  216. // For a sequence of messages having the same time:
  217. //
  218. // - New future messages are inserted after other future messages and before any
  219. // reflector messages.
  220. //
  221. // - New reflector messages are appended after any future messages and after any other
  222. // reflector messages.
  223. // r0( f0 ) => r0 f0
  224. asyncTest( "Reflector action generating a future action for the same time", function() {
  225. createFixtureQueue( function( fixtureID, cleanup ) {
  226. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  227. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0, "f0" ] ] ], 0 );
  228. testUtility.runFutureAssertions( [
  229. { relative: 0.010, assertion: function() {
  230. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  231. "- r0 scheduled", // at time 0, for time 0
  232. "r0 executed", // at time 0
  233. "- f0 scheduled", // at time 0, for time 0
  234. "f0 executed", // at time 0
  235. ], "reflector action then future action" ) } },
  236. ], cleanup );
  237. } );
  238. } );
  239. // r0( f1 ) => r0 f1
  240. asyncTest( "Reflector action generating a future action for a later time", function() {
  241. createFixtureQueue( function( fixtureID, cleanup ) {
  242. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  243. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 );
  244. testUtility.runFutureAssertions( [
  245. { relative: 0.010, assertion: function() {
  246. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  247. "- r0 scheduled", // at time 0, for time 0
  248. "r0 executed", // at time 0
  249. "- f1 scheduled", // at time 0, for time 1
  250. "f1 executed" // at time 1
  251. ], "reflector action then future action" ) } },
  252. ], cleanup );
  253. } );
  254. } );
  255. // r0( f0a, f0b ) => r0 f0a f0b
  256. asyncTest( "Reflector action generating multiple future actions for the same time", function() {
  257. createFixtureQueue( function( fixtureID, cleanup ) {
  258. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  259. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0, "f0a", 0, "f0b" ] ] ], 0 );
  260. testUtility.runFutureAssertions( [
  261. { relative: 0.010, assertion: function() {
  262. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  263. "- r0 scheduled", // at time 0, for time 0
  264. "r0 executed", // at time 0
  265. "- f0a scheduled", // at time 0, for time 0
  266. "- f0b scheduled", // at time 0, for time 0
  267. "f0a executed", // at time 0
  268. "f0b executed", // at time 0
  269. ], "reflector action then future actions in the order of arrival" ) } },
  270. ], cleanup );
  271. } );
  272. } );
  273. // r0( f1a, f1b ) => r0 f1a f1b
  274. asyncTest( "Reflector action generating multiple future actions for the same later time", function() {
  275. createFixtureQueue( function( fixtureID, cleanup ) {
  276. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  277. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1a", 0.001, "f1b" ] ] ], 0 );
  278. testUtility.runFutureAssertions( [
  279. { relative: 0.010, assertion: function() {
  280. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  281. "- r0 scheduled", // at time 0, for time 0
  282. "r0 executed", // at time 0
  283. "- f1a scheduled", // at time 0, for time 1
  284. "- f1b scheduled", // at time 0, for time 1
  285. "f1a executed", // at time 1
  286. "f1b executed", // at time 1
  287. ], "reflector action then future actions in the order of arrival" ) } },
  288. ], cleanup );
  289. } );
  290. } );
  291. // r0( f1 ) r1 ... => r0 f1 r1
  292. asyncTest( "Reflector action generating a future action for a later time, with a reflector " +
  293. "action for that same time arriving beforehand", function() {
  294. createFixtureQueue( function( fixtureID, cleanup ) {
  295. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  296. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 );
  297. vwf.execute( fixtureID, "this.methods.log( '- r1 scheduled' )" );
  298. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 );
  299. testUtility.runFutureAssertions( [
  300. { relative: 0.010, assertion: function() {
  301. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  302. "- r0 scheduled", // at time 0, for time 0
  303. "- r1 scheduled", // at time 0, for time 1
  304. "r0 executed", // at time 0
  305. "- f1 scheduled", // at time 0, for time 1
  306. "f1 executed", // at time 1
  307. "r1 executed", // at time 1
  308. ], "reflector action, later future action, then bounding reflector action" ) } },
  309. ], cleanup );
  310. } );
  311. } );
  312. // r0( f1 ) ... r1 => r0 f1 r1
  313. asyncTest( "Reflector action generating a future action for a later time, with a reflector " +
  314. "action for that same time arriving afterwards", function() {
  315. createFixtureQueue( function( fixtureID, cleanup ) {
  316. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  317. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 );
  318. testUtility.runFutureAssertions( [
  319. { relative: 0.000, assertion: function() {
  320. vwf.execute( fixtureID, "this.methods.log( '- r1 scheduled' )" );
  321. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 ) } },
  322. { relative: 0.010, assertion: function() {
  323. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  324. "- r0 scheduled", // at time 0, for time 0
  325. "r0 executed", // at time 0
  326. "- f1 scheduled", // at time 0, for time 1
  327. "- r1 scheduled", // at time 0+, for time 1
  328. "f1 executed", // at time 1
  329. "r1 executed", // at time 1
  330. ], "reflector action, later future action, then bounding reflector action" ) } },
  331. ], cleanup );
  332. } );
  333. } );
  334. // r0( f1a, f1b ) r1a r1b ... => r0 f1a f1b r1a r1b
  335. asyncTest( "Reflector action generating multiple future actions for the same later time, " +
  336. "with multiple reflector actions for that time arriving beforehand", function() {
  337. createFixtureQueue( function( fixtureID, cleanup ) {
  338. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  339. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1a", 0.001, "f1b" ] ] ], 0 );
  340. vwf.execute( fixtureID, "this.methods.log( '- r1a scheduled' )" );
  341. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1a" ] ], -0.001 );
  342. vwf.execute( fixtureID, "this.methods.log( '- r1b scheduled' )" );
  343. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1b" ] ], -0.001 );
  344. testUtility.runFutureAssertions( [
  345. { relative: 0.010, assertion: function() {
  346. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  347. "- r0 scheduled", // at time 0, for time 0
  348. "- r1a scheduled", // at time 0, for time 1
  349. "- r1b scheduled", // at time 0, for time 1
  350. "r0 executed", // at time 0
  351. "- f1a scheduled", // at time 0, for time 1
  352. "- f1b scheduled", // at time 0, for time 1
  353. "f1a executed", // at time 1
  354. "f1b executed", // at time 1
  355. "r1a executed", // at time 1
  356. "r1b executed", // at time 1
  357. ], "reflector action, later future actions, then bounding reflector actions" ) } },
  358. ], cleanup );
  359. } );
  360. } );
  361. // r0( f1a, f1b ) ... r1a r1b => r0 f1a f1b r1a r1b
  362. asyncTest( "Reflector action generating multiple future actions for the same later time, " +
  363. "with multiple reflector actions for that time arriving afterwards", function() {
  364. createFixtureQueue( function( fixtureID, cleanup ) {
  365. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  366. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1a", 0.001, "f1b" ] ] ], 0 );
  367. testUtility.runFutureAssertions( [
  368. { relative: 0.000, assertion: function() {
  369. vwf.execute( fixtureID, "this.methods.log( '- r1a scheduled' )" );
  370. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1a" ] ], -0.001 ) } },
  371. { relative: 0.000, assertion: function() {
  372. vwf.execute( fixtureID, "this.methods.log( '- r1b scheduled' )" );
  373. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1b" ] ], -0.001 ) } },
  374. { relative: 0.010, assertion: function() {
  375. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  376. "- r0 scheduled", // at time 0, for time 0
  377. "r0 executed", // at time 0
  378. "- f1a scheduled", // at time 0, for time 1
  379. "- f1b scheduled", // at time 0, for time 1
  380. "- r1a scheduled", // at time 0+, for time 1
  381. "- r1b scheduled", // at time 0+, for time 1
  382. "f1a executed", // at time 1
  383. "f1b executed", // at time 1
  384. "r1a executed", // at time 1
  385. "r1b executed", // at time 1
  386. ], "reflector action, later future actions, then bounding reflector actions" ) } },
  387. ], cleanup );
  388. } );
  389. } );
  390. // r0( f1 ) r1a( f1a ) r1b ... => r0 f1 r1a f1a r1b
  391. asyncTest( "Reflector action generating a future action for a later time, " +
  392. "with a reflector action for that same time generating a future action for the same time, " +
  393. "followed by another reflector action for that same time", function() {
  394. createFixtureQueue( function( fixtureID, cleanup ) {
  395. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  396. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 );
  397. vwf.execute( fixtureID, "this.methods.log( '- r1a scheduled' )" );
  398. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1a", [ 0, "f1a" ] ] ], -0.001 );
  399. vwf.execute( fixtureID, "this.methods.log( '- r1b scheduled' )" );
  400. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1b" ] ], -0.001 );
  401. testUtility.runFutureAssertions( [
  402. { relative: 0.020, assertion: function() {
  403. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  404. "- r0 scheduled", // at time 0, for time 0
  405. "- r1a scheduled", // at time 0, for time 1
  406. "- r1b scheduled", // at time 0, for time 1
  407. "r0 executed", // at time 0
  408. "- f1 scheduled", // at time 0, for time 1
  409. "f1 executed", // at time 1
  410. "r1a executed", // at time 1
  411. "- f1a scheduled", // at time 1, for time 1
  412. "f1a executed", // at time 1
  413. "r1b executed", // at time 1
  414. ], "reflector action, future action, first bounding reflector action, " +
  415. "its future action, then second bounding reflector action" ) } },
  416. ], cleanup );
  417. } );
  418. } );
  419. // r0( ff1( ff1a ) ) r1 ... => r0 ff1 ff1a r1
  420. asyncTest( "Reflector action generating a future action for a later time, " +
  421. "which generates a future action for that same time, " +
  422. "followed by another reflector action for the same time", function() {
  423. createFixtureQueue( function( fixtureID, cleanup ) {
  424. vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" );
  425. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "ff1" ] ] ], 0 );
  426. vwf.execute( fixtureID, "this.methods.log( '- r1 scheduled' )" );
  427. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 );
  428. testUtility.runFutureAssertions( [
  429. { relative: 0.010, assertion: function() {
  430. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  431. "- r0 scheduled", // at time 0, for time 0
  432. "- r1 scheduled", // at time 0, for time 1
  433. "r0 executed", // at time 0
  434. "- ff1 scheduled", // at time 0, for time 1
  435. "ff1 executed", // at time 1
  436. "- ff1a scheduled", // at time 1, for time 1
  437. "ff1a executed", // at time 1
  438. "r1 executed", // at time 1
  439. ], "reflector action, future action, its future action, then bounding reflector action" ) } },
  440. ], cleanup );
  441. } );
  442. } );
  443. // setState should remove any messages from the queue that pre-date the setState action,
  444. // remove messages generated while initializing nodes during the setState action, insert
  445. // messages from the setState incoming queue, and leave messages in place that arrived
  446. // after the setState message.
  447. asyncTest( "setState queue", function() {
  448. createFixtureQueue( function( fixtureID, cleanup ) {
  449. // Add reflector and future messages that pre-date the setState message but that would
  450. // execute after it at times 2 and 3, respectively. These represent the prior state of
  451. // the queue. setState should remove them.
  452. vwf.plan( fixtureID, "callMethod", "future_", [ [ "f2" ] ], -0.002 );
  453. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r3" ] ], -0.003 );
  454. // Add the setState message at time 1.
  455. vwf.send( undefined, "setState", undefined, [ {
  456. // The created node generates a future message during initialize that would run at
  457. // time 1, immediately following the setState action. setState should remove it.
  458. nodes: [
  459. testUtility.dataURIFromDescriptor( {
  460. extends:
  461. fixtureID,
  462. scripts: [
  463. "this.initialize = function() { " +
  464. "Object.getPrototypeOf( this ).future( 0 ).future_( 'f1' ); " +
  465. "}"
  466. ],
  467. } )
  468. ],
  469. // Include a future message at time 4 and a reflector message at time 5 in the
  470. // incoming queue. These should be retained.
  471. queue: {
  472. time:
  473. vwf.time() + 0.001,
  474. queue: [ {
  475. time: vwf.time() + 0.004,
  476. node: fixtureID,
  477. action: "callMethod",
  478. member: "future_",
  479. parameters: [ [ "f4" ] ],
  480. origin: "future"
  481. }, {
  482. time: vwf.time() + 0.005,
  483. node: fixtureID,
  484. action: "callMethod",
  485. member: "reflector",
  486. parameters: [ [ "r5" ] ],
  487. origin: "reflector"
  488. } ]
  489. }
  490. } ], -0.001 );
  491. // Add a reflector message at time 1. This represents a new action that the reflector
  492. // sent following the setState action. It should be retained and should execute after
  493. // setState.
  494. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 );
  495. // The expected result is that setState runs and doesn't leave a trace (other than the
  496. // transformed system state), the following reflector action runs, then the actions
  497. // from the incoming queue scheduled later run.
  498. testUtility.runFutureAssertions( [
  499. { relative: 0.010, assertion: function() {
  500. deepEqual( vwf.getProperty( fixtureID, "log" ), [
  501. "r1 executed", // second reflector action at time 1
  502. "f4 executed", // future action from the incoming queue at time 4
  503. "r5 executed", // reflector action from the incoming queue at time 5
  504. ], "setState discarded prior actions, added incoming actions, and retained following actions" ) } },
  505. ], cleanup );
  506. } );
  507. } );
  508. // == Helper functions =====================================================================
  509. // Create a node with a property, a method and an event.
  510. function createFixture( callback /* fixtureID, cleanup */ ) {
  511. vwf.createChild( 0, testUtility.uniqueName( "fixture" ), {
  512. extends:
  513. "http://vwf.example.com/node.vwf",
  514. properties: {
  515. property: undefined,
  516. },
  517. methods: {
  518. method: "this.property = arguments[0]",
  519. },
  520. events: {
  521. event: undefined,
  522. },
  523. scripts: [
  524. "this.event = this.events.add( function() { this.property = arguments[0] }, this )",
  525. ],
  526. }, undefined, function( fixtureID ) {
  527. callback( fixtureID, function() {
  528. vwf.deleteNode( fixtureID );
  529. } );
  530. } );
  531. }
  532. // Create a node with two levels of inheritance and properties to manipulate.
  533. function createFixtureDerivedBase( callback /* derivedID, baseID, cleanup */ ) {
  534. vwf.createNode( {
  535. extends:
  536. "http://vwf.example.com/node.vwf",
  537. properties: {
  538. base: "base",
  539. },
  540. }, function( baseID ) {
  541. vwf.createNode( {
  542. extends:
  543. baseID,
  544. properties: {
  545. derived: "derived",
  546. },
  547. }, function( derivedID ) {
  548. callback( derivedID, baseID, function() {
  549. vwf.deleteNode( derivedID );
  550. vwf.deleteNode( baseID );
  551. } );
  552. } );
  553. } );
  554. }
  555. // Create a node with methods to be called as reflector and future actions that will keep
  556. // a log.
  557. function createFixtureQueue( callback /* fixtureID, cleanup */ ) {
  558. vwf.createChild( 0, testUtility.uniqueName( "fixture" ), {
  559. extends:
  560. "http://vwf.example.com/node.vwf",
  561. properties: {
  562. log: [], // log of methods called on the node
  563. },
  564. methods: {
  565. // A method to be called as a reflector action. Append the first parameter to the
  566. // log to record that this call occurred, then schedule calls to `future` for each
  567. // time/tag pair in the `futures` array.
  568. reflector: {
  569. parameters:
  570. [ "tag", "futures" ],
  571. body:
  572. "this.methods.log( tag + ' executed' ); " + // the reflector action executes
  573. "while ( ( futures || [] ).length ) { " +
  574. "var time = futures.shift(); " +
  575. "var tag = futures.shift(); " +
  576. "this.methods.log( '- ' + tag + ' scheduled' ); " + // the future action is scheduled
  577. "tag.match( /ff/ ) ? " +
  578. "this.future( time ).methods.futurefuture( tag, tag + 'a' ) : " + // special case for future to call future
  579. "this.future( time ).methods.future_( tag ); " +
  580. "}"
  581. },
  582. // A method to be called as a future action. Append the parameter to the log to
  583. // record that this called occurred.
  584. future_: {
  585. parameters:
  586. [ "tag" ],
  587. body:
  588. "this.methods.log( tag + ' executed' )" // the future action executes
  589. },
  590. // A method to be called as a future action that generates another future action.
  591. // Append the first parameter to the log to record that this call occurred, then
  592. // schedule an additional call to `future` with the second tag.
  593. futurefuture: {
  594. parameters:
  595. [ "tag1", "tag2" ],
  596. body:
  597. "this.methods.log( tag1 + ' executed' ); " + // the future action executes
  598. "this.methods.log( '- ' + tag2 + ' scheduled' ); " + // another future action is scheduled
  599. "this.future( 0 ).methods.future_( tag2 )"
  600. },
  601. // Append a tag to the log.
  602. log: {
  603. parameters: [ "tag" ],
  604. body: "this.properties.log = this.properties.log.concat( tag )"
  605. },
  606. },
  607. }, undefined, function( fixtureID ) {
  608. callback( fixtureID, function() {
  609. vwf.deleteNode( fixtureID );
  610. } );
  611. } );
  612. }
  613. // Convert a number to a Number object that expresses itself in 3-point fixed precision
  614. // when referenced in string context.
  615. function fixed3( number ) {
  616. var number3 = new Number( number );
  617. number3.toString = function() {
  618. return this.toFixed( 3 )
  619. }
  620. return number3;
  621. }
  622. } );
  623. } );
  624. </script>
  625. <link rel="stylesheet" type="text/css" href="qunit.css" />
  626. </head>
  627. <body>
  628. <h1 id="qunit-header">Virtual World Framework</h1>
  629. <h2 id="qunit-banner"></h2>
  630. <div id="qunit-testrunner-toolbar"></div>
  631. <h2 id="qunit-userAgent"></h2>
  632. <ol id="qunit-tests"></ol>
  633. <div id="qunit-fixture">test markup, will be hidden</div>
  634. </body>
  635. </html>