<!DOCTYPE html> <html> <head> <title>Virtual World Framework</title> <script type="text/javascript" src="qunit.js"></script> <script type="text/javascript" src="../lib/async.js"></script> <script type="text/javascript" src="../lib/crypto.js"></script> <script type="text/javascript" src="../lib/md5.js"></script> <script type="text/javascript" src="../lib/alea.js"></script> <script type="text/javascript" src="../lib/mash.js"></script> <script type="text/javascript" src="../lib/vwf.js"></script> <script type="text/javascript" src="../lib/require.js"></script> <script type="text/javascript"> require( { baseUrl: "../lib", paths: { jquery: "jquery-1.10.2.min", }, }, [ "domReady", "utility.js", "jquery", "vwf/configuration", "vwf/kernel/model", "vwf/model/javascript", "vwf/model/object", "vwf/model/stage/log", "vwf/kernel/view", "vwf/view/document", "vwf/kernel/utility", "vwf/utility", "logger", ], function( ready, testUtility ) { // Test the kernel "future" interface. ready( function() { vwf.initialize( /* models */ [ "vwf/model/javascript", "vwf/model/object" ], /* views */ [ "vwf/view/document" ] ); var kernel = require( "vwf/kernel/model" ).create( vwf ); // connect through the kernel's model interface // Time passage from future actions and the periodic interval. asyncTest( "Time passage", function() { createFixture( function( fixtureID, cleanup ) { // Calculate times. We need to find two system tick times and additional times between // the system ticks. This calculation will need to be adjusted if the single-user tick // interval changes. var interval = 10; // assumed periodic tick interval var timeStart = ( Math.floor( Math.round( vwf.time() * 1000 ) / interval ) * interval + interval ) / 1000; // next periodic tick var timeStop = ( Math.floor( Math.round( vwf.time() * 1000 ) / interval ) * interval + interval + interval ) / 1000; // the following periodic tick var time1 = fixed3( timeStart + 0.001 ); var time2 = fixed3( timeStart + 0.002 ); var time3 = fixed3( timeStart + 0.003 ); timeStart = fixed3( timeStart ); timeStop = fixed3( timeStop ); // Create a function to drop tags by timestamp in this.property. kernel.execute( fixtureID, "this.mark = function( tag ) { " + "var time = this.time.toFixed(3); " + "this.property[time] || ( this.property[time] = [] ); " + "this.property[time].push( tag ); " + "}" ); kernel.execute( fixtureID, "this.property = {}" ); // Schedule the actions. kernel.execute( fixtureID, "", undefined, time1 ); // nothing kernel.execute( fixtureID, "this.mark( 'one' )", undefined, time2 ); // one thing kernel.execute( fixtureID, "this.mark( 'two' )", undefined, time3 ); // two things kernel.execute( fixtureID, "this.mark( 'three' )", undefined, time3 ); kernel.execute( fixtureID, "", undefined, timeStop ); // nothing, exactly on the tick // Wait and test. testUtility.runFutureAssertions( function( time ) { vwf.execute( fixtureID, "this.mark( 'tick' )" ); // tick, just before assertions }, [ { absolute: timeStart, assertion: function() { deepEqual( vwf.execute( fixtureID, "this.property" )[ timeStart ], [ "tick" ], "tick at the periodic interval" ) } }, { absolute: time1, assertion: function() { deepEqual( vwf.execute( fixtureID, "this.property" )[ time1 ], [ "tick" ], "tick when a future action executes" ) } }, { absolute: time2, assertion: function() { deepEqual( vwf.execute( fixtureID, "this.property" )[ time2 ], [ "tick", "one" ], "tick is before the future action" ) } }, { absolute: time3, assertion: function() { deepEqual( vwf.execute( fixtureID, "this.property" )[ time3 ], [ "tick", "two", "three" ], "one tick per group of future actions" ) } }, { absolute: timeStop, assertion: function() { deepEqual( vwf.execute( fixtureID, "this.property" )[ timeStop ], [ "tick" ], "one tick with the future action at the periodic interval time" ) } }, ], cleanup ); } ); } ); // Future JavaScript API asyncTest( "Future JavaScript", function() { createFixture( function( fixtureID, cleanup ) { vwf.setProperty( fixtureID, "property", undefined ); var time0 = fixed3( vwf.time() ); var time1 = fixed3( time0 + 0.011 ); var time2 = fixed3( time0 + 0.012 ); var time3 = fixed3( time0 + 0.013 ); vwf.execute( fixtureID, "this.in( " + "0.011" + " ).property = 'in'" ); vwf.execute( fixtureID, "this.at( " + time2 + " ).property = 'at'" ); vwf.execute( fixtureID, "this.future( " + "0.013" + " ).property = 'future'" ); testUtility.runFutureAssertions( [ { absolute: time1, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "in", "relative" ) } }, { absolute: time2, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "at", "absolute" ) } }, { absolute: time3, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "future", "relative alias" ) } }, ], cleanup ); } ); } ); // Immediate actions directly to the kernel asyncTest( "Immediate kernel", function() { createFixture( function( fixtureID, cleanup ) { kernel.setProperty( fixtureID, "property", "property", undefined ); equal( vwf.getProperty( fixtureID, "property" ), "property", "immediate property" ); kernel.callMethod( fixtureID, "method", [ "method" ], undefined ); equal( vwf.getProperty( fixtureID, "property" ), "method", "immediate method" ); kernel.fireEvent( fixtureID, "event", [ "event" ], undefined ); equal( vwf.getProperty( fixtureID, "property" ), "event", "immediate event" ); cleanup(); start(); } ); } ); // Immediate actions through JavaScript asyncTest( "Immediate JavaScript", function() { createFixture( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.property = 'property'" ); equal( vwf.getProperty( fixtureID, "property" ), "property", "immediate property" ); vwf.execute( fixtureID, "this.method( 'method' )" ); equal( vwf.getProperty( fixtureID, "property" ), "method", "immediate method" ); vwf.execute( fixtureID, "this.event( 'event' )" ); equal( vwf.getProperty( fixtureID, "property" ), "event", "immediate event" ); cleanup(); start(); } ); } ); // Future actions directly to the kernel asyncTest( "Future kernel", function() { createFixture( function( fixtureID, cleanup ) { vwf.setProperty( fixtureID, "property", undefined ); equal( vwf.getProperty( fixtureID, "property" ), undefined, "future initialized" ); kernel.setProperty( fixtureID, "property", "property", -0.001 ); kernel.callMethod( fixtureID, "method", [ "method" ], -0.002 ); kernel.fireEvent( fixtureID, "event", [ "event" ], -0.003 ); equal( vwf.getProperty( fixtureID, "property" ), undefined, "future queued" ); testUtility.runFutureAssertions( [ { relative: 0.001, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "property", "future property" ) } }, { relative: 0.002, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "method", "future method" ) } }, { relative: 0.003, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "event", "future event" ) } }, ], cleanup ); } ); } ); // Future actions through JavaScript asyncTest( "Future JavaScript", function() { createFixture( function( fixtureID, cleanup ) { vwf.setProperty( fixtureID, "property", undefined ); equal( vwf.getProperty( fixtureID, "property" ), undefined, "future initialized" ); vwf.execute( fixtureID, "this.future( 0.001 ).property = 'property'" ); vwf.execute( fixtureID, "this.future( 0.002 ).method( 'method' )" ); vwf.execute( fixtureID, "this.future( 0.003 ).event( 'event' )" ); equal( vwf.getProperty( fixtureID, "property" ), undefined, "future queued" ); testUtility.runFutureAssertions( [ { relative: 0.001, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "property", "future property" ) } }, { relative: 0.002, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "method", "future method" ) } }, { relative: 0.003, assertion: function() { equal( vwf.getProperty( fixtureID, "property" ), "event", "future event" ) } }, ], cleanup ); } ); } ); // Future inherited actions through JavaScript direct properties asyncTest( "Future JavaScript inherited direct", function() { createFixtureDerivedBase( function( derivedID, baseID, cleanup ) { equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived initialized" ); equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived initialized" ); // inherited equal( vwf.getProperty( baseID, "base" ), "base", "base in base" ); vwf.execute( derivedID, "this.future( 0.001 ).derived = 'derived updated'" ); vwf.execute( derivedID, "this.future( 0.002 ).base = 'base updated'" ); // inherit from base, assign to derived equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived queued" ); equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived queued" ); // still inherited equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" ); testUtility.runFutureAssertions( [ { relative: 0.001, assertion: function() { equal( vwf.getProperty( derivedID, "derived" ), "derived updated", "derived in derived executed" ) } }, { relative: 0.002, assertion: function() { equal( vwf.getProperty( derivedID, "base" ), "base updated", "base in derived executed" ) // no longer inherited equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" ) } }, ], cleanup ); } ); } ); // Future inherited actions through JavaScript collection properties asyncTest( "Future JavaScript inherited collection", function() { createFixtureDerivedBase( function( derivedID, baseID, cleanup ) { equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived initialized" ); equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived initialized" ); // inherited equal( vwf.getProperty( baseID, "base" ), "base", "base in base" ); vwf.execute( derivedID, "this.future( 0.001 ).properties.derived = 'derived updated'" ); vwf.execute( derivedID, "this.future( 0.002 ).properties.base = 'base updated'" ); // inherit from base, assign to derived equal( vwf.getProperty( derivedID, "derived" ), "derived", "derived in derived queued" ); equal( vwf.getProperty( derivedID, "base" ), "base", "base in derived queued" ); // still inherited equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" ); testUtility.runFutureAssertions( [ { relative: 0.001, assertion: function() { equal( vwf.getProperty( derivedID, "derived" ), "derived updated", "derived in derived executed" ) } }, { relative: 0.002, assertion: function() { equal( vwf.getProperty( derivedID, "base" ), "base updated", "base in derived executed" ) // no longer inherited equal( vwf.getProperty( baseID, "base" ), "base", "base in base unchanged" ) } }, ], cleanup ); } ); } ); // -- Interactions between reflector messages and future messages ------------------------ // The queue is must retain a well-defined order when future actions and reflector actions // share the same time, even when reflector messages have not arrived at the client yet. // // For a sequence of messages having the same time: // // - New future messages are inserted after other future messages and before any // reflector messages. // // - New reflector messages are appended after any future messages and after any other // reflector messages. // r0( f0 ) => r0 f0 asyncTest( "Reflector action generating a future action for the same time", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0, "f0" ] ] ], 0 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "r0 executed", // at time 0 "- f0 scheduled", // at time 0, for time 0 "f0 executed", // at time 0 ], "reflector action then future action" ) } }, ], cleanup ); } ); } ); // r0( f1 ) => r0 f1 asyncTest( "Reflector action generating a future action for a later time", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "r0 executed", // at time 0 "- f1 scheduled", // at time 0, for time 1 "f1 executed" // at time 1 ], "reflector action then future action" ) } }, ], cleanup ); } ); } ); // r0( f0a, f0b ) => r0 f0a f0b asyncTest( "Reflector action generating multiple future actions for the same time", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0, "f0a", 0, "f0b" ] ] ], 0 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "r0 executed", // at time 0 "- f0a scheduled", // at time 0, for time 0 "- f0b scheduled", // at time 0, for time 0 "f0a executed", // at time 0 "f0b executed", // at time 0 ], "reflector action then future actions in the order of arrival" ) } }, ], cleanup ); } ); } ); // r0( f1a, f1b ) => r0 f1a f1b asyncTest( "Reflector action generating multiple future actions for the same later time", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1a", 0.001, "f1b" ] ] ], 0 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "r0 executed", // at time 0 "- f1a scheduled", // at time 0, for time 1 "- f1b scheduled", // at time 0, for time 1 "f1a executed", // at time 1 "f1b executed", // at time 1 ], "reflector action then future actions in the order of arrival" ) } }, ], cleanup ); } ); } ); // r0( f1 ) r1 ... => r0 f1 r1 asyncTest( "Reflector action generating a future action for a later time, with a reflector " + "action for that same time arriving beforehand", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 ); vwf.execute( fixtureID, "this.methods.log( '- r1 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "- r1 scheduled", // at time 0, for time 1 "r0 executed", // at time 0 "- f1 scheduled", // at time 0, for time 1 "f1 executed", // at time 1 "r1 executed", // at time 1 ], "reflector action, later future action, then bounding reflector action" ) } }, ], cleanup ); } ); } ); // r0( f1 ) ... r1 => r0 f1 r1 asyncTest( "Reflector action generating a future action for a later time, with a reflector " + "action for that same time arriving afterwards", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 ); testUtility.runFutureAssertions( [ { relative: 0.000, assertion: function() { vwf.execute( fixtureID, "this.methods.log( '- r1 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 ) } }, { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "r0 executed", // at time 0 "- f1 scheduled", // at time 0, for time 1 "- r1 scheduled", // at time 0+, for time 1 "f1 executed", // at time 1 "r1 executed", // at time 1 ], "reflector action, later future action, then bounding reflector action" ) } }, ], cleanup ); } ); } ); // r0( f1a, f1b ) r1a r1b ... => r0 f1a f1b r1a r1b asyncTest( "Reflector action generating multiple future actions for the same later time, " + "with multiple reflector actions for that time arriving beforehand", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1a", 0.001, "f1b" ] ] ], 0 ); vwf.execute( fixtureID, "this.methods.log( '- r1a scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1a" ] ], -0.001 ); vwf.execute( fixtureID, "this.methods.log( '- r1b scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1b" ] ], -0.001 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "- r1a scheduled", // at time 0, for time 1 "- r1b scheduled", // at time 0, for time 1 "r0 executed", // at time 0 "- f1a scheduled", // at time 0, for time 1 "- f1b scheduled", // at time 0, for time 1 "f1a executed", // at time 1 "f1b executed", // at time 1 "r1a executed", // at time 1 "r1b executed", // at time 1 ], "reflector action, later future actions, then bounding reflector actions" ) } }, ], cleanup ); } ); } ); // r0( f1a, f1b ) ... r1a r1b => r0 f1a f1b r1a r1b asyncTest( "Reflector action generating multiple future actions for the same later time, " + "with multiple reflector actions for that time arriving afterwards", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1a", 0.001, "f1b" ] ] ], 0 ); testUtility.runFutureAssertions( [ { relative: 0.000, assertion: function() { vwf.execute( fixtureID, "this.methods.log( '- r1a scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1a" ] ], -0.001 ) } }, { relative: 0.000, assertion: function() { vwf.execute( fixtureID, "this.methods.log( '- r1b scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1b" ] ], -0.001 ) } }, { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "r0 executed", // at time 0 "- f1a scheduled", // at time 0, for time 1 "- f1b scheduled", // at time 0, for time 1 "- r1a scheduled", // at time 0+, for time 1 "- r1b scheduled", // at time 0+, for time 1 "f1a executed", // at time 1 "f1b executed", // at time 1 "r1a executed", // at time 1 "r1b executed", // at time 1 ], "reflector action, later future actions, then bounding reflector actions" ) } }, ], cleanup ); } ); } ); // r0( f1 ) r1a( f1a ) r1b ... => r0 f1 r1a f1a r1b asyncTest( "Reflector action generating a future action for a later time, " + "with a reflector action for that same time generating a future action for the same time, " + "followed by another reflector action for that same time", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "f1" ] ] ], 0 ); vwf.execute( fixtureID, "this.methods.log( '- r1a scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1a", [ 0, "f1a" ] ] ], -0.001 ); vwf.execute( fixtureID, "this.methods.log( '- r1b scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1b" ] ], -0.001 ); testUtility.runFutureAssertions( [ { relative: 0.020, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "- r1a scheduled", // at time 0, for time 1 "- r1b scheduled", // at time 0, for time 1 "r0 executed", // at time 0 "- f1 scheduled", // at time 0, for time 1 "f1 executed", // at time 1 "r1a executed", // at time 1 "- f1a scheduled", // at time 1, for time 1 "f1a executed", // at time 1 "r1b executed", // at time 1 ], "reflector action, future action, first bounding reflector action, " + "its future action, then second bounding reflector action" ) } }, ], cleanup ); } ); } ); // r0( ff1( ff1a ) ) r1 ... => r0 ff1 ff1a r1 asyncTest( "Reflector action generating a future action for a later time, " + "which generates a future action for that same time, " + "followed by another reflector action for the same time", function() { createFixtureQueue( function( fixtureID, cleanup ) { vwf.execute( fixtureID, "this.methods.log( '- r0 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r0", [ 0.001, "ff1" ] ] ], 0 ); vwf.execute( fixtureID, "this.methods.log( '- r1 scheduled' )" ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 ); testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "- r0 scheduled", // at time 0, for time 0 "- r1 scheduled", // at time 0, for time 1 "r0 executed", // at time 0 "- ff1 scheduled", // at time 0, for time 1 "ff1 executed", // at time 1 "- ff1a scheduled", // at time 1, for time 1 "ff1a executed", // at time 1 "r1 executed", // at time 1 ], "reflector action, future action, its future action, then bounding reflector action" ) } }, ], cleanup ); } ); } ); // setState should remove any messages from the queue that pre-date the setState action, // remove messages generated while initializing nodes during the setState action, insert // messages from the setState incoming queue, and leave messages in place that arrived // after the setState message. asyncTest( "setState queue", function() { createFixtureQueue( function( fixtureID, cleanup ) { // Add reflector and future messages that pre-date the setState message but that would // execute after it at times 2 and 3, respectively. These represent the prior state of // the queue. setState should remove them. vwf.plan( fixtureID, "callMethod", "future_", [ [ "f2" ] ], -0.002 ); vwf.send( fixtureID, "callMethod", "reflector", [ [ "r3" ] ], -0.003 ); // Add the setState message at time 1. vwf.send( undefined, "setState", undefined, [ { // The created node generates a future message during initialize that would run at // time 1, immediately following the setState action. setState should remove it. nodes: [ testUtility.dataURIFromDescriptor( { extends: fixtureID, scripts: [ "this.initialize = function() { " + "Object.getPrototypeOf( this ).future( 0 ).future_( 'f1' ); " + "}" ], } ) ], // Include a future message at time 4 and a reflector message at time 5 in the // incoming queue. These should be retained. queue: { time: vwf.time() + 0.001, queue: [ { time: vwf.time() + 0.004, node: fixtureID, action: "callMethod", member: "future_", parameters: [ [ "f4" ] ], origin: "future" }, { time: vwf.time() + 0.005, node: fixtureID, action: "callMethod", member: "reflector", parameters: [ [ "r5" ] ], origin: "reflector" } ] } } ], -0.001 ); // Add a reflector message at time 1. This represents a new action that the reflector // sent following the setState action. It should be retained and should execute after // setState. vwf.send( fixtureID, "callMethod", "reflector", [ [ "r1" ] ], -0.001 ); // The expected result is that setState runs and doesn't leave a trace (other than the // transformed system state), the following reflector action runs, then the actions // from the incoming queue scheduled later run. testUtility.runFutureAssertions( [ { relative: 0.010, assertion: function() { deepEqual( vwf.getProperty( fixtureID, "log" ), [ "r1 executed", // second reflector action at time 1 "f4 executed", // future action from the incoming queue at time 4 "r5 executed", // reflector action from the incoming queue at time 5 ], "setState discarded prior actions, added incoming actions, and retained following actions" ) } }, ], cleanup ); } ); } ); // == Helper functions ===================================================================== // Create a node with a property, a method and an event. function createFixture( callback /* fixtureID, cleanup */ ) { vwf.createChild( 0, testUtility.uniqueName( "fixture" ), { extends: "http://vwf.example.com/node.vwf", properties: { property: undefined, }, methods: { method: "this.property = arguments[0]", }, events: { event: undefined, }, scripts: [ "this.event = this.events.add( function() { this.property = arguments[0] }, this )", ], }, undefined, function( fixtureID ) { callback( fixtureID, function() { vwf.deleteNode( fixtureID ); } ); } ); } // Create a node with two levels of inheritance and properties to manipulate. function createFixtureDerivedBase( callback /* derivedID, baseID, cleanup */ ) { vwf.createNode( { extends: "http://vwf.example.com/node.vwf", properties: { base: "base", }, }, function( baseID ) { vwf.createNode( { extends: baseID, properties: { derived: "derived", }, }, function( derivedID ) { callback( derivedID, baseID, function() { vwf.deleteNode( derivedID ); vwf.deleteNode( baseID ); } ); } ); } ); } // Create a node with methods to be called as reflector and future actions that will keep // a log. function createFixtureQueue( callback /* fixtureID, cleanup */ ) { vwf.createChild( 0, testUtility.uniqueName( "fixture" ), { extends: "http://vwf.example.com/node.vwf", properties: { log: [], // log of methods called on the node }, methods: { // A method to be called as a reflector action. Append the first parameter to the // log to record that this call occurred, then schedule calls to `future` for each // time/tag pair in the `futures` array. reflector: { parameters: [ "tag", "futures" ], body: "this.methods.log( tag + ' executed' ); " + // the reflector action executes "while ( ( futures || [] ).length ) { " + "var time = futures.shift(); " + "var tag = futures.shift(); " + "this.methods.log( '- ' + tag + ' scheduled' ); " + // the future action is scheduled "tag.match( /ff/ ) ? " + "this.future( time ).methods.futurefuture( tag, tag + 'a' ) : " + // special case for future to call future "this.future( time ).methods.future_( tag ); " + "}" }, // A method to be called as a future action. Append the parameter to the log to // record that this called occurred. future_: { parameters: [ "tag" ], body: "this.methods.log( tag + ' executed' )" // the future action executes }, // A method to be called as a future action that generates another future action. // Append the first parameter to the log to record that this call occurred, then // schedule an additional call to `future` with the second tag. futurefuture: { parameters: [ "tag1", "tag2" ], body: "this.methods.log( tag1 + ' executed' ); " + // the future action executes "this.methods.log( '- ' + tag2 + ' scheduled' ); " + // another future action is scheduled "this.future( 0 ).methods.future_( tag2 )" }, // Append a tag to the log. log: { parameters: [ "tag" ], body: "this.properties.log = this.properties.log.concat( tag )" }, }, }, undefined, function( fixtureID ) { callback( fixtureID, function() { vwf.deleteNode( fixtureID ); } ); } ); } // Convert a number to a Number object that expresses itself in 3-point fixed precision // when referenced in string context. function fixed3( number ) { var number3 = new Number( number ); number3.toString = function() { return this.toFixed( 3 ) } return number3; } } ); } ); </script> <link rel="stylesheet" type="text/css" href="qunit.css" /> </head> <body> <h1 id="qunit-header">Virtual World Framework</h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> <div id="qunit-fixture">test markup, will be hidden</div> </body> </html>