@@ -575,6 +575,54 @@ public void downloadOutputs_relativeDirectorySymlink_success() throws Exception
575
575
assertThat (context .isLockOutputFilesCalled ()).isTrue ();
576
576
}
577
577
578
+ @ Test
579
+ public void downloadOutputs_relativeOutputSymlinks_success () throws Exception {
580
+ // Test that download outputs works when the action result only contains output_symlinks
581
+ // and not output_file_symlinks or output_directory_symlinks, which are deprecated.
582
+ ActionResult .Builder builder = ActionResult .newBuilder ();
583
+ builder .addOutputSymlinksBuilder ().setPath ("outputs/a/b/link" ).setTarget ("../../foo" );
584
+ RemoteActionResult result =
585
+ RemoteActionResult .createFromCache (CachedActionResult .remote (builder .build ()));
586
+ Spawn spawn = newSpawnFromResult (result );
587
+ FakeSpawnExecutionContext context = newSpawnExecutionContext (spawn );
588
+ RemoteExecutionService service = newRemoteExecutionService ();
589
+ RemoteAction action = service .buildRemoteAction (spawn , context );
590
+
591
+ // Doesn't check for dangling links, hence download succeeds.
592
+ service .downloadOutputs (action , result );
593
+
594
+ Path path = execRoot .getRelative ("outputs/a/b/link" );
595
+ assertThat (path .isSymbolicLink ()).isTrue ();
596
+ assertThat (path .readSymbolicLink ()).isEqualTo (PathFragment .create ("../../foo" ));
597
+ assertThat (context .isLockOutputFilesCalled ()).isTrue ();
598
+ }
599
+
600
+ @ Test
601
+ public void downloadOutputs_outputSymlinksCompatibility_success () throws Exception {
602
+ // Test that download outputs works when the action result contains both output_symlinks
603
+ // and output_file_symlinks (or output_directory_symlinks).
604
+ //
605
+ // Remote Execution Server may set both fields to ensure backward compatibility with
606
+ // clients that don't support output_symlinks.
607
+ ActionResult .Builder builder = ActionResult .newBuilder ();
608
+ builder .addOutputFileSymlinksBuilder ().setPath ("outputs/a/b/link" ).setTarget ("foo" );
609
+ builder .addOutputSymlinksBuilder ().setPath ("outputs/a/b/link" ).setTarget ("foo" );
610
+ RemoteActionResult result =
611
+ RemoteActionResult .createFromCache (CachedActionResult .remote (builder .build ()));
612
+ Spawn spawn = newSpawnFromResult (result );
613
+ FakeSpawnExecutionContext context = newSpawnExecutionContext (spawn );
614
+ RemoteExecutionService service = newRemoteExecutionService ();
615
+ RemoteAction action = service .buildRemoteAction (spawn , context );
616
+
617
+ // Doesn't check for dangling links, hence download succeeds.
618
+ service .downloadOutputs (action , result );
619
+
620
+ Path path = execRoot .getRelative ("outputs/a/b/link" );
621
+ assertThat (path .isSymbolicLink ()).isTrue ();
622
+ assertThat (path .readSymbolicLink ()).isEqualTo (PathFragment .create ("foo" ));
623
+ assertThat (context .isLockOutputFilesCalled ()).isTrue ();
624
+ }
625
+
578
626
@ Test
579
627
public void downloadOutputs_relativeSymlinkInDirectory_success () throws Exception {
580
628
Tree tree =
@@ -666,6 +714,28 @@ public void downloadOutputs_absoluteSymlinkInDirectory_error() throws Exception
666
714
assertThat (context .isLockOutputFilesCalled ()).isTrue ();
667
715
}
668
716
717
+ @ Test
718
+ public void downloadOutputs_symlinkCollision_error () throws Exception {
719
+ ActionResult .Builder builder = ActionResult .newBuilder ();
720
+ builder .addOutputDirectorySymlinksBuilder ().setPath ("outputs/foo" ).setTarget ("foo1" );
721
+ builder .addOutputSymlinksBuilder ().setPath ("outputs/foo" ).setTarget ("foo2" );
722
+ RemoteActionResult result =
723
+ RemoteActionResult .createFromCache (CachedActionResult .remote (builder .build ()));
724
+ Spawn spawn = newSpawnFromResult (result );
725
+ FakeSpawnExecutionContext context = newSpawnExecutionContext (spawn );
726
+ RemoteExecutionService service = newRemoteExecutionService ();
727
+ RemoteAction action = service .buildRemoteAction (spawn , context );
728
+
729
+ IOException expected =
730
+ assertThrows (IOException .class , () -> service .downloadOutputs (action , result ));
731
+
732
+ assertThat (expected .getSuppressed ()).isEmpty ();
733
+ assertThat (expected ).hasMessageThat ().contains ("Symlink path collision" );
734
+ assertThat (expected ).hasMessageThat ().contains ("outputs/foo" );
735
+ assertThat (expected ).hasMessageThat ().contains ("foo1" );
736
+ assertThat (expected ).hasMessageThat ().contains ("foo2" );
737
+ }
738
+
669
739
@ Test
670
740
public void downloadOutputs_onFailure_maintainDirectories () throws Exception {
671
741
// Test that output directories are not deleted on download failure. See
@@ -1948,6 +2018,12 @@ private Spawn newSpawnFromResult(
1948
2018
outputs .add (output );
1949
2019
}
1950
2020
2021
+ for (OutputSymlink symlink : result .getOutputSymlinks ()) {
2022
+ Path path = remotePathResolver .outputPathToLocalPath (symlink .getPath ());
2023
+ Artifact output = ActionsTestUtil .createArtifact (artifactRoot , path );
2024
+ outputs .add (output );
2025
+ }
2026
+
1951
2027
return newSpawn (executionInfo , outputs .build ());
1952
2028
}
1953
2029
0 commit comments