Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: test/test-action.js

Issue 10233013: Crawler, second version (Closed)
Patch Set: Created April 12, 2013, 1:38 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « test/jsTestDriver.conf ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: test/test-action.js
===================================================================
new file mode 100644
--- /dev/null
+++ b/test/test-action.js
@@ -0,0 +1,763 @@
+ActionTest = AsyncTestCase( "ActionTest" );
+
+/**
+ * Test that variables are defined
+ */
+ActionTest.prototype.test__source_is_well_formed = function()
+{
+ assertTrue( Action != null );
+ assertTrue( Action.dispatch != null );
+};
+
+//-------------------------------------------------------
+// Utility
+//-------------------------------------------------------
+
+//-------------------------------------------------------
+// Generic tests
+//-------------------------------------------------------
+/**
+ *
+ * @param {Action.Asynchronous_Action} action
+ * @param {string} state_name
+ */
+function verify_state( action, state_name )
+{
+ var expected = Action.State[ state_name ];
+ assertEquals( "action state is not '" + state_name + "'.", expected, action.state );
+}
+
+/**
+ * Check that an action executes its body and returns. Generic for simple, non-compound actions.
+ *
+ * @param {function} factory
+ * A factory function that yields an action.
+ * @param queue
+ */
+function simple_try( factory, queue )
+{
+ /**
+ * @type {Action.Asynchronous_Action}
+ */
+ var d = null;
+ var sequence = 0;
+
+ function trial()
+ {
+ verify_state( d, "Running" );
+ sequence += 1;
+ }
+
+ queue.call( "Go phase.", function( callbacks )
+ {
+ var monitored_trial = callbacks.add( trial );
+ d = factory( monitored_trial );
+ verify_state( d, "Ready" );
+ assertEquals( 0, sequence );
+ d.go();
+ verify_state( d, "Running" );
+ assertEquals( 0, sequence );
+ } );
+
+ queue.call( "End phase.", function()
+ {
+ verify_state( d, "Done" );
+ assertEquals( 1, sequence );
+ } );
+}
+
+/**
+ * Check that an action executes its body and the finisher function and returns. Generic for simple, non-compound
+ * actions. Verifying execution is done with both direct and indirect means.
+ *
+ * @param {function} factory
+ * A factory function that yields an action.
+ * @param queue
+ */
+function simple_finally( factory, queue )
+{
+ /**
+ * @type {Action.Asynchronous_Action}
+ */
+ var d;
+ var sequence = 0;
+
+ function trial()
+ {
+ verify_state( d, "Running" );
+ sequence += 1;
+ }
+
+ function catcher()
+ {
+ fail( "Action under test should not throw an exception nor call its catcher." );
+ }
+
+ function finisher()
+ {
+ verify_state( d, "Done" );
+ sequence += 2;
+ }
+
+ queue.call( "Go phase.", function( callbacks )
+ {
+ var monitored_trial = callbacks.add( trial );
+ var monitored_finisher = callbacks.add( finisher );
+ d = factory( monitored_trial, monitored_finisher, catcher );
+ verify_state( d, "Ready" );
+ assertEquals( 0, sequence );
+ d.go();
+ verify_state( d, "Running" );
+ assertEquals( 0, sequence );
+ } );
+
+ queue.call( "End phase.", function()
+ {
+ verify_state( d, "Done" );
+ assertEquals( 3, sequence );
+ } );
+}
+
+/**
+ * Run a simple trial, one that throws an exception, with both finisher and catcher. Indirectly verify that all
+ * three run, but directly verify only the finally and catch.
+ *
+ * Generic for simple non-compound actions.
+ *
+ * @param {function} factory
+ * A factory function that yields an action.
+ * @param queue
+ */
+function simple_catch( factory, queue )
+{
+ /**
+ * @type {Action.Asynchronous_Action}
+ */
+ var d;
+ var sequence = 0;
+
+ function trial()
+ {
+ verify_state( d, "Running" );
+ assertEquals( 0, sequence );
+ sequence += 1;
+ throw new Error( "This error is part of the test." );
+ }
+
+ function catcher()
+ {
+ verify_state( d, "Exception" );
+ assertEquals( 1, sequence );
+ sequence += 2;
+ }
+
+ function finisher()
+ {
+ verify_state( d, "Exception" );
+ assertEquals( 3, sequence );
+ sequence += 4;
+ }
+
+ queue.call( "Go phase.", function( callbacks )
+ {
+ /* If we monitor the trial by adding to the callback list, it will report the exception as an error, which is not
+ * what we want. We indirectly test that it runs by incrementing the sequence number.
+ */
+ var monitored_catch = callbacks.add( catcher );
+ var monitored_finally = callbacks.add( finisher );
+ d = factory( trial, monitored_finally, monitored_catch );
+ verify_state( d, "Ready" );
+ assertEquals( 0, sequence );
+ d.go();
+ verify_state( d, "Running" );
+ assertEquals( 0, sequence );
+ } );
+
+ queue.call( "End phase.", function()
+ {
+ verify_state( d, "Exception" );
+ assertEquals( 7, sequence );
+ } );
+}
+
+/**
+ * Run a simple trial, one that throws an exception, with both finisher and catcher. Indirectly verify that all
+ * three run, but directly verify only the finally and catch.
+ *
+ * Generic for simple non-compound actions.
+ *
+ * @param {function} factory
+ * A factory function that yields an action.
+ * @param queue
+ */
+function simple_abort( factory, queue )
+{
+ /**
+ * @type {Action.Asynchronous_Action}
+ */
+ var d;
+ var sequence = 0;
+
+ function trial()
+ {
+ fail( "Executed trial when aborted." );
+ }
+
+ function catcher()
+ {
+ verify_state( d, "Exception" );
+ assertEquals( 0, sequence );
+ sequence += 2;
+ }
+
+ function finisher()
+ {
+ verify_state( d, "Exception" );
+ assertEquals( 2, sequence );
+ sequence += 4;
+ }
+
+ queue.call( "Go phase.", function( callbacks )
+ {
+ /* If we monitor the trial by adding to the callback list, it will report the exception as an error, which is not
+ * what we want. We indirectly test that it runs by incrementing the sequence number.
+ */
+ var monitored_catch = callbacks.add( catcher );
+ var monitored_finally = callbacks.add( finisher );
+ d = factory( trial, monitored_finally, monitored_catch );
+ verify_state( d, "Ready" );
+ assertEquals( 0, sequence );
+ d.go();
+ verify_state( d, "Running" );
+ assertEquals( 0, sequence );
+ d.abort();
+ verify_state( d, "Exception" );
+ assertEquals( 6, sequence );
+ } );
+}
+
+function simple_value( factory, queue )
+{
+ /**
+ * @type {Action.Asynchronous_Action}
+ */
+ var d;
+
+ function trial()
+ {
+ return [ 1, "two" ];
+ }
+
+ function finisher( a, b )
+ {
+ assertEquals( 1, a );
+ assertEquals( "two", b );
+ }
+
+ queue.call( "Phase[1] Go.", function( callbacks )
+ {
+ /* If we monitor the trial by adding to the callback list, it will report the exception as an error, which is not
+ * what we want. We indirectly test that it runs by incrementing the sequence number.
+ */
+ var monitored_finally = callbacks.add( finisher );
+ d = factory( trial, monitored_finally );
+ verify_state( d, "Ready" );
+ d.go();
+ verify_state( d, "Running" );
+ } );
+
+ queue.call( "Phase[2] Complete.", function()
+ {
+ verify_state( d, "Done" );
+ } );
+}
+
+//-------------------------------------------------------
+// Defer
+//-------------------------------------------------------
+/**
+ * Factory for Defer objects
+ * @param trial
+ * @param finisher
+ * @param catcher
+ * @return {Action.Defer}
+ */
+function defer_factory( trial, finisher, catcher )
+{
+ return new Action.Defer( trial, finisher, catcher );
+}
+
+ActionTest.prototype.test_defer_try = function( queue )
+{
+ simple_try( defer_factory, queue );
+};
+
+ActionTest.prototype.test_defer_finally = function( queue )
+{
+ simple_finally( defer_factory, queue );
+};
+
+ActionTest.prototype.test_defer_catch = function( queue )
+{
+ simple_catch( defer_factory, queue );
+};
+
+ActionTest.prototype.test_defer_abort = function( queue )
+{
+ simple_abort( defer_factory, queue );
+};
+
+ActionTest.prototype.test_simple_value = function( queue )
+{
+ simple_value( defer_factory, queue );
+};
+
+//-------------------------------------------------------
+// Delay
+//-------------------------------------------------------
+
+/**
+ * Factory for Delay objects
+ * @param delay
+ * @param trial
+ * @param finisher
+ * @param catcher
+ * @return {Action.Delay}
+ */
+function delay_factory( delay, trial, finisher, catcher )
+{
+ return new Action.Delay( trial, delay, finisher, catcher );
+}
+
+function simple_delay_factory( trial, finisher, catcher )
+{
+ return delay_factory( 2, trial, finisher, catcher );
+}
+
+ActionTest.prototype.test_delay_tries = function( queue )
+{
+ simple_try( simple_delay_factory, queue );
+};
+
+ActionTest.prototype.test_delay_finally = function( queue )
+{
+ simple_finally( simple_delay_factory, queue );
+};
+
+ActionTest.prototype.test_delay_catch = function( queue )
+{
+ simple_catch( simple_delay_factory, queue );
+};
+
+//-------------------------------------------------------
+// JM_Reporting implementation in Asynchronous_Action
+//-------------------------------------------------------
+var Asynchronous_Action__Test = AsyncTestCase( "Asynchronous_Action__Test" );
+
+function assert_reporting_is_empty( action )
+{
+ if ( "_end_watchers" in action )
+ {
+ // If the array exists, everything in it must be null.
+ var i;
+ for ( i = 0 ; i < action._end_watchers.length ; ++i )
+ {
+ if ( action._end_watchers[ i ] )
+ fail( "_end_watchers is still present and not empty." );
+ }
+ }
+ // If the _end_watchers array is absent, then the outbound reporting links are absent and the test passes.
+}
+
+
+/*
+ * Test plan: Make a simple action and join to it twice. Ensure that all mutual references are null everything
+ * completes.
+ */
+Asynchronous_Action__Test.prototype.test_reporting__refencerences_are_absent_upon_completion = function( queue )
+{
+ var defer, join1, join2;
+
+ function null_function()
+ {
+ }
+
+ function reporting_has_watchers( action, n )
+ {
+ assertTrue( "_end_watchers" in action );
+ assertEquals( n, action._end_watchers.length );
+ }
+
+ function attentive_is_empty( action )
+ {
+ assertNull( "joined_action should be null.", action.joined_action );
+ }
+
+ queue.call( "Phase[1]=Go.", function( callbacks )
+ {
+ // argument[2] is the number of times to expect this function
+ var monitored_trial = callbacks.add( null_function, 1, 5000, "defer trial function" );
+ var monitored_finisher = callbacks.add( null_function, 2, 5000, "join finisher function" );
+ defer = new Action.Defer( monitored_trial );
+ reporting_has_watchers( defer, 0 );
+ join1 = new Action.Join( defer, monitored_finisher );
+ join2 = new Action.Join( defer, monitored_finisher );
+ reporting_has_watchers( defer, 0 );
+ join1.go();
+ reporting_has_watchers( defer, 1 );
+ join2.go();
+ reporting_has_watchers( defer, 2 );
+ defer.go();
+ verify_state( defer, "Running" );
+ verify_state( join1, "Running" );
+ } );
+
+ queue.call( "Phase[2]=Complete.", function()
+ {
+ verify_state( defer, "Done" );
+ verify_state( join1, "Done" );
+ verify_state( join2, "Done" );
+ assert_reporting_is_empty( defer );
+ assert_reporting_is_empty( join1 );
+ assert_reporting_is_empty( join2 );
+ attentive_is_empty( join1 );
+ attentive_is_empty( join2 );
+ } );
+};
+
+Asynchronous_Action__Test.prototype.test_reporting__refencerences_are_absent_after_cancel = function( queue )
+{
+ var defer, join;
+
+ function null_function()
+ {
+ }
+
+ function reporting_has_watchers( action, n )
+ {
+ assertTrue( "_end_watchers" in action );
+ assertEquals( n, action._end_watchers.length );
+ }
+
+ function attentive_is_empty( action )
+ {
+ assertNull( "joined_action should be null.", action.joined_action );
+ }
+
+ queue.call( "Phase[1]=Go.", function( callbacks )
+ {
+ // argument[2] is the number of times to expect this function
+ var monitored_finisher = callbacks.add( null_function, 1, 5000, "join finisher function" );
+ defer = new Action.Defer( null_function );
+ reporting_has_watchers( defer, 0 );
+ join = new Action.Join( defer, monitored_finisher );
+ reporting_has_watchers( defer, 0 );
+ join.go();
+ reporting_has_watchers( defer, 1 );
+ verify_state( defer, "Ready" );
+ verify_state( join, "Running" );
+ join.cancel();
+ verify_state( defer, "Ready" );
+ verify_state( join, "Done" );
+ assert_reporting_is_empty( defer );
+ assert_reporting_is_empty( join );
+ attentive_is_empty( join );
+ } );
+};
+
+//-------------------------------------------------------
+// Join
+//-------------------------------------------------------
+ActionTest.prototype.test_join__throw_on_null_constructor_argument = function()
+{
+ try
+ {
+ new Action.Join( null );
+ fail( "Join must throw an exception when passed a null constructor argument." );
+ }
+ catch ( e )
+ {
+ // Exception is what's supposed to happen.
+ }
+};
+
+function join_test( variation, factory, queue )
+/**
+ * Combined variations on a number of simple join tests. There's an ordinary action and a join that it depends upon.
+ * Both are invoked. The ordinary action executes first and then the join does.
+ *
+ * There are four operations {construct, go} x {ordinary, join} and some ordering dependencies. The construction of the
+ * join must come after that of the ordinary action. Each invocation must come after construction. Given these
+ * constraints, there are three possible orders.
+ *
+ * @param variation
+ * @param factory
+ * @param queue
+ */
+{
+ var sequence = 0;
+ var defer = null, join = null;
+
+ function deferred_trial()
+ {
+ // The Defer instance should run first
+ verify_state( defer, "Running" );
+ assertEquals( "deferred trial. sequence", 0, sequence );
+ sequence += 1;
+ }
+
+ function join_catcher()
+ {
+ fail( "Joined catcher should not be called." );
+ }
+
+ function join_finisher( x )
+ {
+ assertEquals( "joined finisher. number of arguments", 1, arguments.length );
+ assertEquals( "joined finisher. first argument", "ABC", x );
+ // The Join instance should run second.
+ verify_state( defer, "Done" );
+ verify_state( join, "Done" );
+ assertEquals( "joined finisher. sequence", 1, sequence );
+ sequence += 2;
+ }
+
+ /*
+ * The split finisher supports the case where we need to construct a join in one phase but launch it in another.
+ * Because of a monitored function must complete in the same phase in which it set to be monitored, we need a static
+ * function that we can use to initialize and a variable we can initialize in a later phase.
+ */
+ var split_finisher_f;
+
+ function split_finisher()
+ {
+ if ( !split_finisher_f )
+ throw new Error( "split_finisher_f is not initialized" );
+ split_finisher_f.apply( this, arguments );
+ }
+
+ function monitored_join_finisher( callbacks )
+ {
+ return callbacks.add( join_finisher, null, 2000, "join finisher" );
+ }
+
+ function make_join( finisher )
+ {
+ join = factory( defer, finisher, join_catcher );
+ verify_state( join, "Ready" );
+ }
+
+ function join_go()
+ {
+ join.go( "ABC" );
+ }
+
+ queue.call( "Phase[1]=Go.", function( callbacks )
+ {
+ /*
+ * Construction of the Defer instance always has to come first.
+ */
+ defer = new Action.Defer( callbacks.add( deferred_trial, null, 2000, "defer_trial" ) );
+ verify_state( defer, "Ready" );
+ switch ( variation )
+ {
+ case "existing ready":
+ /*
+ * Invoke the join before the defer has been invoked. This tests joining to a ready action.
+ */
+ make_join( monitored_join_finisher( callbacks ) );
+ join_go();
+ defer.go();
+ verify_state( defer, "Running" );
+ verify_state( join, "Running" );
+ break;
+ case "existing running":
+ /*
+ * Invoke the join after the defer has been invoked. This tests joining to a running action.
+ */
+ make_join( monitored_join_finisher( callbacks ) );
+ defer.go();
+ join_go();
+ /*
+ * The defer is running, but it hasn't completed yet, so the join hasn't completed yet. Contrast this with
+ * the split version, where the defer action has already completed when we invoke the join.
+ */
+ verify_state( defer, "Running" );
+ verify_state( join, "Running" );
+ break;
+ case "existing running split":
+ /*
+ * Invoke the defer after the join is invoked, but invoke the join later. This test ensures that the join
+ * does not complete prematurely.
+ */
+ make_join( split_finisher );
+ defer.go();
+ verify_state( defer, "Running" );
+ verify_state( join, "Ready" );
+ break;
+ case "new running":
+ /*
+ * Construct the join after defer has already been invoked.
+ */
+ defer.go();
+ make_join( monitored_join_finisher( callbacks ) );
+ join_go();
+ verify_state( defer, "Running" );
+ verify_state( join, "Running" );
+ break;
+ default:
+ throw new Error( "unknown variation" );
+ }
+ } );
+
+ queue.call( "Phase[2]=Intermediate.", function( callbacks )
+ {
+ switch ( variation )
+ {
+ case "existing running split":
+ split_finisher_f = monitored_join_finisher( callbacks );
+ /*
+ * The join should not yet have run at this point.
+ */
+ verify_state( defer, "Done" );
+ verify_state( join, "Ready" );
+ /*
+ * We invoke the join on a completed action. As a result, the join will complete immediately.
+ */
+ join_go();
+ verify_state( defer, "Done" );
+ verify_state( join, "Done" );
+ break;
+ default:
+ /*
+ * We're already verified the variation in the first phase. Some variations have no intermediate phase.
+ */
+ break;
+ }
+ } );
+
+ queue.call( "Phase[3]=End.", function()
+ {
+ verify_state( defer, "Done" );
+ verify_state( join, "Done" );
+ assertEquals( 3, sequence );
+ } );
+}
+
+//-------------------------------------------------------
+// Join
+//-------------------------------------------------------
+/**
+ * @param action
+ * @param [finisher]
+ * @param [catcher]
+ * @return {Action.Join}
+ */
+function join_factory( action, finisher, catcher )
+{
+ return new Action.Join( action, finisher, catcher );
+}
+
+ActionTest__Join = AsyncTestCase( "Join" );
+
+ActionTest__Join.prototype.test_join__existing_join_to_new_defer_instance = function( queue )
+{
+ join_test( "existing ready", join_factory, queue );
+};
+
+ActionTest__Join.prototype.test_join__existing_join_to_running_defer_instance = function( queue )
+{
+ join_test( "existing running", join_factory, queue );
+};
+
+ActionTest__Join.prototype.test_join__existing_join_to_running_defer_instance__split = function( queue )
+{
+ join_test( "existing running split", join_factory, queue );
+};
+
+ActionTest__Join.prototype.test_join__new_join_to_running_defer_instance = function( queue )
+{
+ join_test( "new running", join_factory, queue )
+};
+
+//-------------------------------------------------------
+// ActionTest__Join_Timeout
+//-------------------------------------------------------
+/**
+ * Join_Timeout factory set at 15 seconds, which should be enough longer than callback limit, set to 2 seconds, to
+ * avoid false negatives.
+ *
+ * @param action
+ * @param [finisher]
+ * @param [catcher]
+ * @return {Action.Join_Timeout}
+ */
+function join_timeout_factory( action, finisher, catcher )
+{
+ return new Action.Join_Timeout( action, 15000, finisher, catcher );
+}
+
+ActionTest__Join_Timeout = AsyncTestCase( "Join_Timeout" );
+
+ActionTest__Join_Timeout.prototype.test_join_timeout__existing_join_to_new_defer_instance = function( queue )
+{
+ join_test( "existing ready", join_timeout_factory, queue );
+};
+
+ActionTest__Join_Timeout.prototype.test_join_timeout__existing_join_to_running_defer_instance = function( queue )
+{
+ join_test( "existing running", join_timeout_factory, queue );
+};
+
+ActionTest__Join_Timeout.prototype.test_join_timeout__existing_join_to_running_defer_instance__split = function( queue )
+{
+ join_test( "existing running split", join_timeout_factory, queue );
+};
+
+ActionTest__Join_Timeout.prototype.test_join_timeout__new_join_to_running_defer_instance = function( queue )
+{
+ join_test( "new running", join_timeout_factory, queue )
+};
+
+ActionTest__Join_Timeout.prototype.test_join_timeout__simple_timeout = function( queue )
+{
+ var sequence = 0;
+
+ function defer_trial()
+ {
+ fail( "The trial on the defer object should not be called." );
+ }
+
+ function join_catch()
+ {
+ assertEquals( 0, sequence );
+ sequence += 1;
+ verify_state( defer, "Ready" );
+ verify_state( join, "Exception" );
+ assertTrue( "Join should be exceptional.", !join.completed_well );
+ }
+
+ function join_finally()
+ {
+ }
+
+ /*
+ * Construct the defer object outside the test queue because it does not generate a callback in this case.
+ */
+ var defer = new Action.Defer( defer_trial );
+ verify_state( defer, "Ready" );
+ var join;
+ queue.call( "Phase[1]=Go.", function( callbacks )
+ {
+ /*
+ * Timeout is set to a very short time.
+ */
+ var monitored_join_catch = callbacks.add( join_catch, null, 5000, "join catch" );
+ join = new Action.Join_Timeout( defer, 1, join_finally, monitored_join_catch );
+ join.go();
+ } );
+ /*
+ * No need to launch the Defer action. If the timeout doesn't trigger the catcher, the test fails.
+ */
+};
+
« no previous file with comments | « test/jsTestDriver.conf ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld