| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| package com.android.tools.r8.desugar.desugaredlibrary.jdk11; |
| |
| import static com.android.tools.r8.desugar.desugaredlibrary.jdk11.FilesMoveCopyTest.TestClass.CopyOrMove.COPY; |
| import static com.android.tools.r8.desugar.desugaredlibrary.jdk11.FilesMoveCopyTest.TestClass.CopyOrMove.COPY_FROM_INPUT_STREAM; |
| import static com.android.tools.r8.desugar.desugaredlibrary.jdk11.FilesMoveCopyTest.TestClass.CopyOrMove.COPY_TO_OUTPUT_STREAM; |
| import static com.android.tools.r8.desugar.desugaredlibrary.jdk11.FilesMoveCopyTest.TestClass.CopyOrMove.MOVE; |
| import static com.android.tools.r8.desugar.desugaredlibrary.jdk11.FilesMoveCopyTest.TestClass.NewFile.EXISTING; |
| import static com.android.tools.r8.desugar.desugaredlibrary.jdk11.FilesMoveCopyTest.TestClass.NewFile.NEW; |
| import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS; |
| import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH; |
| |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestRuntime.CfVm; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.DexVm.Version; |
| import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; |
| import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification; |
| import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification; |
| import com.android.tools.r8.utils.StringUtils; |
| import com.google.common.collect.ImmutableList; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.CopyOption; |
| import java.nio.file.FileAlreadyExistsException; |
| import java.nio.file.Files; |
| import java.nio.file.LinkOption; |
| import java.nio.file.Path; |
| import java.nio.file.StandardCopyOption; |
| import java.nio.file.StandardOpenOption; |
| import java.util.List; |
| import org.junit.Assume; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| /** |
| * Tests Files#copy and Files#move methods. Known limitations/details below 26: - ATOMIC MOVE is a |
| * no-op, moves are effectively file renaming which seem to be atomic on Android. - COPY_ATTRIBUTES |
| * is a no-op, except if unsupported in which case it throws appropriately, now it seems only basic |
| * file attributes are supported below 26 anyway and such attributes are not meaningfully copied |
| * around anyway. Creation/last modification/last access times are set to the time of the move/copy, |
| * and the other ones such as "isDirectory" cannot be easily abused. |
| */ |
| @RunWith(Parameterized.class) |
| public class FilesMoveCopyTest extends DesugaredLibraryTestBase { |
| |
| private static final String EXPECTED_RESULT = |
| StringUtils.lines( |
| "Testing COPY_TO_OUTPUT_STREAM EXISTING NONE", |
| "COPY_TO_OUTPUT_STREAM_NONE", |
| "Testing MOVE NEW NONE", |
| "MOVE_NONE", |
| "Testing COPY NEW NONE", |
| "COPY_NONE", |
| "Testing COPY_FROM_INPUT_STREAM NEW NONE", |
| "COPY_FROM_INPUT_STREAM_NONE", |
| "Testing MOVE EXISTING NONE", |
| "Failure: class java.nio.file.FileAlreadyExistsException :: dest", |
| "Testing COPY EXISTING NONE", |
| "Failure: class java.nio.file.FileAlreadyExistsException :: dest", |
| "Testing COPY_FROM_INPUT_STREAM EXISTING NONE", |
| "Failure: class java.nio.file.FileAlreadyExistsException :: dest", |
| "Testing MOVE NEW ATOMIC_MOVE", |
| "MOVE_ATOMIC_MOVE", |
| "Testing COPY NEW ATOMIC_MOVE", |
| "Failure: class java.lang.UnsupportedOperationException :: Unsupported copy option", |
| "Testing COPY_FROM_INPUT_STREAM NEW ATOMIC_MOVE", |
| "Failure: class java.lang.UnsupportedOperationException :: ATOMIC_MOVE not supported", |
| "Testing MOVE NEW COPY_ATTRIBUTES", |
| "Failure: class java.lang.UnsupportedOperationException :: Unsupported copy option", |
| "Testing COPY NEW COPY_ATTRIBUTES", |
| "COPY_COPY_ATTRIBUTES", |
| "Testing COPY_FROM_INPUT_STREAM NEW COPY_ATTRIBUTES", |
| "Failure: class java.lang.UnsupportedOperationException :: COPY_ATTRIBUTES not supported", |
| "Testing MOVE NEW REPLACE_EXISTING", |
| "MOVE_REPLACE_EXISTING", |
| "Testing COPY NEW REPLACE_EXISTING", |
| "COPY_REPLACE_EXISTING", |
| "Testing COPY_FROM_INPUT_STREAM NEW REPLACE_EXISTING", |
| "COPY_FROM_INPUT_STREAM_REPLACE_EXISTING", |
| "Testing MOVE EXISTING REPLACE_EXISTING", |
| "MOVE_REPLACE_EXISTING", |
| "Testing COPY EXISTING REPLACE_EXISTING", |
| "COPY_REPLACE_EXISTING", |
| "Testing COPY_FROM_INPUT_STREAM EXISTING REPLACE_EXISTING", |
| "COPY_FROM_INPUT_STREAM_REPLACE_EXISTING", |
| "Testing MOVE NEW NOFOLLOW_LINKS", |
| "MOVE_NOFOLLOW_LINKS", |
| "Testing COPY NEW NOFOLLOW_LINKS", |
| "COPY_NOFOLLOW_LINKS", |
| "Testing COPY_FROM_INPUT_STREAM NEW NOFOLLOW_LINKS", |
| "Failure: class java.lang.UnsupportedOperationException :: NOFOLLOW_LINKS not supported"); |
| |
| private final TestParameters parameters; |
| private final LibraryDesugaringSpecification libraryDesugaringSpecification; |
| private final CompilationSpecification compilationSpecification; |
| |
| @Parameters(name = "{0}, spec: {1}, {2}") |
| public static List<Object[]> data() { |
| return buildParameters( |
| // Skip Android 4.4.4 due to missing libjavacrypto. |
| getTestParameters() |
| .withCfRuntime(CfVm.JDK11) |
| .withDexRuntime(Version.V4_0_4) |
| .withDexRuntimesStartingFromIncluding(Version.V5_1_1) |
| .withAllApiLevels() |
| .build(), |
| ImmutableList.of(JDK11_PATH), |
| DEFAULT_SPECIFICATIONS); |
| } |
| |
| public FilesMoveCopyTest( |
| TestParameters parameters, |
| LibraryDesugaringSpecification libraryDesugaringSpecification, |
| CompilationSpecification compilationSpecification) { |
| this.parameters = parameters; |
| this.libraryDesugaringSpecification = libraryDesugaringSpecification; |
| this.compilationSpecification = compilationSpecification; |
| } |
| |
| @Test |
| public void test() throws Throwable { |
| if (parameters.isCfRuntime()) { |
| // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8 |
| // behaves differently on this test. |
| Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows()); |
| testForJvm() |
| .addInnerClasses(getClass()) |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED_RESULT); |
| return; |
| } |
| testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification) |
| .addInnerClasses(getClass()) |
| .addKeepMainRule(TestClass.class) |
| .compile() |
| .withArt6Plus64BitsLib() |
| .run(parameters.getRuntime(), TestClass.class) |
| .assertSuccessWithOutput(EXPECTED_RESULT); |
| } |
| |
| public static class TestClass { |
| |
| enum CopyOrMove { |
| COPY, |
| COPY_FROM_INPUT_STREAM, |
| COPY_TO_OUTPUT_STREAM, |
| MOVE |
| } |
| |
| enum NewFile { |
| EXISTING, |
| NEW |
| } |
| |
| public static void main(String[] args) throws Throwable { |
| test(COPY_TO_OUTPUT_STREAM, EXISTING, null); |
| for (CopyOption copyOption : allOptions()) { |
| test(MOVE, NEW, copyOption); |
| test(COPY, NEW, copyOption); |
| test(COPY_FROM_INPUT_STREAM, NEW, copyOption); |
| if (copyOption == null || copyOption == StandardCopyOption.REPLACE_EXISTING) { |
| test(MOVE, EXISTING, copyOption); |
| test(COPY, EXISTING, copyOption); |
| test(COPY_FROM_INPUT_STREAM, EXISTING, copyOption); |
| } |
| } |
| } |
| |
| private static CopyOption[] allOptions() { |
| return new CopyOption[] { |
| null, |
| StandardCopyOption.ATOMIC_MOVE, |
| StandardCopyOption.COPY_ATTRIBUTES, |
| StandardCopyOption.REPLACE_EXISTING, |
| LinkOption.NOFOLLOW_LINKS |
| }; |
| } |
| |
| private static void clearTmp(Path src, Path dest) throws IOException { |
| if (Files.exists(src)) { |
| Files.delete(src); |
| } |
| if (Files.exists(dest)) { |
| Files.delete(dest); |
| } |
| } |
| |
| private static void test(CopyOrMove copyOrMove, NewFile newFile, CopyOption copyOption) |
| throws IOException { |
| String copyOptionString = copyOption == null ? "NONE" : copyOption.toString(); |
| CopyOption[] opts = copyOption == null ? new CopyOption[0] : new CopyOption[] {copyOption}; |
| System.out.println("Testing " + copyOrMove + " " + newFile + " " + copyOptionString); |
| Path src = Files.createTempFile("src_", ".txt"); |
| Path dest = |
| newFile == NEW |
| ? src.getParent().resolve("dest_" + System.currentTimeMillis() + ".txt") |
| : Files.createTempFile("dest_", ".txt"); |
| String toWrite = copyOrMove + "_" + copyOptionString; |
| Files.write(src, toWrite.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE); |
| try { |
| switch (copyOrMove) { |
| case COPY: |
| Files.copy(src, dest, opts); |
| break; |
| case MOVE: |
| Files.move(src, dest, opts); |
| break; |
| case COPY_FROM_INPUT_STREAM: |
| FileInputStream in = new FileInputStream(src.toFile()); |
| Files.copy(in, dest, opts); |
| in.close(); |
| break; |
| case COPY_TO_OUTPUT_STREAM: |
| if (opts.length != 0) { |
| throw new UnsupportedOperationException("Api does not encode options"); |
| } |
| FileOutputStream out = new FileOutputStream(dest.toFile()); |
| Files.copy(src, out); |
| out.close(); |
| break; |
| } |
| System.out.println(Files.readAllLines(dest).get(0)); |
| } catch (Throwable t) { |
| String subMessage; |
| if (t instanceof FileAlreadyExistsException && t.getMessage() != null) { |
| // The message is the file path, however we cannot rely on temp path, so we check |
| // only against the known part of the path. |
| String[] split = t.getMessage().split("/"); |
| subMessage = split[split.length - 1].substring(0, 4); |
| } else { |
| subMessage = t.getMessage(); |
| } |
| System.out.println("Failure: " + t.getClass() + " :: " + subMessage); |
| } |
| clearTmp(src, dest); |
| } |
| } |
| } |