예제 #1
0
    def test_cryptosystem_new_with_task_monitor(self):
        """
        Test that EGCryptoSystem.new(...) correctly reports progress using 
        task monitor.
        """
        # Get new counter and logger objects
        counter = Counter()
        logger = Logger()

        # Create a task monitor
        task_monitor = TaskMonitor()

        # Register callbacks for:

        # 1) logging subtask creation,
        def task_start_cb(tm):
            tm_p = tm.parent
            msg = "New task started: \"%s\" " \
                  "(Subtask #%d of %d for task \"%s\")\n" % \
                  (tm.task_name, tm_p.current_subtask_num, \
                   tm_p.num_subtasks, tm_p.task_name)
            logger.log(msg)

        task_monitor.add_on_task_start_callback(task_start_cb)

        # 2) logging subtask completion,
        def task_end_cb(tm):
            msg = "Task completed: \"%s\"\n" % tm.task_name
            logger.log(msg)

        task_monitor.add_on_task_end_callback(task_end_cb)

        # 3) counting the number of ticks of progress
        task_monitor.add_on_tick_callback(lambda tm: counter.increment(),
                                          num_ticks=1)

        # Note:
        # EGCryptoSystem.new(...) does not provide progress percent monitoring

        # We call EGCryptoSystem.new(...) with our task monitor object
        # We use the *insecure* size of 256bits for performance reasons
        EGCryptoSystem.new(nbits=256, task_monitor=task_monitor)

        # Check that the logged match the expected output of our callbacks:
        expected_log = \
"""New task started: \"Generate safe prime\" (Subtask #1 of 1 for task \"Root\")
Task completed: \"Generate safe prime\"
New task started: \"Obtain a generator for the cyclic group\" (Subtask #2 of 2 for task \"Root\")
Task completed: \"Obtain a generator for the cyclic group\"
"""
        self.assertEqual(str(logger), expected_log)

        # Also, each of the two subtask produces a progress tick before testing
        # each safe prime or generator candidate (respectively). So counter
        # must have registered at least two ticks (likely more)
        self.assertTrue(counter.value >= 2)
예제 #2
0
    def test_cryptosystem_new_with_task_monitor(self):
        """
        Test that EGCryptoSystem.new(...) correctly reports progress using 
        task monitor.
        """
        # Get new counter and logger objects
        counter = Counter()
        logger = Logger()
        
        # Create a task monitor
        task_monitor = TaskMonitor()
        
        # Register callbacks for:
        
        # 1) logging subtask creation,
        def task_start_cb(tm):
            tm_p = tm.parent
            msg = "New task started: \"%s\" " \
                  "(Subtask #%d of %d for task \"%s\")\n" % \
                  (tm.task_name, tm_p.current_subtask_num, \
                   tm_p.num_subtasks, tm_p.task_name)
            logger.log(msg)
           
        task_monitor.add_on_task_start_callback(task_start_cb)
        
        # 2) logging subtask completion,
        def task_end_cb(tm):
            msg = "Task completed: \"%s\"\n" % tm.task_name
            logger.log(msg)
           
        task_monitor.add_on_task_end_callback(task_end_cb)
        
        # 3) counting the number of ticks of progress
        task_monitor.add_on_tick_callback(lambda tm: counter.increment(), 
                                               num_ticks = 1)
        
        # Note:
        # EGCryptoSystem.new(...) does not provide progress percent monitoring 
        
        # We call EGCryptoSystem.new(...) with our task monitor object 
        # We use the *insecure* size of 256bits for performance reasons
        EGCryptoSystem.new(nbits=256, task_monitor=task_monitor)
        
        # Check that the logged match the expected output of our callbacks:
        expected_log = \
"""New task started: \"Generate safe prime\" (Subtask #1 of 1 for task \"Root\")
Task completed: \"Generate safe prime\"
New task started: \"Obtain a generator for the cyclic group\" (Subtask #2 of 2 for task \"Root\")
Task completed: \"Obtain a generator for the cyclic group\"
"""
        self.assertEqual(str(logger),expected_log)
        
        # Also, each of the two subtask produces a progress tick before testing 
        # each safe prime or generator candidate (respectively). So counter 
        # must have registered at least two ticks (likely more)
        self.assertTrue(counter.value >= 2)
예제 #3
0
def run_tool(nbits, filename, name, description):
    """
	Runs the plonevote.gen_cryptosys tool and generates a new cryptosystem.
	"""

    # Define callbacks for the TaskMonitor for progress monitoring
    def cb_task_start(task):
        print task.task_name + ":"

    def cb_task_progress(task):
        sys.stdout.write(".")
        sys.stdout.flush()

    def cb_task_end(task):
        print ""

    # Create new TaskMonitor and register the callbacks
    taskmon = TaskMonitor()
    taskmon.add_on_task_start_callback(cb_task_start)
    taskmon.add_on_tick_callback(cb_task_progress)
    taskmon.add_on_task_end_callback(cb_task_end)

    # Generate a new cryptosystem of the requested size
    try:
        cryptosys = EGCryptoSystem.new(nbits, task_monitor=taskmon)
    except KeyLengthTooLowError:
        print "ERROR: The given bit size does not meet PloneVoteCryptoLib "\
           "minimum security requirements (too short)."
    except KeyLengthNonBytableError:
        print "ERROR: The given bit size must be a multiple of 8."

    # Save the cryptosystem to file
    print "\nSaving cryptosystem to %s..." % filename,

    cryptosys.to_file(name, description, filename)

    print "SAVED.\n"
예제 #4
0
def run_tool(nbits, filename, name, description):
	"""
	Runs the plonevote.gen_cryptosys tool and generates a new cryptosystem.
	"""
	# Define callbacks for the TaskMonitor for progress monitoring
	def cb_task_start(task):
		print task.task_name + ":"

	def cb_task_progress(task):
		sys.stdout.write(".")
		sys.stdout.flush()

	def cb_task_end(task):
		print ""
	
	# Create new TaskMonitor and register the callbacks
	taskmon = TaskMonitor()
	taskmon.add_on_task_start_callback(cb_task_start)
	taskmon.add_on_tick_callback(cb_task_progress)
	taskmon.add_on_task_end_callback(cb_task_end)
	
	# Generate a new cryptosystem of the requested size
	try:
		cryptosys = EGCryptoSystem.new(nbits, task_monitor = taskmon)
	except KeyLengthTooLowError:
		print "ERROR: The given bit size does not meet PloneVoteCryptoLib "\
			  "minimum security requirements (too short)."
	except KeyLengthNonBytableError:
		print "ERROR: The given bit size must be a multiple of 8."
	
	# Save the cryptosystem to file
	print "\nSaving cryptosystem to %s..." % filename,
	
	cryptosys.to_file(name, description, filename)
	
	print "SAVED.\n"
예제 #5
0
class TestTaskMonitor(unittest.TestCase):
    """
    Test the class: plonevotecryptolib.utilities.TaskMonitor.TaskMonitor
    """
    
    def setUp(self):
        """
        Unit test setup method.
        """
        self.task_monitor = TaskMonitor()
    
    def test_report_ticks(self):
        """
        Test that TaskMonitor can be used to report the number of steps (ticks) 
        performed by a "TaskMonitor-enabled" function.
        """
        # We register a callback that increments a counter by one and is 
        # called every 10 ticks
        counter = Counter()
        self.task_monitor.add_on_tick_callback(lambda tm: counter.increment(), 
                                               num_ticks = 10)
        
        # Now, we call the tme_fibonacci function with our task monitor, asking 
        # for the 100th Fibonacci number
        tme_fibonacci(100, self.task_monitor)
        
        # The counter should have been updated once for every 10 numbers in the 
        # sequence from 0 to our desired number, inclusive. So counter should 
        # be 100/10 = 10.
        self.assertEqual(counter.value, 10)
          
    def test_report_percent(self):
        """
        Test that TaskMonitor can be used to report the completion percentage 
        of a "TaskMonitor-enabled" function that allows percentage monitoring.
        """
        # We register a callback that increments a counter by one and is 
        # called every 5% progress
        counter = Counter()
        self.task_monitor.add_on_progress_percent_callback( \
                            lambda tm: counter.increment(), 
                            percent_span = 5.0)
        
        # Now, we call the tme_fibonacci function with our task monitor, asking 
        # for the 100th Fibonacci number
        tme_fibonacci(100, self.task_monitor)
        
        # The counter should have been updated once for every 5% advance in the 
        # called function. This gives us 20 updates.
        self.assertEqual(counter.value, 20)
        
    def test_task_without_percent_reporting(self):
        """
        Test how add_on_tick_callback(...) *and* 
        add_on_progress_percent_callback(...) work when the task does not 
        provide progress percent reporting (ie. a variable length task).
        """
        # We register a callback that increments a counter by one and is 
        # called every 10 ticks
        counter1 = Counter()
        self.task_monitor.add_on_tick_callback(lambda tm: counter1.increment(), 
                                               num_ticks = 10)
        
        # We register a callback that increments a counter by one and is 
        # called every 5% progress
        counter2 = Counter()
        self.task_monitor.add_on_progress_percent_callback( \
                            lambda tm: counter2.increment(), 
                            percent_span = 5.0)
        
        # Now, we call the tme_search_in_list function with our task monitor, 
        # asking for the position of element 100 in range(0,300)
        tme_search_in_list(range(0,300), 100, self.task_monitor)
        
        # counter1 should have been update 10 times, one for each 10 ticks of 
        # a total of 100 ticks (100 is the 100th element of range(0,300))
        self.assertEqual(counter1.value, 10)
        
        # counter2 should have never been updated, since tme_search_in_list 
        # does not provide progress percent monitoring
        self.assertEqual(counter2.value, 0)
        
    def test_subtask_reporting(self):
        """
        Test complex reporting on a task with subtasks.
        This includes:
            * Test task start/end callbacks.
            * Test getting the name and number of each subtask.
            * Test reporting percent progress of the whole task and each 
              subtask.
        """
        # Construct a logger object
        logger = Logger()
        
        # Set up callbacks to log messages on:
        
        # subtask creation,
        def task_start_cb(tm):
            tm_p = tm.parent
            msg = "New task started: \"%s\" " \
                  "(Subtask #%d of %d for task \"%s\")\n" % \
                  (tm.task_name, tm_p.current_subtask_num, \
                   tm_p.num_subtasks, tm_p.task_name)
            logger.log(msg)
           
        self.task_monitor.add_on_task_start_callback(task_start_cb)
        
        # subtask completion,
        def task_end_cb(tm):
            msg = "Task completed: \"%s\"\n" % tm.task_name
            logger.log(msg)
           
        self.task_monitor.add_on_task_end_callback(task_end_cb)
        
        # and progress percent (20%)
        def task_percent_cb(tm):
            msg = "\"%s\"... %d%% completed.\n" % \
                  (tm.task_name, tm.get_percent_completed())
            logger.log(msg)
           
        self.task_monitor.add_on_progress_percent_callback(task_percent_cb)
        
        # We also count the total number of ticks
        counter = Counter()
        self.task_monitor.add_on_tick_callback(lambda tm: counter.increment(), 
                                               num_ticks = 1)
        
        # Call tme_fibonacci_subtasks using the task monitor
        tme_fibonacci_subtasks(self.task_monitor)
        
        # and compare the logged output with the expected one
        expected_output = \
"""New task started: \"Multiple Fibonacci Tasks\" (Subtask #1 of 1 for task \"Root\")
New task started: \"Fibonacci(300)\" (Subtask #1 of 3 for task \"Multiple Fibonacci Tasks\")
New task started: \"Calculating the 300th Fibonacci number\" (Subtask #1 of 1 for task \"Fibonacci(300)\")
\"Calculating the 300th Fibonacci number\"... 5% completed.
\"Calculating the 300th Fibonacci number\"... 10% completed.
\"Calculating the 300th Fibonacci number\"... 15% completed.
\"Calculating the 300th Fibonacci number\"... 20% completed.
\"Calculating the 300th Fibonacci number\"... 25% completed.
\"Calculating the 300th Fibonacci number\"... 30% completed.
\"Calculating the 300th Fibonacci number\"... 35% completed.
\"Calculating the 300th Fibonacci number\"... 40% completed.
\"Calculating the 300th Fibonacci number\"... 45% completed.
\"Calculating the 300th Fibonacci number\"... 50% completed.
\"Calculating the 300th Fibonacci number\"... 55% completed.
\"Calculating the 300th Fibonacci number\"... 60% completed.
\"Calculating the 300th Fibonacci number\"... 65% completed.
\"Calculating the 300th Fibonacci number\"... 70% completed.
\"Calculating the 300th Fibonacci number\"... 75% completed.
\"Calculating the 300th Fibonacci number\"... 80% completed.
\"Calculating the 300th Fibonacci number\"... 85% completed.
\"Calculating the 300th Fibonacci number\"... 90% completed.
\"Calculating the 300th Fibonacci number\"... 95% completed.
\"Calculating the 300th Fibonacci number\"... 100% completed.
Task completed: \"Calculating the 300th Fibonacci number\"
Task completed: \"Fibonacci(300)\"
New task started: \"Fibonacci(500)\" (Subtask #2 of 3 for task \"Multiple Fibonacci Tasks\")
New task started: \"Calculating the 500th Fibonacci number\" (Subtask #1 of 1 for task \"Fibonacci(500)\")
\"Calculating the 500th Fibonacci number\"... 5% completed.
\"Calculating the 500th Fibonacci number\"... 10% completed.
\"Calculating the 500th Fibonacci number\"... 15% completed.
\"Calculating the 500th Fibonacci number\"... 20% completed.
\"Calculating the 500th Fibonacci number\"... 25% completed.
\"Calculating the 500th Fibonacci number\"... 30% completed.
\"Calculating the 500th Fibonacci number\"... 35% completed.
\"Calculating the 500th Fibonacci number\"... 40% completed.
\"Calculating the 500th Fibonacci number\"... 45% completed.
\"Calculating the 500th Fibonacci number\"... 50% completed.
\"Calculating the 500th Fibonacci number\"... 55% completed.
\"Calculating the 500th Fibonacci number\"... 60% completed.
\"Calculating the 500th Fibonacci number\"... 65% completed.
\"Calculating the 500th Fibonacci number\"... 70% completed.
\"Calculating the 500th Fibonacci number\"... 75% completed.
\"Calculating the 500th Fibonacci number\"... 80% completed.
\"Calculating the 500th Fibonacci number\"... 85% completed.
\"Calculating the 500th Fibonacci number\"... 90% completed.
\"Calculating the 500th Fibonacci number\"... 95% completed.
\"Calculating the 500th Fibonacci number\"... 100% completed.
Task completed: \"Calculating the 500th Fibonacci number\"
Task completed: \"Fibonacci(500)\"
New task started: \"Fibonacci(200)\" (Subtask #3 of 3 for task \"Multiple Fibonacci Tasks\")
New task started: \"Calculating the 200th Fibonacci number\" (Subtask #1 of 1 for task \"Fibonacci(200)\")
\"Calculating the 200th Fibonacci number\"... 5% completed.
\"Calculating the 200th Fibonacci number\"... 10% completed.
\"Calculating the 200th Fibonacci number\"... 15% completed.
\"Calculating the 200th Fibonacci number\"... 20% completed.
\"Calculating the 200th Fibonacci number\"... 25% completed.
\"Calculating the 200th Fibonacci number\"... 30% completed.
\"Calculating the 200th Fibonacci number\"... 35% completed.
\"Calculating the 200th Fibonacci number\"... 40% completed.
\"Calculating the 200th Fibonacci number\"... 45% completed.
\"Calculating the 200th Fibonacci number\"... 50% completed.
\"Calculating the 200th Fibonacci number\"... 55% completed.
\"Calculating the 200th Fibonacci number\"... 60% completed.
\"Calculating the 200th Fibonacci number\"... 65% completed.
\"Calculating the 200th Fibonacci number\"... 70% completed.
\"Calculating the 200th Fibonacci number\"... 75% completed.
\"Calculating the 200th Fibonacci number\"... 80% completed.
\"Calculating the 200th Fibonacci number\"... 85% completed.
\"Calculating the 200th Fibonacci number\"... 90% completed.
\"Calculating the 200th Fibonacci number\"... 95% completed.
\"Calculating the 200th Fibonacci number\"... 100% completed.
Task completed: \"Calculating the 200th Fibonacci number\"
Task completed: \"Fibonacci(200)\"
Task completed: \"Multiple Fibonacci Tasks\"
"""

        self.assertEqual(str(logger), expected_output)
        
        # Including ticks for the 0th number in each succession..
        self.assertEqual(counter.value, 1003)

    def test_remove_callback(self):
        """
        Test the remove_callback method of TaskMonitor.
        """
        # We will run a new Task Monitor to monitor the tme_fibonacci_subtasks 
        # function.
        
        # First, we define a callback to count all ticks for our task
        counter = Counter()
        def tick_counter_cb(tm):
            counter.increment()
         
        self.task_monitor.add_on_tick_callback(tick_counter_cb, 
                                               num_ticks = 1)
                                               
        # Then, we define a callback to be called whenever a subtask starts
        # This callback will remove our previous "tick counter" callback as 
        # soon as the task named "Fibonacci(500)" starts.
        def task_start_cb(tm):
            if(tm.task_name == "Fibonacci(500)"):
                tm.remove_callback(tick_counter_cb)
           
        self.task_monitor.add_on_task_start_callback(task_start_cb)
        
        # Note that since remove_callback removes the callback solely for the 
        # task for which it was called and its subtasks, our tick counter 
        # callback will still be called for the "Fibonacci(200)" subtask of 
        # tme_fibonacci_subtasks.
        
        # Lets run tme_fibonacci_subtasks:
        tme_fibonacci_subtasks(self.task_monitor)
        
        # Check that the counter has registered 301 ticks for Fibonacci(300) 
        # (and subtasks) and 201 for Fibonacci(200) (and subtasks), but no 
        # ticks for Fibonacci(500):
        self.assertEqual(counter.value, 502)
예제 #6
0
class TestTaskMonitor(unittest.TestCase):
    """
    Test the class: plonevotecryptolib.utilities.TaskMonitor.TaskMonitor
    """
    def setUp(self):
        """
        Unit test setup method.
        """
        self.task_monitor = TaskMonitor()

    def test_report_ticks(self):
        """
        Test that TaskMonitor can be used to report the number of steps (ticks) 
        performed by a "TaskMonitor-enabled" function.
        """
        # We register a callback that increments a counter by one and is
        # called every 10 ticks
        counter = Counter()
        self.task_monitor.add_on_tick_callback(lambda tm: counter.increment(),
                                               num_ticks=10)

        # Now, we call the tme_fibonacci function with our task monitor, asking
        # for the 100th Fibonacci number
        tme_fibonacci(100, self.task_monitor)

        # The counter should have been updated once for every 10 numbers in the
        # sequence from 0 to our desired number, inclusive. So counter should
        # be 100/10 = 10.
        self.assertEqual(counter.value, 10)

    def test_report_percent(self):
        """
        Test that TaskMonitor can be used to report the completion percentage 
        of a "TaskMonitor-enabled" function that allows percentage monitoring.
        """
        # We register a callback that increments a counter by one and is
        # called every 5% progress
        counter = Counter()
        self.task_monitor.add_on_progress_percent_callback( \
                            lambda tm: counter.increment(),
                            percent_span = 5.0)

        # Now, we call the tme_fibonacci function with our task monitor, asking
        # for the 100th Fibonacci number
        tme_fibonacci(100, self.task_monitor)

        # The counter should have been updated once for every 5% advance in the
        # called function. This gives us 20 updates.
        self.assertEqual(counter.value, 20)

    def test_task_without_percent_reporting(self):
        """
        Test how add_on_tick_callback(...) *and* 
        add_on_progress_percent_callback(...) work when the task does not 
        provide progress percent reporting (ie. a variable length task).
        """
        # We register a callback that increments a counter by one and is
        # called every 10 ticks
        counter1 = Counter()
        self.task_monitor.add_on_tick_callback(lambda tm: counter1.increment(),
                                               num_ticks=10)

        # We register a callback that increments a counter by one and is
        # called every 5% progress
        counter2 = Counter()
        self.task_monitor.add_on_progress_percent_callback( \
                            lambda tm: counter2.increment(),
                            percent_span = 5.0)

        # Now, we call the tme_search_in_list function with our task monitor,
        # asking for the position of element 100 in range(0,300)
        tme_search_in_list(list(range(0, 300)), 100, self.task_monitor)

        # counter1 should have been update 10 times, one for each 10 ticks of
        # a total of 100 ticks (100 is the 100th element of range(0,300))
        self.assertEqual(counter1.value, 10)

        # counter2 should have never been updated, since tme_search_in_list
        # does not provide progress percent monitoring
        self.assertEqual(counter2.value, 0)

    def test_subtask_reporting(self):
        """
        Test complex reporting on a task with subtasks.
        This includes:
            * Test task start/end callbacks.
            * Test getting the name and number of each subtask.
            * Test reporting percent progress of the whole task and each 
              subtask.
        """
        # Construct a logger object
        logger = Logger()

        # Set up callbacks to log messages on:

        # subtask creation,
        def task_start_cb(tm):
            tm_p = tm.parent
            msg = "New task started: \"%s\" " \
                  "(Subtask #%d of %d for task \"%s\")\n" % \
                  (tm.task_name, tm_p.current_subtask_num, \
                   tm_p.num_subtasks, tm_p.task_name)
            logger.log(msg)

        self.task_monitor.add_on_task_start_callback(task_start_cb)

        # subtask completion,
        def task_end_cb(tm):
            msg = "Task completed: \"%s\"\n" % tm.task_name
            logger.log(msg)

        self.task_monitor.add_on_task_end_callback(task_end_cb)

        # and progress percent (20%)
        def task_percent_cb(tm):
            msg = "\"%s\"... %d%% completed.\n" % \
                  (tm.task_name, tm.get_percent_completed())
            logger.log(msg)

        self.task_monitor.add_on_progress_percent_callback(task_percent_cb)

        # We also count the total number of ticks
        counter = Counter()
        self.task_monitor.add_on_tick_callback(lambda tm: counter.increment(),
                                               num_ticks=1)

        # Call tme_fibonacci_subtasks using the task monitor
        tme_fibonacci_subtasks(self.task_monitor)

        # and compare the logged output with the expected one
        expected_output = \
"""New task started: \"Multiple Fibonacci Tasks\" (Subtask #1 of 1 for task \"Root\")
New task started: \"Fibonacci(300)\" (Subtask #1 of 3 for task \"Multiple Fibonacci Tasks\")
New task started: \"Calculating the 300th Fibonacci number\" (Subtask #1 of 1 for task \"Fibonacci(300)\")
\"Calculating the 300th Fibonacci number\"... 5% completed.
\"Calculating the 300th Fibonacci number\"... 10% completed.
\"Calculating the 300th Fibonacci number\"... 15% completed.
\"Calculating the 300th Fibonacci number\"... 20% completed.
\"Calculating the 300th Fibonacci number\"... 25% completed.
\"Calculating the 300th Fibonacci number\"... 30% completed.
\"Calculating the 300th Fibonacci number\"... 35% completed.
\"Calculating the 300th Fibonacci number\"... 40% completed.
\"Calculating the 300th Fibonacci number\"... 45% completed.
\"Calculating the 300th Fibonacci number\"... 50% completed.
\"Calculating the 300th Fibonacci number\"... 55% completed.
\"Calculating the 300th Fibonacci number\"... 60% completed.
\"Calculating the 300th Fibonacci number\"... 65% completed.
\"Calculating the 300th Fibonacci number\"... 70% completed.
\"Calculating the 300th Fibonacci number\"... 75% completed.
\"Calculating the 300th Fibonacci number\"... 80% completed.
\"Calculating the 300th Fibonacci number\"... 85% completed.
\"Calculating the 300th Fibonacci number\"... 90% completed.
\"Calculating the 300th Fibonacci number\"... 95% completed.
\"Calculating the 300th Fibonacci number\"... 100% completed.
Task completed: \"Calculating the 300th Fibonacci number\"
Task completed: \"Fibonacci(300)\"
New task started: \"Fibonacci(500)\" (Subtask #2 of 3 for task \"Multiple Fibonacci Tasks\")
New task started: \"Calculating the 500th Fibonacci number\" (Subtask #1 of 1 for task \"Fibonacci(500)\")
\"Calculating the 500th Fibonacci number\"... 5% completed.
\"Calculating the 500th Fibonacci number\"... 10% completed.
\"Calculating the 500th Fibonacci number\"... 15% completed.
\"Calculating the 500th Fibonacci number\"... 20% completed.
\"Calculating the 500th Fibonacci number\"... 25% completed.
\"Calculating the 500th Fibonacci number\"... 30% completed.
\"Calculating the 500th Fibonacci number\"... 35% completed.
\"Calculating the 500th Fibonacci number\"... 40% completed.
\"Calculating the 500th Fibonacci number\"... 45% completed.
\"Calculating the 500th Fibonacci number\"... 50% completed.
\"Calculating the 500th Fibonacci number\"... 55% completed.
\"Calculating the 500th Fibonacci number\"... 60% completed.
\"Calculating the 500th Fibonacci number\"... 65% completed.
\"Calculating the 500th Fibonacci number\"... 70% completed.
\"Calculating the 500th Fibonacci number\"... 75% completed.
\"Calculating the 500th Fibonacci number\"... 80% completed.
\"Calculating the 500th Fibonacci number\"... 85% completed.
\"Calculating the 500th Fibonacci number\"... 90% completed.
\"Calculating the 500th Fibonacci number\"... 95% completed.
\"Calculating the 500th Fibonacci number\"... 100% completed.
Task completed: \"Calculating the 500th Fibonacci number\"
Task completed: \"Fibonacci(500)\"
New task started: \"Fibonacci(200)\" (Subtask #3 of 3 for task \"Multiple Fibonacci Tasks\")
New task started: \"Calculating the 200th Fibonacci number\" (Subtask #1 of 1 for task \"Fibonacci(200)\")
\"Calculating the 200th Fibonacci number\"... 5% completed.
\"Calculating the 200th Fibonacci number\"... 10% completed.
\"Calculating the 200th Fibonacci number\"... 15% completed.
\"Calculating the 200th Fibonacci number\"... 20% completed.
\"Calculating the 200th Fibonacci number\"... 25% completed.
\"Calculating the 200th Fibonacci number\"... 30% completed.
\"Calculating the 200th Fibonacci number\"... 35% completed.
\"Calculating the 200th Fibonacci number\"... 40% completed.
\"Calculating the 200th Fibonacci number\"... 45% completed.
\"Calculating the 200th Fibonacci number\"... 50% completed.
\"Calculating the 200th Fibonacci number\"... 55% completed.
\"Calculating the 200th Fibonacci number\"... 60% completed.
\"Calculating the 200th Fibonacci number\"... 65% completed.
\"Calculating the 200th Fibonacci number\"... 70% completed.
\"Calculating the 200th Fibonacci number\"... 75% completed.
\"Calculating the 200th Fibonacci number\"... 80% completed.
\"Calculating the 200th Fibonacci number\"... 85% completed.
\"Calculating the 200th Fibonacci number\"... 90% completed.
\"Calculating the 200th Fibonacci number\"... 95% completed.
\"Calculating the 200th Fibonacci number\"... 100% completed.
Task completed: \"Calculating the 200th Fibonacci number\"
Task completed: \"Fibonacci(200)\"
Task completed: \"Multiple Fibonacci Tasks\"
"""

        self.assertEqual(str(logger), expected_output)

        # Including ticks for the 0th number in each succession..
        self.assertEqual(counter.value, 1003)

    def test_remove_callback(self):
        """
        Test the remove_callback method of TaskMonitor.
        """
        # We will run a new Task Monitor to monitor the tme_fibonacci_subtasks
        # function.

        # First, we define a callback to count all ticks for our task
        counter = Counter()

        def tick_counter_cb(tm):
            counter.increment()

        self.task_monitor.add_on_tick_callback(tick_counter_cb, num_ticks=1)

        # Then, we define a callback to be called whenever a subtask starts
        # This callback will remove our previous "tick counter" callback as
        # soon as the task named "Fibonacci(500)" starts.
        def task_start_cb(tm):
            if (tm.task_name == "Fibonacci(500)"):
                tm.remove_callback(tick_counter_cb)

        self.task_monitor.add_on_task_start_callback(task_start_cb)

        # Note that since remove_callback removes the callback solely for the
        # task for which it was called and its subtasks, our tick counter
        # callback will still be called for the "Fibonacci(200)" subtask of
        # tme_fibonacci_subtasks.

        # Lets run tme_fibonacci_subtasks:
        tme_fibonacci_subtasks(self.task_monitor)

        # Check that the counter has registered 301 ticks for Fibonacci(300)
        # (and subtasks) and 201 for Fibonacci(200) (and subtasks), but no
        # ticks for Fibonacci(500):
        self.assertEqual(counter.value, 502)