@@ -590,6 +590,54 @@ public void downloadOutputs_relativeDirectorySymlink_success() throws Exception
590590 assertThat (context .isLockOutputFilesCalled ()).isTrue ();
591591 }
592592
593+ @ Test
594+ public void downloadOutputs_relativeOutputSymlinks_success () throws Exception {
595+ // Test that download outputs works when the action result only contains output_symlinks
596+ // and not output_file_symlinks or output_directory_symlinks, which are deprecated.
597+ ActionResult .Builder builder = ActionResult .newBuilder ();
598+ builder .addOutputSymlinksBuilder ().setPath ("outputs/a/b/link" ).setTarget ("../../foo" );
599+ RemoteActionResult result =
600+ RemoteActionResult .createFromCache (CachedActionResult .remote (builder .build ()));
601+ Spawn spawn = newSpawnFromResult (result );
602+ FakeSpawnExecutionContext context = newSpawnExecutionContext (spawn );
603+ RemoteExecutionService service = newRemoteExecutionService ();
604+ RemoteAction action = service .buildRemoteAction (spawn , context );
605+
606+ // Doesn't check for dangling links, hence download succeeds.
607+ service .downloadOutputs (action , result );
608+
609+ Path path = execRoot .getRelative ("outputs/a/b/link" );
610+ assertThat (path .isSymbolicLink ()).isTrue ();
611+ assertThat (path .readSymbolicLink ()).isEqualTo (PathFragment .create ("../../foo" ));
612+ assertThat (context .isLockOutputFilesCalled ()).isTrue ();
613+ }
614+
615+ @ Test
616+ public void downloadOutputs_outputSymlinksCompatibility_success () throws Exception {
617+ // Test that download outputs works when the action result contains both output_symlinks
618+ // and output_file_symlinks (or output_directory_symlinks).
619+ //
620+ // Remote Execution Server may set both fields to ensure backward compatibility with
621+ // clients that don't support output_symlinks.
622+ ActionResult .Builder builder = ActionResult .newBuilder ();
623+ builder .addOutputFileSymlinksBuilder ().setPath ("outputs/a/b/link" ).setTarget ("foo" );
624+ builder .addOutputSymlinksBuilder ().setPath ("outputs/a/b/link" ).setTarget ("foo" );
625+ RemoteActionResult result =
626+ RemoteActionResult .createFromCache (CachedActionResult .remote (builder .build ()));
627+ Spawn spawn = newSpawnFromResult (result );
628+ FakeSpawnExecutionContext context = newSpawnExecutionContext (spawn );
629+ RemoteExecutionService service = newRemoteExecutionService ();
630+ RemoteAction action = service .buildRemoteAction (spawn , context );
631+
632+ // Doesn't check for dangling links, hence download succeeds.
633+ service .downloadOutputs (action , result );
634+
635+ Path path = execRoot .getRelative ("outputs/a/b/link" );
636+ assertThat (path .isSymbolicLink ()).isTrue ();
637+ assertThat (path .readSymbolicLink ()).isEqualTo (PathFragment .create ("foo" ));
638+ assertThat (context .isLockOutputFilesCalled ()).isTrue ();
639+ }
640+
593641 @ Test
594642 public void downloadOutputs_relativeSymlinkInDirectory_success () throws Exception {
595643 Tree tree =
@@ -681,6 +729,28 @@ public void downloadOutputs_absoluteSymlinkInDirectory_error() throws Exception
681729 assertThat (context .isLockOutputFilesCalled ()).isTrue ();
682730 }
683731
732+ @ Test
733+ public void downloadOutputs_symlinkCollision_error () throws Exception {
734+ ActionResult .Builder builder = ActionResult .newBuilder ();
735+ builder .addOutputDirectorySymlinksBuilder ().setPath ("outputs/foo" ).setTarget ("foo1" );
736+ builder .addOutputSymlinksBuilder ().setPath ("outputs/foo" ).setTarget ("foo2" );
737+ RemoteActionResult result =
738+ RemoteActionResult .createFromCache (CachedActionResult .remote (builder .build ()));
739+ Spawn spawn = newSpawnFromResult (result );
740+ FakeSpawnExecutionContext context = newSpawnExecutionContext (spawn );
741+ RemoteExecutionService service = newRemoteExecutionService ();
742+ RemoteAction action = service .buildRemoteAction (spawn , context );
743+
744+ IOException expected =
745+ assertThrows (IOException .class , () -> service .downloadOutputs (action , result ));
746+
747+ assertThat (expected .getSuppressed ()).isEmpty ();
748+ assertThat (expected ).hasMessageThat ().contains ("Symlink path collision" );
749+ assertThat (expected ).hasMessageThat ().contains ("outputs/foo" );
750+ assertThat (expected ).hasMessageThat ().contains ("foo1" );
751+ assertThat (expected ).hasMessageThat ().contains ("foo2" );
752+ }
753+
684754 @ Test
685755 public void downloadOutputs_onFailure_maintainDirectories () throws Exception {
686756 // Test that output directories are not deleted on download failure. See
@@ -2014,6 +2084,12 @@ private Spawn newSpawnFromResult(
20142084 outputs .add (output );
20152085 }
20162086
2087+ for (OutputSymlink symlink : result .getOutputSymlinks ()) {
2088+ Path path = remotePathResolver .outputPathToLocalPath (symlink .getPath ());
2089+ Artifact output = ActionsTestUtil .createArtifact (artifactRoot , path );
2090+ outputs .add (output );
2091+ }
2092+
20172093 return newSpawn (executionInfo , outputs .build ());
20182094 }
20192095
0 commit comments