Exemplo n.º 1
0
class SprintControllerTestForRemainingTimes(AgiloTestCase):
    # Most of the test cases were previously in sprint_test 
    # (agilo.scrum.sprint.tests) However, when we moved the 
    # functionality to a command, these test cases needed to be ported 
    # so that they test the command instead of the direct model 
    # implementation. Several things are still artifacts from that old 
    # test setup.
    
    def setUp(self):
        self.super()
        self.controller = SprintController(self.env)
        
        self.team = self.teh.create_team('Test team')
        # Preventing a RuleValidationException (Owner not Team Member)
        self.teh.create_member(name='tester', team=self.team)
        self.sprint = self.teh.create_sprint("Test Sprint", team=self.team)
        
        self.metrics = TeamMetrics(self.env, self.sprint, self.team)
        self.metrics[Key.RT_USP_RATIO] = 1.5
        self.metrics.save()
        
        self.bmm = BacklogModelManager(self.env)
        self.smm = SprintModelManager(self.env)
        
        self.sprint_backlog, self.story1, self.task1, self.task2 = \
            self._build_sprint_backlog_with_tasks(self.sprint)
    
    def _build_sprint_backlog_with_tasks(self, sprint):
        story_props = {Key.OWNER: 'tester',
                       Key.SPRINT: sprint.name,
                       Key.STORY_POINTS: "8"}
        story = self.teh.create_ticket(Type.USER_STORY, story_props)
        task1 = self.teh.create_ticket(Type.TASK, {Key.OWNER: 'tester',
                                                   Key.SPRINT: sprint.name,
                                                   Key.REMAINING_TIME: "8"})
        story.link_to(task1)
        
        task2 = self.teh.create_ticket(Type.TASK, {Key.OWNER: 'tester',
                                                   Key.SPRINT: sprint.name,
                                                   Key.REMAINING_TIME: "4"})
        story.link_to(task2)
        
        sprint_backlog = self.bmm.get(name="Sprint Backlog", scope=sprint.name)
        self.assert_equals(len(sprint_backlog), 3)
        return (sprint_backlog, story, task1, task2)
    
    def get_total_remaining_time(self, sprint_name, day, commitment=None):
        cmd_class = SprintController.GetTotalRemainingTimeCommand
        cmd = cmd_class(self.env, sprint=sprint_name, day=day, commitment=commitment)
        return self.controller.process_command(cmd)
    
    def test_can_calculate_remaining_time_for_a_specific_day(self):
        # Set remaining time for tasks at the end of the sprint
        sprint = self.sprint
        end = sprint.end
        rt1 = RemainingTime(self.env, self.task1)
        rt2 = RemainingTime(self.env, self.task2)
        rt1.set_remaining_time(2, day=end)
        rt2.set_remaining_time(1, day=end)
        self.assert_equals(2, RemainingTime(self.env, self.task1).get_remaining_time(end))
        self.assert_equals(1, RemainingTime(self.env, self.task2).get_remaining_time(end))
        self.assert_equals(3, self.get_total_remaining_time(sprint.name, end))
    
    def _create_remaining_time_series(self, ticket, start, time_series):
        rt = RemainingTime(self.env, ticket)
        for i, remaining_time in enumerate(time_series):
            day = start + (i * timedelta(days=1))
            rt.set_remaining_time(remaining_time, day=day)
    
    def test_can_calculate_total_remaining_time_for_start_of_sprint(self):
        start = self.sprint.start 
        self._create_remaining_time_series(self.task1, start,[12, 7.5, 3, 2.5, 0])
        self._create_remaining_time_series(self.task2, start, [8, 9, 4.5, 0, 3])
        
        total_remaining_time = self.get_total_remaining_time(self.sprint.name, start)
        self.assert_equals(12+8, total_remaining_time)
    
    def test_can_calculate_remaining_time_series_for_sprint(self):
        start = datetime(2009, 5, 11, tzinfo=utc)
        self.sprint.start = start
        self.sprint.end = datetime(2009, 5, 15, 18, 00, tzinfo=utc)
        self.smm.save(self.sprint)

        # check there is not time set right now
        series = self.get_remaining_times(self.sprint.name)
        self.assert_equals(0, sum(series))

        self._create_remaining_time_series(self.task1, start,[12, 7.5, 3, 2.5, 0])
        self._create_remaining_time_series(self.task2, start, [8, 9, 4.5, 0, 3])
        
        series = self.get_remaining_times(self.sprint.name)
        self.assert_equals([12+8, 7.5+9, 3+4.5, 2.5+0, 0+3], series)
    
    def  get_remaining_times(self, sprint_name=None, cut_to_today=False, commitment=None):
        if sprint_name is None:
            sprint_name = self.sprint.name
        cmd_class = SprintController.GetRemainingTimesCommand
        cmd = cmd_class(self.env, sprint=sprint_name, cut_to_today=cut_to_today,
                        commitment=commitment)
        return self.controller.process_command(cmd)
    
    def test_compute_remaining_time_for_sprint_even_if_story_has_no_remaining_time(self):
        self.task1[Key.REMAINING_TIME] = 0
        self.task2[Key.REMAINING_TIME] = 0
        self.task1.save_changes('foo', 'bar')
        self.task2.save_changes('foo', 'bar')
        # This raised an exception before because TOTAL_REMAINING_TIME was None
        self.get_remaining_times()
    
    def test_use_estimated_remaining_time(self):
        story_props = {Key.OWNER: 'tester',
                       Key.SPRINT: self.sprint.name, 
                       Key.STORY_POINTS: "5"}
        story2 = self.teh.create_ticket(Type.USER_STORY, story_props)
        self.assert_length(4, self.sprint_backlog)
        
        self.assert_equals(8, int(self.story1[Key.STORY_POINTS]))
        self.assert_equals(8 * 1.5, self.story1[Key.ESTIMATED_REMAINING_TIME])
        self.assert_equals(5 * 1.5, story2[Key.ESTIMATED_REMAINING_TIME])
        remaining_time = self.get_total_remaining_time(self.sprint.name, now(tz=utc))
        self.assert_equals((8+4) + 5 * 1.5, remaining_time)
    
    def _close_ticket_as_fixed(self, task):
        task[Key.STATUS] = Status.CLOSED
        task[Key.RESOLUTION] = Status.RES_FIXED
        task.save_changes(None, None)
    
    def test_remaining_time_correct_even_for_closed_stories(self):
        self.sprint.start = datetime.today() - timedelta(days=5)
        self.sprint.save()
        
        # Store some remaining time for yesterday
        yesterday_midnight = datetime.combine(yesterday(tz=utc), time(tzinfo=utc))
        RemainingTime(self.env, self.task1).set_remaining_time(3, yesterday_midnight)
        RemainingTime(self.env, self.task2).set_remaining_time(1, yesterday_midnight)
        
        self._close_ticket_as_fixed(self.task1)
        self._close_ticket_as_fixed(self.task2)
        self._close_ticket_as_fixed(self.story1)
        
        remaining_times = self.get_remaining_times(self.sprint.name, cut_to_today=True)
        # We have to use relative positioning from the end because we don't know
        # if the sprint will be extended due to a holiday.
        self.assert_equals([4, 0], remaining_times[-2:])
        
        # Check that the same holds true for retrieving a single day
        remaining_time = self.get_total_remaining_time(self.sprint.name, yesterday_midnight)
        self.assert_equals(4, remaining_time)
    
    def yesterday_midnight(self):
        return midnight(yesterday(tz=utc), tz=utc)
    
    def _create_historic_remaining_times(self):
        def set_remaining_time(task, day, remaining_time):
            remaining = RemainingTime(self.env, task)
            remaining.set_remaining_time(remaining_time, day=day)
        
        # 6 days before today so we are sure that the sprint started at least 
        # three days ago even if it was moved.
        self.sprint.start = now(tz=utc) - timedelta(days=6)
        self.smm.save(self.sprint)
        
        # We already burned some data on the first day of the sprint
        set_remaining_time(self.task1, self.sprint.start, 6)
        set_remaining_time(self.task2, self.sprint.start, 3)
        
        # Yesterday we burned some time already
        set_remaining_time(self.task1, self.yesterday_midnight(), 5)
    
    def test_remaining_time_of_first_sprint_day_equals_commitment(self):
        self._create_historic_remaining_times()
        # But now the remaining time went up again (fields are unchanged!)
        self.assert_equals(8+4, self.story1[Key.TOTAL_REMAINING_TIME])
        
        remaining_times = self.get_remaining_times(self.sprint.name, cut_to_today=True)
        # if no commitment is passed to the function, just return the remaining 
        # time
        self.assert_equals(6+3, remaining_times[0])
        
        remaining_times = self.get_remaining_times(self.sprint.name, 
                                                   cut_to_today=True, commitment=42)
        self.assert_equals(42, remaining_times[0])
        self.assert_equals([5+3, 8+4], remaining_times[-2:])
    
    def test_total_remaining_time_of_first_sprint_day_equals_commitment(self):
        self._create_historic_remaining_times()
        sprint = self.sprint
        self.assert_equals(5+3, self.get_total_remaining_time(sprint.name, self.yesterday_midnight()))
        self.assert_equals(42, self.get_total_remaining_time(sprint.name, sprint.start, commitment=42))
Exemplo n.º 2
0
class SprintControllerTestForRemainingTimes(AgiloTestCase):
    # Most of the test cases were previously in sprint_test
    # (agilo.scrum.sprint.tests) However, when we moved the
    # functionality to a command, these test cases needed to be ported
    # so that they test the command instead of the direct model
    # implementation. Several things are still artifacts from that old
    # test setup.

    def setUp(self):
        self.super()
        self.controller = SprintController(self.env)

        self.team = self.teh.create_team('Test team')
        # Preventing a RuleValidationException (Owner not Team Member)
        self.teh.create_member(name='tester', team=self.team)
        self.sprint = self.teh.create_sprint("Test Sprint", team=self.team)

        self.metrics = TeamMetrics(self.env, self.sprint, self.team)
        self.metrics[Key.RT_USP_RATIO] = 1.5
        self.metrics.save()

        self.bmm = BacklogModelManager(self.env)
        self.smm = SprintModelManager(self.env)

        self.sprint_backlog, self.story1, self.task1, self.task2 = \
            self._build_sprint_backlog_with_tasks(self.sprint)

    def _build_sprint_backlog_with_tasks(self, sprint):
        story_props = {
            Key.OWNER: 'tester',
            Key.SPRINT: sprint.name,
            Key.STORY_POINTS: "8"
        }
        story = self.teh.create_ticket(Type.USER_STORY, story_props)
        task1 = self.teh.create_ticket(Type.TASK, {
            Key.OWNER: 'tester',
            Key.SPRINT: sprint.name,
            Key.REMAINING_TIME: "8"
        })
        story.link_to(task1)

        task2 = self.teh.create_ticket(Type.TASK, {
            Key.OWNER: 'tester',
            Key.SPRINT: sprint.name,
            Key.REMAINING_TIME: "4"
        })
        story.link_to(task2)

        sprint_backlog = self.bmm.get(name="Sprint Backlog", scope=sprint.name)
        self.assert_equals(len(sprint_backlog), 3)
        return (sprint_backlog, story, task1, task2)

    def get_total_remaining_time(self, sprint_name, day, commitment=None):
        cmd_class = SprintController.GetTotalRemainingTimeCommand
        cmd = cmd_class(self.env,
                        sprint=sprint_name,
                        day=day,
                        commitment=commitment)
        return self.controller.process_command(cmd)

    def test_can_calculate_remaining_time_for_a_specific_day(self):
        # Set remaining time for tasks at the end of the sprint
        sprint = self.sprint
        end = sprint.end
        rt1 = RemainingTime(self.env, self.task1)
        rt2 = RemainingTime(self.env, self.task2)
        rt1.set_remaining_time(2, day=end)
        rt2.set_remaining_time(1, day=end)
        self.assert_equals(
            2,
            RemainingTime(self.env, self.task1).get_remaining_time(end))
        self.assert_equals(
            1,
            RemainingTime(self.env, self.task2).get_remaining_time(end))
        self.assert_equals(3, self.get_total_remaining_time(sprint.name, end))

    def _create_remaining_time_series(self, ticket, start, time_series):
        rt = RemainingTime(self.env, ticket)
        for i, remaining_time in enumerate(time_series):
            day = start + (i * timedelta(days=1))
            rt.set_remaining_time(remaining_time, day=day)

    def test_can_calculate_total_remaining_time_for_start_of_sprint(self):
        start = self.sprint.start
        self._create_remaining_time_series(self.task1, start,
                                           [12, 7.5, 3, 2.5, 0])
        self._create_remaining_time_series(self.task2, start,
                                           [8, 9, 4.5, 0, 3])

        total_remaining_time = self.get_total_remaining_time(
            self.sprint.name, start)
        self.assert_equals(12 + 8, total_remaining_time)

    def test_can_calculate_remaining_time_series_for_sprint(self):
        start = datetime(2009, 5, 11, tzinfo=utc)
        self.sprint.start = start
        self.sprint.end = datetime(2009, 5, 15, 18, 00, tzinfo=utc)
        self.smm.save(self.sprint)

        # check there is not time set right now
        series = self.get_remaining_times(self.sprint.name)
        self.assert_equals(0, sum(series))

        self._create_remaining_time_series(self.task1, start,
                                           [12, 7.5, 3, 2.5, 0])
        self._create_remaining_time_series(self.task2, start,
                                           [8, 9, 4.5, 0, 3])

        series = self.get_remaining_times(self.sprint.name)
        self.assert_equals([12 + 8, 7.5 + 9, 3 + 4.5, 2.5 + 0, 0 + 3], series)

    def get_remaining_times(self,
                            sprint_name=None,
                            cut_to_today=False,
                            commitment=None):
        if sprint_name is None:
            sprint_name = self.sprint.name
        cmd_class = SprintController.GetRemainingTimesCommand
        cmd = cmd_class(self.env,
                        sprint=sprint_name,
                        cut_to_today=cut_to_today,
                        commitment=commitment)
        return self.controller.process_command(cmd)

    def test_compute_remaining_time_for_sprint_even_if_story_has_no_remaining_time(
            self):
        self.task1[Key.REMAINING_TIME] = 0
        self.task2[Key.REMAINING_TIME] = 0
        self.task1.save_changes('foo', 'bar')
        self.task2.save_changes('foo', 'bar')
        # This raised an exception before because TOTAL_REMAINING_TIME was None
        self.get_remaining_times()

    def test_use_estimated_remaining_time(self):
        story_props = {
            Key.OWNER: 'tester',
            Key.SPRINT: self.sprint.name,
            Key.STORY_POINTS: "5"
        }
        story2 = self.teh.create_ticket(Type.USER_STORY, story_props)
        self.assert_length(4, self.sprint_backlog)

        self.assert_equals(8, int(self.story1[Key.STORY_POINTS]))
        self.assert_equals(8 * 1.5, self.story1[Key.ESTIMATED_REMAINING_TIME])
        self.assert_equals(5 * 1.5, story2[Key.ESTIMATED_REMAINING_TIME])
        remaining_time = self.get_total_remaining_time(self.sprint.name,
                                                       now(tz=utc))
        self.assert_equals((8 + 4) + 5 * 1.5, remaining_time)

    def _close_ticket_as_fixed(self, task):
        task[Key.STATUS] = Status.CLOSED
        task[Key.RESOLUTION] = Status.RES_FIXED
        task.save_changes(None, None)

    def test_remaining_time_correct_even_for_closed_stories(self):
        self.sprint.start = datetime.today() - timedelta(days=5)
        self.sprint.save()

        # Store some remaining time for yesterday
        yesterday_midnight = datetime.combine(yesterday(tz=utc),
                                              time(tzinfo=utc))
        RemainingTime(self.env,
                      self.task1).set_remaining_time(3, yesterday_midnight)
        RemainingTime(self.env,
                      self.task2).set_remaining_time(1, yesterday_midnight)

        self._close_ticket_as_fixed(self.task1)
        self._close_ticket_as_fixed(self.task2)
        self._close_ticket_as_fixed(self.story1)

        remaining_times = self.get_remaining_times(self.sprint.name,
                                                   cut_to_today=True)
        # We have to use relative positioning from the end because we don't know
        # if the sprint will be extended due to a holiday.
        self.assert_equals([4, 0], remaining_times[-2:])

        # Check that the same holds true for retrieving a single day
        remaining_time = self.get_total_remaining_time(self.sprint.name,
                                                       yesterday_midnight)
        self.assert_equals(4, remaining_time)

    def yesterday_midnight(self):
        return midnight(yesterday(tz=utc), tz=utc)

    def _create_historic_remaining_times(self):
        def set_remaining_time(task, day, remaining_time):
            remaining = RemainingTime(self.env, task)
            remaining.set_remaining_time(remaining_time, day=day)

        # 6 days before today so we are sure that the sprint started at least
        # three days ago even if it was moved.
        self.sprint.start = now(tz=utc) - timedelta(days=6)
        self.smm.save(self.sprint)

        # We already burned some data on the first day of the sprint
        set_remaining_time(self.task1, self.sprint.start, 6)
        set_remaining_time(self.task2, self.sprint.start, 3)

        # Yesterday we burned some time already
        set_remaining_time(self.task1, self.yesterday_midnight(), 5)

    def test_remaining_time_of_first_sprint_day_equals_commitment(self):
        self._create_historic_remaining_times()
        # But now the remaining time went up again (fields are unchanged!)
        self.assert_equals(8 + 4, self.story1[Key.TOTAL_REMAINING_TIME])

        remaining_times = self.get_remaining_times(self.sprint.name,
                                                   cut_to_today=True)
        # if no commitment is passed to the function, just return the remaining
        # time
        self.assert_equals(6 + 3, remaining_times[0])

        remaining_times = self.get_remaining_times(self.sprint.name,
                                                   cut_to_today=True,
                                                   commitment=42)
        self.assert_equals(42, remaining_times[0])
        self.assert_equals([5 + 3, 8 + 4], remaining_times[-2:])

    def test_total_remaining_time_of_first_sprint_day_equals_commitment(self):
        self._create_historic_remaining_times()
        sprint = self.sprint
        self.assert_equals(
            5 + 3,
            self.get_total_remaining_time(sprint.name,
                                          self.yesterday_midnight()))
        self.assert_equals(
            42,
            self.get_total_remaining_time(sprint.name,
                                          sprint.start,
                                          commitment=42))
Exemplo n.º 3
0
 def set_rtusp_ratio(sprint):
     metrics = TeamMetrics(self.env, sprint, sprint.team)
     metrics[Key.RT_USP_RATIO] = 2 # 16h for 8 usp
     metrics.save()
Exemplo n.º 4
0
 def set_rtusp_ratio(sprint):
     metrics = TeamMetrics(self.env, sprint, sprint.team)
     metrics[Key.RT_USP_RATIO] = 2  # 16h for 8 usp
     metrics.save()