@@ -148,4 +148,159 @@ mod tests {
148148 // Clean up
149149 fs:: remove_dir_all ( test_path) . unwrap ( ) ;
150150 }
151+
152+ #[ test]
153+ fn test_parse_porcelain_output_edge_cases ( ) {
154+ // Test empty lines and malformed lines
155+ let output = "\n \n M valid.txt\n XX\n \n A another.txt\n " ;
156+ let status = GitStatus :: parse_porcelain_output ( output) ;
157+
158+ assert_eq ! ( status. files. len( ) , 2 ) ;
159+ assert ! ( status. files. contains( & ( FileStatus :: Modified , "valid.txt" . to_string( ) ) ) ) ;
160+ assert ! ( status. files. contains( & ( FileStatus :: Added , "another.txt" . to_string( ) ) ) ) ;
161+ }
162+
163+ #[ test]
164+ fn test_parse_porcelain_all_status_types ( ) {
165+ let output = "M modified.txt\n A added.txt\n D deleted.txt\n R renamed.txt\n C copied.txt\n ?? untracked.txt\n !! ignored.txt\n " ;
166+ let status = GitStatus :: parse_porcelain_output ( output) ;
167+
168+ assert_eq ! ( status. files. len( ) , 7 ) ;
169+ assert ! ( status. files. contains( & ( FileStatus :: Modified , "modified.txt" . to_string( ) ) ) ) ;
170+ assert ! ( status. files. contains( & ( FileStatus :: Added , "added.txt" . to_string( ) ) ) ) ;
171+ assert ! ( status. files. contains( & ( FileStatus :: Deleted , "deleted.txt" . to_string( ) ) ) ) ;
172+ assert ! ( status. files. contains( & ( FileStatus :: Renamed , "renamed.txt" . to_string( ) ) ) ) ;
173+ assert ! ( status. files. contains( & ( FileStatus :: Copied , "copied.txt" . to_string( ) ) ) ) ;
174+ assert ! ( status. files. contains( & ( FileStatus :: Untracked , "untracked.txt" . to_string( ) ) ) ) ;
175+ assert ! ( status. files. contains( & ( FileStatus :: Ignored , "ignored.txt" . to_string( ) ) ) ) ;
176+ }
177+
178+ #[ test]
179+ fn test_parse_porcelain_worktree_modifications ( ) {
180+ let output = " M worktree_modified.txt\n " ;
181+ let status = GitStatus :: parse_porcelain_output ( output) ;
182+
183+ assert_eq ! ( status. files. len( ) , 1 ) ;
184+ assert ! ( status. files. contains( & ( FileStatus :: Modified , "worktree_modified.txt" . to_string( ) ) ) ) ;
185+ }
186+
187+ #[ test]
188+ fn test_parse_porcelain_unknown_status ( ) {
189+ let output = "XY unknown.txt\n Z another_unknown.txt\n " ;
190+ let status = GitStatus :: parse_porcelain_output ( output) ;
191+
192+ // Unknown statuses should be ignored
193+ assert_eq ! ( status. files. len( ) , 0 ) ;
194+ }
195+
196+ #[ test]
197+ fn test_file_status_equality ( ) {
198+ assert_eq ! ( FileStatus :: Modified , FileStatus :: Modified ) ;
199+ assert_ne ! ( FileStatus :: Modified , FileStatus :: Added ) ;
200+ assert_eq ! ( FileStatus :: Untracked , FileStatus :: Untracked ) ;
201+ }
202+
203+ #[ test]
204+ fn test_file_status_clone ( ) {
205+ let status = FileStatus :: Modified ;
206+ let cloned = status. clone ( ) ;
207+ assert_eq ! ( status, cloned) ;
208+ }
209+
210+ #[ test]
211+ fn test_file_status_debug ( ) {
212+ let status = FileStatus :: Modified ;
213+ let debug_str = format ! ( "{:?}" , status) ;
214+ assert_eq ! ( debug_str, "Modified" ) ;
215+ }
216+
217+ #[ test]
218+ fn test_git_status_equality ( ) {
219+ let files1 = vec ! [
220+ ( FileStatus :: Modified , "file1.txt" . to_string( ) ) ,
221+ ( FileStatus :: Added , "file2.txt" . to_string( ) ) ,
222+ ] ;
223+ let files2 = vec ! [
224+ ( FileStatus :: Modified , "file1.txt" . to_string( ) ) ,
225+ ( FileStatus :: Added , "file2.txt" . to_string( ) ) ,
226+ ] ;
227+ let files3 = vec ! [
228+ ( FileStatus :: Modified , "different.txt" . to_string( ) ) ,
229+ ] ;
230+
231+ let status1 = GitStatus { files : files1. into_boxed_slice ( ) } ;
232+ let status2 = GitStatus { files : files2. into_boxed_slice ( ) } ;
233+ let status3 = GitStatus { files : files3. into_boxed_slice ( ) } ;
234+
235+ assert_eq ! ( status1, status2) ;
236+ assert_ne ! ( status1, status3) ;
237+ }
238+
239+ #[ test]
240+ fn test_git_status_clone ( ) {
241+ let files = vec ! [
242+ ( FileStatus :: Modified , "file1.txt" . to_string( ) ) ,
243+ ] ;
244+ let status1 = GitStatus { files : files. into_boxed_slice ( ) } ;
245+ let status2 = status1. clone ( ) ;
246+
247+ assert_eq ! ( status1, status2) ;
248+ }
249+
250+ #[ test]
251+ fn test_git_status_debug ( ) {
252+ let files = vec ! [
253+ ( FileStatus :: Modified , "file1.txt" . to_string( ) ) ,
254+ ] ;
255+ let status = GitStatus { files : files. into_boxed_slice ( ) } ;
256+ let debug_str = format ! ( "{:?}" , status) ;
257+
258+ assert ! ( debug_str. contains( "GitStatus" ) ) ;
259+ assert ! ( debug_str. contains( "Modified" ) ) ;
260+ assert ! ( debug_str. contains( "file1.txt" ) ) ;
261+ }
262+
263+ #[ test]
264+ fn test_files_with_status_multiple_same_status ( ) {
265+ let output = "M file1.txt\n M file2.txt\n A file3.txt\n " ;
266+ let status = GitStatus :: parse_porcelain_output ( output) ;
267+
268+ let modified = status. files_with_status ( FileStatus :: Modified ) ;
269+ assert_eq ! ( modified. len( ) , 2 ) ;
270+ assert ! ( modified. contains( &&"file1.txt" . to_string( ) ) ) ;
271+ assert ! ( modified. contains( &&"file2.txt" . to_string( ) ) ) ;
272+
273+ let added = status. files_with_status ( FileStatus :: Added ) ;
274+ assert_eq ! ( added. len( ) , 1 ) ;
275+ assert ! ( added. contains( &&"file3.txt" . to_string( ) ) ) ;
276+ }
277+
278+ #[ test]
279+ fn test_files_with_status_no_matches ( ) {
280+ let output = "M file1.txt\n A file2.txt\n " ;
281+ let status = GitStatus :: parse_porcelain_output ( output) ;
282+
283+ let deleted = status. files_with_status ( FileStatus :: Deleted ) ;
284+ assert ! ( deleted. is_empty( ) ) ;
285+ }
286+
287+ #[ test]
288+ fn test_parse_porcelain_filenames_with_spaces ( ) {
289+ let output = "M file with spaces.txt\n A another file.txt\n " ;
290+ let status = GitStatus :: parse_porcelain_output ( output) ;
291+
292+ assert_eq ! ( status. files. len( ) , 2 ) ;
293+ assert ! ( status. files. contains( & ( FileStatus :: Modified , "file with spaces.txt" . to_string( ) ) ) ) ;
294+ assert ! ( status. files. contains( & ( FileStatus :: Added , "another file.txt" . to_string( ) ) ) ) ;
295+ }
296+
297+ #[ test]
298+ fn test_parse_porcelain_unicode_filenames ( ) {
299+ let output = "M 测试文件.txt\n A 🚀rocket.txt\n " ;
300+ let status = GitStatus :: parse_porcelain_output ( output) ;
301+
302+ assert_eq ! ( status. files. len( ) , 2 ) ;
303+ assert ! ( status. files. contains( & ( FileStatus :: Modified , "测试文件.txt" . to_string( ) ) ) ) ;
304+ assert ! ( status. files. contains( & ( FileStatus :: Added , "🚀rocket.txt" . to_string( ) ) ) ) ;
305+ }
151306}
0 commit comments