@@ -26,6 +26,7 @@ def _create_zip(self, **kwargs):
2626 "workspace_name" : "my_ws" ,
2727 "legacy_external_runfiles" : False ,
2828 "runfiles_dir" : "runfiles" ,
29+ "pathsep" : "/" ,
2930 }
3031 defaults .update (kwargs )
3132 zipper .create_zip (** defaults )
@@ -90,6 +91,71 @@ def test_create_zip_with_files_and_symlinks(self):
9091 self .assertZipFileContent (zf , "runfiles/root_file" , content = "content1" )
9192 self .assertZipFileContent (zf , "runfiles/my_ws/empty_file" , content = "" )
9293
94+ def test_create_zip_with_direct_symlink (self ):
95+ # Test the 'symlink' manifest entry type
96+ manifest_content = [
97+ "symlink|path/to/link|target/path" ,
98+ ]
99+ self .manifest_path .write_text ("\n " .join (manifest_content ))
100+
101+ self ._create_zip ()
102+
103+ with zipfile .ZipFile (self .output_zip , "r" ) as zf :
104+ self .assertEqual (zf .namelist (), ["runfiles/path/to/link" ])
105+ self .assertZipFileContent (
106+ zf , "runfiles/path/to/link" , is_symlink = True , target = "target/path"
107+ )
108+
109+ def test_pathsep_normalization (self ):
110+ # Test that pathsep="\\" normalizes paths
111+ file1_path = self .test_dir / "file1.txt"
112+ file1_path .write_text ("content1" )
113+
114+ manifest_content = [
115+ f"regular|0|dir/file.txt|{ file1_path } " ,
116+ "symlink|link/path|target/path" ,
117+ ]
118+ self .manifest_path .write_text ("\n " .join (manifest_content ))
119+
120+ # Use backslash as pathsep
121+ self ._create_zip (pathsep = "\\ " )
122+
123+ with zipfile .ZipFile (self .output_zip , "r" ) as zf :
124+ # zipfile.namelist() always returns with forward slashes
125+ # But the content of the symlink should be normalized if it was passed through path_norm
126+ self .assertEqual (
127+ set (zf .namelist ()),
128+ {"dir\\ file.txt" , "runfiles\\ link\\ path" },
129+ )
130+ # The target of the symlink should have backslashes
131+ self .assertZipFileContent (
132+ zf , "runfiles\\ link\\ path" , is_symlink = True , target = "target\\ path"
133+ )
134+
135+ def test_symlink_precedence (self ):
136+ # Test that 'symlink' entries take precedence over others for the same path
137+ file1_path = self .test_dir / "file1.txt"
138+ file1_path .write_text ("content1" )
139+
140+ manifest_content = [
141+ # Same zip path: runfiles/my_ws/path/to/file
142+ f"rf-file|0|path/to/file|{ file1_path } " ,
143+ "symlink|my_ws/path/to/file|symlink/target" ,
144+ ]
145+ self .manifest_path .write_text ("\n " .join (manifest_content ))
146+
147+ self ._create_zip ()
148+
149+ with zipfile .ZipFile (self .output_zip , "r" ) as zf :
150+ self .assertEqual (zf .namelist (), ["runfiles/my_ws/path/to/file" ])
151+ # It should be the symlink, not the file
152+ self .assertZipFileContent (
153+ zf ,
154+ "runfiles/my_ws/path/to/file" ,
155+ is_symlink = True ,
156+ target = "symlink/target" ,
157+ )
158+
93159 def test_timestamps_are_deterministic (self ):
94160 # Create a content file with a specific recent timestamp
95161 file1_path = self .test_dir / "file1.txt"
0 commit comments