class HistoryTestCase(_HistoryTestCase):
    def test_simple(self):
        a = "file:///home/trentm/a.txt"
        b = "file:///home/trentm/b.txt"
        loc1 = self.history.note_loc(Location(a, 10, 10))
        loc2 = self.history.note_loc(Location(a, 20, 20))
        loc3 = self.history.note_loc(Location(b, 30, 30))
        loc4 = Location(b, 40, 40) # the current location
        
        # No "forward" visits, i.e. the top item of the stack is the
        # current pos.
        #self.history.debug_dump_recent_history(loc4)
        h = list(self.history.recent_history(loc4))
        self.assertEqual(h[0][0], True)  

        # Go "back".
        loc_back = self.history.go_back(loc4)
        self.assertEqual(loc_back, loc3)

        #self.history.debug_dump_recent_history(curr_loc)
        h = list(self.history.recent_history(loc_back))
        self.assertEqual(h[0][1], loc4) # one "forward" visit
        self.assertEqual(h[1][1], loc3) # the curr location
        self.assertEqual(h[2][1], loc2) # "back" one visit
        self.assertEqual(h[3][1], loc1) # "back" two visits

        # Go "forward".
        loc_fwd = self.history.go_forward(curr_loc=loc_back)
        self.assertEqual(loc_fwd, loc4)

        # No "forward" visits remain.
        #self.history.debug_dump_recent_history(loc_fwd)
        h = list(self.history.recent_history(loc_fwd))
        self.assertEqual(h[0][0], True) 

    @testlib.tag("bug81978")
    def test_remember_curr_place(self):
        # Test whether the curr place in history will be remembered between runs.
        
        # Make some interesting history.
        a = "file:///home/trentm/a.txt"
        b = "file:///home/trentm/current.txt"
        locs = [self.history.note_loc(Location(a, 10 * (i + 1), 0))
                for i in range(10)]
        locs.insert(0, None) # Make locs 1-based
        current_loc = Location(b, 55, 0)
        loc_back = self.history.go_back(current_loc, 4)
        self.assertEqual(loc_back, locs[7])
        
        # Test history before the restart:
        h = list(self.history.recent_history(loc_back))
        for i in range(1, 10):
            self.assertEqual(h[i][1], locs[11 - i])

        # Simulate a restart.
        self.history.close()
        self.history = History(self._db_path_)
        
        h = list(self.history.recent_history(loc_back))
        # Test boundary conditions, then check all in a loop.
        self.assertEqual(h[0][1], current_loc) # 4 fwd
        self.assertEqual(h[1][1], locs[10]) # 3 fwd
        self.assertEqual(h[3][1], locs[8]) # 1 fwd
        self.assertEqual(h[4][1], locs[7]) # here
        self.assertEqual(h[5][1], locs[6]) # 1 back
        h = list(self.history.recent_history(loc_back))
        for i in range(1, 10):
            self.assertEqual(h[i][1], locs[11 - i])

    def test_back_then_fwd(self):
        # See the example in the "When to note a location" section in KD 218
        # with this code sample for the scenario being tested here:
        #   1: def foo():
        #   2:   # a comment
        #   3:   print "hi"
        #   4:
        #   5: foo()
        uri = "file:///home/bob/src/foo.txt"
        
        loc1 = self.history.note_loc(Location("file:///etc/passwd", 10, 4))
        loc2 = self.history.note_loc(Location(uri, 5, 1))  # 2. does goto definition: 1,1

        # 3. arrow down a couple of lines: 3,1
        loc3 = Location(uri, 3, 1)
        #self.history.debug_dump_recent_history(loc3)

        # 4. go "Back" one in the history
        loc4 = self.history.go_back(curr_loc=loc3)
        self.assertEqual(loc4, loc2)
        #self.history.debug_dump_recent_history(loc4)

        # 5. go "Forward" should return us to loc3
        loc5 = self.history.go_forward(loc4)
        #self.history.debug_dump_recent_history(loc5)
        self.assertEqual(loc5, loc3)

        # ... Back/Forward should cycle between those two positions.
        curr_loc = loc5
        for i in range(10):
            curr_loc = self.history.go_back(curr_loc)
            self.assertEqual(curr_loc, loc4)
            curr_loc = self.history.go_forward(curr_loc)
            self.assertEqual(curr_loc, loc5)

    def test_update_referer(self):
        # Test the code path that results in `Database.update_referer_id`
        # being called.
        uri = "file:///home/bob/.bashrc"
        locs = [Location(uri, i*2, 1) for i in range(1, 11)]
        for loc in locs:
            self.history.note_loc(loc)
        curr_loc = Location(uri, 42, 1)
        
        for i in range(3):
            curr_loc = Location(curr_loc.uri, curr_loc.line-1, curr_loc.col)
            curr_loc = self.history.go_back(curr_loc)
            self.assertEqual(curr_loc, locs[-(i+1)])
        #self.history.debug_dump_recent_history(curr_loc)
        last_loc = None
        for is_curr, loc in self.history.recent_history(curr_loc):
            if last_loc:
                self.assertEqual(last_loc.referer_id, loc.id)
            last_loc = loc

    @testlib.tag("depleted")
    def test_depleted_recent_back_visits(self):
        # Test the code path that results in the
        # `HistorySession.recent_back_visits` cache being depleted so that it
        # needs to be replenished from the db.
        uri = "file:///home/bob/.bashrc"
        locs = [Location(uri, i*2, 1) for i in
            range(1, HistorySession.RECENT_BACK_VISITS_CACHE_LENGTH + 10)]
        for loc in locs:
            self.history.note_loc(loc)
        curr_loc = Location(uri, 666, 1)
        #self.history.debug_dump_recent_history(curr_loc)
        
        session = self.history.get_session()
        n_to_deplete = len(session.recent_back_visits) \
            - HistorySession.RECENT_BACK_VISITS_DEPLETION_LENGTH + 5
        for i in range(n_to_deplete):
            curr_loc = self.history.go_back(curr_loc)
            self.assertEqual(curr_loc, locs[-(i+1)])
        #self.history.debug_dump_recent_history(curr_loc)
        
        # Test going back forward.
        for j in range(min(n_to_deplete, 20)):
            curr_loc = self.history.go_forward(curr_loc)
            self.assertEqual(curr_loc, locs[-(i-j)])

    @testlib.tag("bug81979")
    def test_forward_visits_integrity_on_multi_step_moves(self):
        uri =         "file:///home/tester/a.txt"
        uri_current = "file:///home/tester/current.txt"
        num_items_to_create = 20
        locs = [self.history.note_loc(Location(uri, i + 1, 0))
                for i in range(num_items_to_create)]
        locs.insert(0, None) # Simplify code, since we're 1-based
            
        first_current_loc = current_loc = Location(uri_current, num_items_to_create + 1, 0)
        locs.append(first_current_loc)
        jump_count = 10
        loc = self.history.go_back(current_loc, jump_count)
        current_loc = loc
        # "Flat" test on individual items at boundaries.
        # If one of these tests fails, it's easy to determine which.
        session = self.history.get_session()
        self.assertEqual(current_loc,
                         locs[num_items_to_create - jump_count + 1])
        self.assertEqual(session.forward_visits[-1],
                         locs[num_items_to_create - jump_count + 2])
        self.assertEqual(session.forward_visits[1],
                         locs[num_items_to_create])
        self.assertEqual(session.forward_visits[0],
                         first_current_loc)
        # Loop through to verify we found everything.
        # If one of these fails, we'll need to uncomment the
        # debug line to figure out what went wrong.
        #self.history.debug_dump_recent_history(curr_loc)
        for i in range(jump_count):
            self.assertEqual(session.forward_visits[i],
                             locs[num_items_to_create - i + 1])
        
        # Verify we don't lose the current spot when we move forward.
        jump_count = 4
        old_cid = current_loc.id # 11
        loc = self.history.go_forward(current_loc, jump_count)
        current_loc = loc
        self.assertEqual(current_loc,
                         locs[old_cid + jump_count])
        # Don't roll this loop -- if one of the tests fail, it's
        # harder to determine which one failed
        session = self.history.get_session()
        self.assertEqual(session.recent_back_visits[0],
                         locs[old_cid + jump_count - 1])
        self.assertEqual(session.recent_back_visits[1],
                         locs[old_cid + jump_count - 2])
        self.assertEqual(session.recent_back_visits[2],
                         locs[old_cid + jump_count - 3])
        self.assertEqual(session.recent_back_visits[3],
                         locs[old_cid + jump_count - 4])
     
    @testlib.tag("bug81987")
    def test_curr_loc_is_jump_back_loc(self):
        # Test the behaviour when we go_back to a loc that happens to be the
        # same as the current location.
        #
        # The bug is/was that a new location was being added to the history
        # db, rather than just re-using the one to which we are jumping.
        #
        # Note that going forward seems to be fine.
        
        # Setup starter recent history.
        a = "file:///home/tester/a.txt"
        locs = [self.history.note_loc(Location(a, (i+1)*10, 1))
                for i in range(10)]
        curr_loc = Location(a, locs[-1].line, locs[-1].col)     # haven't moved
        #self.history.debug_dump_recent_history(curr_loc, merge_curr_loc=False)
    
        # Test going back one: verify we don't go anywhere.
        new_loc = self.history.go_back(curr_loc, 1)
        #self.history.debug_dump_recent_history(new_loc, merge_curr_loc=False)
        session = self.history.get_session()
        self.assertEqual(len(session.forward_visits), 1)
        self.assertEqual(session.forward_visits[0], new_loc)

    def test_recent_uris(self):
        uris = ["file://home/joe/%s.py" % n for n in range(10)]
        locs = [self.history.note_loc(Location(random.choice(uris), i+1, 1))
                for i in range(100)]
        #self.history.debug_dump_recent_history()
        #self.history.debug_dump_recent_uris()
        recent_uris = list(self.history.recent_uris(10, show_all=True))
        self.assertEqual(recent_uris[0], locs[-1].uri)
        self.assertEqual(set(uris), set(recent_uris))

    def test_recent_uris_multi_page(self):
        # Test the "PAGE_SIZE" handling in HistorySession.recent_uris.
        uris = ["file://home/joe/%s.py" % n for n in range(150)]
        locs = [self.history.note_loc(Location(uris[i], i+1, 1))
                for i in range(150)]
        #self.history.debug_dump_recent_history()
        #self.history.debug_dump_recent_uris()
        recent_uris = list(self.history.recent_uris(120, show_all=True), )
        self.assertEqual(len(recent_uris), 120)

    @testlib.tag("bug82342")
    def test_no_view_recent_locs(self):
        a = "file:///home/trentm/a.txt"
        b = "file:///home/trentm/b.txt"
        loc1 = self.history.note_loc(Location(a, 10, 10))
        loc2 = self.history.note_loc(Location(b, 20, 20))
        loc3 = self.history.note_loc(Location(b, 30, 30))

        curr_loc = None
        #self.history.debug_dump_recent_history(curr_loc)
        h = list(self.history.recent_history(curr_loc))
        self.assertEqual(len(h), 4)
        self.assertEqual(h[0][0], True)
        self.assertEqual(h[0][1], None)

        # Go back
        loc_back = self.history.go_back(curr_loc)
        self.assertEqual(loc_back, loc3)

        h = list(self.history.recent_history(loc_back))
        self.assertEqual(h[0][1], loc3) # the curr location
        self.assertEqual(h[1][1], loc2) # "back" one visit
        self.assertEqual(h[2][1], loc1) # "back" two visits

        curr_loc = loc_back
        loc_back = self.history.go_back(curr_loc)
        self.assertEqual(loc_back, loc2)

        # Go forward
        loc_fwd = self.history.go_forward(curr_loc=None)
        self.assertEqual(loc_fwd, loc3)
class HistoryTestCase(_HistoryTestCase):
    def test_simple(self):
        a = "file:///home/trentm/a.txt"
        b = "file:///home/trentm/b.txt"
        loc1 = self.history.note_loc(Location(a, 10, 10))
        loc2 = self.history.note_loc(Location(a, 20, 20))
        loc3 = self.history.note_loc(Location(b, 30, 30))
        loc4 = Location(b, 40, 40)  # the current location

        # No "forward" visits, i.e. the top item of the stack is the
        # current pos.
        #self.history.debug_dump_recent_history(loc4)
        h = list(self.history.recent_history(loc4))
        self.assertEqual(h[0][0], True)

        # Go "back".
        loc_back = self.history.go_back(loc4)
        self.assertEqual(loc_back, loc3)

        #self.history.debug_dump_recent_history(curr_loc)
        h = list(self.history.recent_history(loc_back))
        self.assertEqual(h[0][1], loc4)  # one "forward" visit
        self.assertEqual(h[1][1], loc3)  # the curr location
        self.assertEqual(h[2][1], loc2)  # "back" one visit
        self.assertEqual(h[3][1], loc1)  # "back" two visits

        # Go "forward".
        loc_fwd = self.history.go_forward(curr_loc=loc_back)
        self.assertEqual(loc_fwd, loc4)

        # No "forward" visits remain.
        #self.history.debug_dump_recent_history(loc_fwd)
        h = list(self.history.recent_history(loc_fwd))
        self.assertEqual(h[0][0], True)

    @testlib.tag("bug81978")
    def test_remember_curr_place(self):
        # Test whether the curr place in history will be remembered between runs.

        # Make some interesting history.
        a = "file:///home/trentm/a.txt"
        b = "file:///home/trentm/current.txt"
        locs = [
            self.history.note_loc(Location(a, 10 * (i + 1), 0))
            for i in range(10)
        ]
        locs.insert(0, None)  # Make locs 1-based
        current_loc = Location(b, 55, 0)
        loc_back = self.history.go_back(current_loc, 4)
        self.assertEqual(loc_back, locs[7])

        # Test history before the restart:
        h = list(self.history.recent_history(loc_back))
        for i in range(1, 10):
            self.assertEqual(h[i][1], locs[11 - i])

        # Simulate a restart.
        self.history.close()
        self.history = History(self._db_path_)

        h = list(self.history.recent_history(loc_back))
        # Test boundary conditions, then check all in a loop.
        self.assertEqual(h[0][1], current_loc)  # 4 fwd
        self.assertEqual(h[1][1], locs[10])  # 3 fwd
        self.assertEqual(h[3][1], locs[8])  # 1 fwd
        self.assertEqual(h[4][1], locs[7])  # here
        self.assertEqual(h[5][1], locs[6])  # 1 back
        h = list(self.history.recent_history(loc_back))
        for i in range(1, 10):
            self.assertEqual(h[i][1], locs[11 - i])

    def test_back_then_fwd(self):
        # See the example in the "When to note a location" section in KD 218
        # with this code sample for the scenario being tested here:
        #   1: def foo():
        #   2:   # a comment
        #   3:   print "hi"
        #   4:
        #   5: foo()
        uri = "file:///home/bob/src/foo.txt"

        loc1 = self.history.note_loc(Location("file:///etc/passwd", 10, 4))
        loc2 = self.history.note_loc(Location(
            uri, 5, 1))  # 2. does goto definition: 1,1

        # 3. arrow down a couple of lines: 3,1
        loc3 = Location(uri, 3, 1)
        #self.history.debug_dump_recent_history(loc3)

        # 4. go "Back" one in the history
        loc4 = self.history.go_back(curr_loc=loc3)
        self.assertEqual(loc4, loc2)
        #self.history.debug_dump_recent_history(loc4)

        # 5. go "Forward" should return us to loc3
        loc5 = self.history.go_forward(loc4)
        #self.history.debug_dump_recent_history(loc5)
        self.assertEqual(loc5, loc3)

        # ... Back/Forward should cycle between those two positions.
        curr_loc = loc5
        for i in range(10):
            curr_loc = self.history.go_back(curr_loc)
            self.assertEqual(curr_loc, loc4)
            curr_loc = self.history.go_forward(curr_loc)
            self.assertEqual(curr_loc, loc5)

    def test_update_referer(self):
        # Test the code path that results in `Database.update_referer_id`
        # being called.
        uri = "file:///home/bob/.bashrc"
        locs = [Location(uri, i * 2, 1) for i in range(1, 11)]
        for loc in locs:
            self.history.note_loc(loc)
        curr_loc = Location(uri, 42, 1)

        for i in range(3):
            curr_loc = Location(curr_loc.uri, curr_loc.line - 1, curr_loc.col)
            curr_loc = self.history.go_back(curr_loc)
            self.assertEqual(curr_loc, locs[-(i + 1)])
        #self.history.debug_dump_recent_history(curr_loc)
        last_loc = None
        for is_curr, loc in self.history.recent_history(curr_loc):
            if last_loc:
                self.assertEqual(last_loc.referer_id, loc.id)
            last_loc = loc

    @testlib.tag("depleted")
    def test_depleted_recent_back_visits(self):
        # Test the code path that results in the
        # `HistorySession.recent_back_visits` cache being depleted so that it
        # needs to be replenished from the db.
        uri = "file:///home/bob/.bashrc"
        locs = [
            Location(uri, i * 2, 1)
            for i in range(1, HistorySession.RECENT_BACK_VISITS_CACHE_LENGTH +
                           10)
        ]
        for loc in locs:
            self.history.note_loc(loc)
        curr_loc = Location(uri, 666, 1)
        #self.history.debug_dump_recent_history(curr_loc)

        session = self.history.get_session()
        n_to_deplete = len(session.recent_back_visits) \
            - HistorySession.RECENT_BACK_VISITS_DEPLETION_LENGTH + 5
        for i in range(n_to_deplete):
            curr_loc = self.history.go_back(curr_loc)
            self.assertEqual(curr_loc, locs[-(i + 1)])
        #self.history.debug_dump_recent_history(curr_loc)

        # Test going back forward.
        for j in range(min(n_to_deplete, 20)):
            curr_loc = self.history.go_forward(curr_loc)
            self.assertEqual(curr_loc, locs[-(i - j)])

    @testlib.tag("bug81979")
    def test_forward_visits_integrity_on_multi_step_moves(self):
        uri = "file:///home/tester/a.txt"
        uri_current = "file:///home/tester/current.txt"
        num_items_to_create = 20
        locs = [
            self.history.note_loc(Location(uri, i + 1, 0))
            for i in range(num_items_to_create)
        ]
        locs.insert(0, None)  # Simplify code, since we're 1-based

        first_current_loc = current_loc = Location(uri_current,
                                                   num_items_to_create + 1, 0)
        locs.append(first_current_loc)
        jump_count = 10
        loc = self.history.go_back(current_loc, jump_count)
        current_loc = loc
        # "Flat" test on individual items at boundaries.
        # If one of these tests fails, it's easy to determine which.
        session = self.history.get_session()
        self.assertEqual(current_loc,
                         locs[num_items_to_create - jump_count + 1])
        self.assertEqual(session.forward_visits[-1],
                         locs[num_items_to_create - jump_count + 2])
        self.assertEqual(session.forward_visits[1], locs[num_items_to_create])
        self.assertEqual(session.forward_visits[0], first_current_loc)
        # Loop through to verify we found everything.
        # If one of these fails, we'll need to uncomment the
        # debug line to figure out what went wrong.
        #self.history.debug_dump_recent_history(curr_loc)
        for i in range(jump_count):
            self.assertEqual(session.forward_visits[i],
                             locs[num_items_to_create - i + 1])

        # Verify we don't lose the current spot when we move forward.
        jump_count = 4
        old_cid = current_loc.id  # 11
        loc = self.history.go_forward(current_loc, jump_count)
        current_loc = loc
        self.assertEqual(current_loc, locs[old_cid + jump_count])
        # Don't roll this loop -- if one of the tests fail, it's
        # harder to determine which one failed
        session = self.history.get_session()
        self.assertEqual(session.recent_back_visits[0],
                         locs[old_cid + jump_count - 1])
        self.assertEqual(session.recent_back_visits[1],
                         locs[old_cid + jump_count - 2])
        self.assertEqual(session.recent_back_visits[2],
                         locs[old_cid + jump_count - 3])
        self.assertEqual(session.recent_back_visits[3],
                         locs[old_cid + jump_count - 4])

    @testlib.tag("bug81987")
    def test_curr_loc_is_jump_back_loc(self):
        # Test the behaviour when we go_back to a loc that happens to be the
        # same as the current location.
        #
        # The bug is/was that a new location was being added to the history
        # db, rather than just re-using the one to which we are jumping.
        #
        # Note that going forward seems to be fine.

        # Setup starter recent history.
        a = "file:///home/tester/a.txt"
        locs = [
            self.history.note_loc(Location(a, (i + 1) * 10, 1))
            for i in range(10)
        ]
        curr_loc = Location(a, locs[-1].line, locs[-1].col)  # haven't moved
        #self.history.debug_dump_recent_history(curr_loc, merge_curr_loc=False)

        # Test going back one: verify we don't go anywhere.
        new_loc = self.history.go_back(curr_loc, 1)
        #self.history.debug_dump_recent_history(new_loc, merge_curr_loc=False)
        session = self.history.get_session()
        self.assertEqual(len(session.forward_visits), 1)
        self.assertEqual(session.forward_visits[0], new_loc)

    def test_recent_uris(self):
        uris = ["file://home/joe/%s.py" % n for n in range(10)]
        locs = [
            self.history.note_loc(Location(random.choice(uris), i + 1, 1))
            for i in range(100)
        ]
        #self.history.debug_dump_recent_history()
        #self.history.debug_dump_recent_uris()
        recent_uris = list(self.history.recent_uris(10, show_all=True))
        self.assertEqual(recent_uris[0], locs[-1].uri)
        self.assertEqual(set(uris), set(recent_uris))

    def test_recent_uris_multi_page(self):
        # Test the "PAGE_SIZE" handling in HistorySession.recent_uris.
        uris = ["file://home/joe/%s.py" % n for n in range(150)]
        locs = [
            self.history.note_loc(Location(uris[i], i + 1, 1))
            for i in range(150)
        ]
        #self.history.debug_dump_recent_history()
        #self.history.debug_dump_recent_uris()
        recent_uris = list(self.history.recent_uris(120, show_all=True), )
        self.assertEqual(len(recent_uris), 120)

    @testlib.tag("bug82342")
    def test_no_view_recent_locs(self):
        a = "file:///home/trentm/a.txt"
        b = "file:///home/trentm/b.txt"
        loc1 = self.history.note_loc(Location(a, 10, 10))
        loc2 = self.history.note_loc(Location(b, 20, 20))
        loc3 = self.history.note_loc(Location(b, 30, 30))

        curr_loc = None
        #self.history.debug_dump_recent_history(curr_loc)
        h = list(self.history.recent_history(curr_loc))
        self.assertEqual(len(h), 4)
        self.assertEqual(h[0][0], True)
        self.assertEqual(h[0][1], None)

        # Go back
        loc_back = self.history.go_back(curr_loc)
        self.assertEqual(loc_back, loc3)

        h = list(self.history.recent_history(loc_back))
        self.assertEqual(h[0][1], loc3)  # the curr location
        self.assertEqual(h[1][1], loc2)  # "back" one visit
        self.assertEqual(h[2][1], loc1)  # "back" two visits

        curr_loc = loc_back
        loc_back = self.history.go_back(curr_loc)
        self.assertEqual(loc_back, loc2)

        # Go forward
        loc_fwd = self.history.go_forward(curr_loc=None)
        self.assertEqual(loc_fwd, loc3)