Add additional symlink tests for MkdirAll

Adds additional symlink-related tests for FSTestOSWrapper's MkdirAll().
Also fixes a discrepancy in behavior compared to the real implementation
when hitting the symlink loop case.

Bug: 436025865
Change-Id: Idcff67fb52d4338a786285d6bb790ec9bc81fe68
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/276887
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@google.com>
diff --git a/tools/src/oswrapper/fstestoswrapper.go b/tools/src/oswrapper/fstestoswrapper.go
index be7a33e..832b402 100644
--- a/tools/src/oswrapper/fstestoswrapper.go
+++ b/tools/src/oswrapper/fstestoswrapper.go
@@ -589,7 +589,7 @@
 		return err
 	}
 
-	if !os.IsNotExist(err) {
+	if !os.IsNotExist(err) && !errors.Is(err, syscall.ELOOP) {
 		// Unexpected failure, probably indicating a bad path, i.e. parent is a file, so propagate the error
 		return err
 	}
diff --git a/tools/src/oswrapper/fstestoswrapper_mkdirall_test.go b/tools/src/oswrapper/fstestoswrapper_mkdirall_test.go
index 29a8cb5..b9a42e17 100644
--- a/tools/src/oswrapper/fstestoswrapper_mkdirall_test.go
+++ b/tools/src/oswrapper/fstestoswrapper_mkdirall_test.go
@@ -152,6 +152,29 @@
 				require.True(t, info.IsDir())
 			},
 		},
+		{
+			name: "Part of path is a broken symlink",
+			path: filepath.Join(root, "broken_link", "a"),
+			setup: unittestSetup{
+				initialSymlinks: map[string]string{filepath.Join(root, "broken_link"): filepath.Join(root, "nonexistent")},
+			},
+			expectedError: expectedError{
+				wantErrIs: os.ErrExist, // MkdirAll returns EEXIST because the broken link exists as a file/link
+			},
+		},
+		{
+			name: "Symlink loop in path",
+			path: filepath.Join(root, "loop1", "a"),
+			setup: unittestSetup{
+				initialSymlinks: map[string]string{
+					filepath.Join(root, "loop1"): filepath.Join(root, "loop2"),
+					filepath.Join(root, "loop2"): filepath.Join(root, "loop1"),
+				},
+			},
+			expectedError: expectedError{
+				wantErrIs: os.ErrExist, // MkdirAll recurses on ELOOP and hits EEXIST on the loop link itself
+			},
+		},
 	}
 
 	for _, tc := range tests {
@@ -225,6 +248,23 @@
 			}},
 			path: filepath.Join("link_to_dir", "a", "b"),
 		},
+		{
+			name: "Part of path is a broken symlink",
+			setup: matchesRealSetup{unittestSetup{
+				initialSymlinks: map[string]string{"broken_link": "nonexistent"},
+			}},
+			path: filepath.Join("broken_link", "a"),
+		},
+		{
+			name: "Symlink loop in path",
+			setup: matchesRealSetup{unittestSetup{
+				initialSymlinks: map[string]string{
+					"loop1": "loop2",
+					"loop2": "loop1",
+				},
+			}},
+			path: filepath.Join("loop1", "a"),
+		},
 	}
 
 	for _, tc := range tests {