diff --git a/python/fixture/server.zip b/python/fixture/server.zip index 23b7c48..2a8fb0f 100644 Binary files a/python/fixture/server.zip and b/python/fixture/server.zip differ diff --git a/python/perforce.py b/python/perforce.py index fd27b0c..496879a 100644 --- a/python/perforce.py +++ b/python/perforce.py @@ -77,6 +77,22 @@ def insert_clientname(mapping): return '%s //%s/%s' % (depot, clientname, local) return [insert_clientname(mapping) for mapping in view] + def _flush_to_previous_client(self, current_client, prev_clientname): + """Flush a new client to match existing workspace data from a previous client""" + prev_client = self.perforce.fetch_client(prev_clientname) + stream_switch = self.stream and prev_client._stream != self.stream + if stream_switch: + self.perforce.logger.info("previous client stream %s does not match %s, switching stream temporarily to flush" % (prev_client._stream, self.stream)) + current_client._stream = prev_client._stream + self.perforce.save_client(current_client) + + self.perforce.run_flush(['//...@%s' % prev_clientname]) + + if stream_switch: + self.perforce.logger.info("switching stream back to %s" % self.stream) + current_client._stream = self.stream + self.perforce.save_client(current_client) + def _setup_client(self): """Creates or re-uses the client workspace for this machine""" # pylint: disable=protected-access @@ -107,7 +123,8 @@ def _setup_client(self): if line.startswith('P4CLIENT=')) if prev_clientname != clientname: self.perforce.logger.warning("p4config last client was %s, flushing workspace to match" % prev_clientname) - self.perforce.run_flush(['//...@%s' % prev_clientname]) + self._flush_to_previous_client(client, prev_clientname) + elif 'Update' in client: # client was accessed previously self.perforce.logger.warning("p4config missing for previously accessed client workspace. flushing to revision zero") self.perforce.run_flush(['//...@0']) diff --git a/python/test_perforce.py b/python/test_perforce.py index 54c67b7..c64a6a4 100644 --- a/python/test_perforce.py +++ b/python/test_perforce.py @@ -106,6 +106,7 @@ def test_fixture(capsys, server): assert depotfile_to_content == { "//depot/file.txt": "Hello World\n", "//stream-depot/main/file.txt": "Hello Stream World\n", + "//stream-depot/main/file_2.txt": "file_2\n", "//stream-depot/dev/file.txt": "Hello Stream World (dev)\n", } @@ -143,6 +144,11 @@ def test_fixture(capsys, server): 'action': ['edit'], 'depotFile': ['//stream-depot/dev/file.txt'], 'desc': 'Update contents of //stream-depot/dev/file.txt\n' + }, + '9': { + 'action': ['add'], + 'depotFile': ['//stream-depot/main/file_2.txt'], + 'desc': 'file_2.txt - exists in main but not dev\n' } } @@ -188,11 +194,12 @@ def test_fixture(capsys, server): def test_head(server, tmpdir): """Test resolve of HEAD changelist""" - repo = P4Repo(root=tmpdir) - assert repo.head() == "@6", "Unexpected global HEAD revision" + # workspace with no changes in view defaults to global view + repo = P4Repo(root=tmpdir, view="//depot/empty_dir/... empty_dir/...") + assert repo.head() == "@9", "Unexpected global HEAD revision" - repo = P4Repo(root=tmpdir, stream='//stream-depot/main') - assert repo.head() == "@2", "Unexpected HEAD revision for stream" + repo = P4Repo(root=tmpdir, stream='//stream-depot/dev') + assert repo.head() == "@8", "Unexpected HEAD revision for stream" repo = P4Repo(root=tmpdir, stream='//stream-depot/idontexist') with pytest.raises(Exception, match=r"Stream '//stream-depot/idontexist' doesn't exist."): @@ -413,6 +420,8 @@ def test_stream_switching(server, tmpdir): repo = P4Repo(root=tmpdir, stream='//stream-depot/main') synced = repo.sync() assert len(synced) > 0, "Didn't sync any files" + assert set(os.listdir(tmpdir)) == set([ + "file.txt", "file_2.txt", "p4config"]) with open(os.path.join(tmpdir, "file.txt")) as content: assert content.read() == "Hello Stream World\n", "Unexpected content in workspace file" @@ -420,9 +429,33 @@ def test_stream_switching(server, tmpdir): repo = P4Repo(root=tmpdir, stream='//stream-depot/dev') repo.sync() assert len(synced) > 0, "Didn't sync any files" + assert set(os.listdir(tmpdir)) == set([ + "file.txt", "p4config"]) # file_2.txt was de-synced with open(os.path.join(tmpdir, "file.txt")) as content: assert content.read() == "Hello Stream World (dev)\n", "Unexpected content in workspace file" +def test_stream_switching_migration(server, tmpdir): + """Test stream-switching and client migration simultaneously""" + repo = P4Repo(root=tmpdir, stream='//stream-depot/main') + synced = repo.sync() + assert len(synced) > 0, "Didn't sync any files" + assert set(os.listdir(tmpdir)) == set([ + "file.txt", "file_2.txt", "p4config"]) + with open(os.path.join(tmpdir, "file.txt")) as content: + assert content.read() == "Hello Stream World\n", "Unexpected content in workspace file" + + with tempfile.TemporaryDirectory(prefix="bk-p4-test-") as second_client: + copytree(tmpdir, second_client) + # Client names include path on disk, so this creates a new unique client + # Re-use the same checkout directory and switch streams at the same time + repo = P4Repo(root=second_client, stream='//stream-depot/dev') + repo.sync() + assert len(synced) > 0, "Didn't sync any files" + assert set(os.listdir(second_client)) == set([ + "file.txt", "p4config"]) # file_2.txt was de-synced + with open(os.path.join(second_client, "file.txt")) as content: + assert content.read() == "Hello Stream World (dev)\n", "Unexpected content in workspace file" + # def test_live_server(): # """Reproduce production issues quickly by writing tests which run against a real server"""