/*
* Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.android;
import static com.facebook.buck.android.DefaultAndroidDirectoryResolver.NDK_POST_R11_VERSION_FILENAME;
import static com.facebook.buck.android.DefaultAndroidDirectoryResolver.NDK_PRE_R11_VERSION_FILENAME;
import static com.facebook.buck.android.DefaultAndroidDirectoryResolver.NDK_TARGET_VERSION_IS_EMPTY_MESSAGE;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import com.facebook.buck.io.MoreFiles;
import com.facebook.buck.testutil.integration.TemporaryPaths;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class DefaultAndroidDirectoryResolverTest {
@Rule public TemporaryPaths tmpDir = new TemporaryPaths();
@Rule public ExpectedException expectedException = ExpectedException.none();
@Test
public void getAbsentSdkNdk() {
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of(),
Optional.empty(),
Optional.empty());
assertEquals(Optional.empty(), resolver.getSdkOrAbsent());
assertEquals(Optional.empty(), resolver.getNdkOrAbsent());
}
@Test
public void throwAtAbsentSdk() {
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of(),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(DefaultAndroidDirectoryResolver.SDK_NOT_FOUND_MESSAGE);
resolver.getSdkOrThrow();
}
@Test
public void throwAtAbsentBuildTools() {
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of(),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(DefaultAndroidDirectoryResolver.TOOLS_NEED_SDK_MESSAGE);
resolver.getBuildToolsOrThrow();
}
@Test
public void throwAtAbsentNdk() {
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of(),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(DefaultAndroidDirectoryResolver.NDK_NOT_FOUND_MESSAGE);
resolver.getNdkOrThrow();
}
@Test
public void throwAtSdkPathIsNotDirectory() throws IOException {
Path file = tmpDir.getRoot().resolve(tmpDir.newFile("file"));
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", file.toString()),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
"Environment variable 'ANDROID_SDK' points to a path that is not a directory: '"
+ file
+ "'.");
resolver.getSdkOrThrow();
}
@Test
public void throwAtNdkPathIsNotDirectory() throws IOException {
Path file = tmpDir.getRoot().resolve(tmpDir.newFile("file"));
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", file.toString()),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
"Environment variable 'ANDROID_NDK' points to a path that is not a directory: '"
+ file
+ "'.");
resolver.getNdkOrThrow();
}
@Test
public void throwAtGetNdkDirectoryIsEmpty() throws IOException {
Path ndkDir = tmpDir.newFolder("ndk-dir");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
ndkDir.toAbsolutePath()
+ " does not contain a valid "
+ "properties file for Android NDK.");
resolver.getNdkOrThrow();
}
@Test
public void throwAtGetNdkIsUnsupportedVersion() throws IOException {
Path ndkDir = createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir", "r9q")[0];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.of("r9e"));
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
"Buck is configured to use Android NDK version r9e at "
+ "ndk.dir or ANDROID_NDK or NDK_HOME. The found version is r9q located at "
+ ndkDir);
resolver.getNdkOrThrow();
}
@Test
public void throwAtGetNdkTargetVersionIsEmpty() throws IOException {
createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir-r9a", "r9a-rc2");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK_REPOSITORY", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of(""));
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(NDK_TARGET_VERSION_IS_EMPTY_MESSAGE);
resolver.getNdkOrThrow();
}
@Test
public void throwAtGetNdkVersionFileIsEmpty() throws IOException {
createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir-r9a", "");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of(""));
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
tmpDir.getRoot() + " does not contain a valid properties " + "file for Android NDK.");
resolver.getNdkOrThrow();
}
@Test
public void getNdkSpecificVersion() throws IOException {
Path ndkDir = createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir", "r9d (64-bit)")[0];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.of("r9d"));
assertEquals(ndkDir, resolver.getNdkOrThrow());
ndkDir =
createTmpNdkVersions(
NDK_POST_R11_VERSION_FILENAME,
"ndk-dir-new",
"Pkg.Desc = Android NDK\nPkg.Revision = 11.2.2725575")[0];
resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.of("11.2"));
assertEquals(ndkDir, resolver.getNdkOrThrow());
}
@Test
public void getNdkInexactMatchVersion() throws IOException {
Path ndkDir = createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir", "r9d (64-bit)")[0];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.of("r9"));
assertEquals(ndkDir, resolver.getNdkOrThrow());
ndkDir =
createTmpNdkVersions(
NDK_POST_R11_VERSION_FILENAME,
"ndk-dir-new",
"Pkg.Desc = Android NDK\nPkg.Revision = 11.2.2725575")[0];
resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.of("11"));
assertEquals(ndkDir, resolver.getNdkOrThrow());
}
@Test
public void getNdkNewestVersion() throws IOException {
Path ndkDir = createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir-old", "r9e")[0];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.empty());
assertEquals(ndkDir, resolver.getNdkOrThrow());
ndkDir =
createTmpNdkVersions(
NDK_POST_R11_VERSION_FILENAME,
"ndk-dir-new",
"Pkg.Desc = Android NDK\nPkg.Revision = 11.2.2725575")[0];
resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.empty());
assertEquals(ndkDir, resolver.getNdkOrThrow());
}
@Test
public void getNdkOrAbsent() throws IOException {
Path ndkDir = createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir-old", "r9e")[0];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.empty());
assertEquals(ndkDir, resolver.getNdkOrAbsent().get());
resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of(),
Optional.empty(),
Optional.empty());
assertEquals(Optional.empty(), resolver.getNdkOrAbsent());
}
@Test
public void scanNdkSpecificVersion() throws IOException {
Path expectedPath =
createTmpNdkVersions(
NDK_PRE_R11_VERSION_FILENAME,
"ndk-dir-r9a",
"r9a",
"ndk-dir-r9b",
"r9b",
"ndk-dir-r9c",
"r9c")[1];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK_REPOSITORY", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of("r9b"));
assertEquals(expectedPath, resolver.getNdkOrThrow());
}
@Test
public void throwAtscanNdkSpecificVersion() throws IOException {
createTmpNdkVersions(
NDK_PRE_R11_VERSION_FILENAME,
"ndk-dir-r9a",
"r9a",
"ndk-dir-r9b",
"r9b",
"ndk-dir-r9c",
"r9c");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK_REPOSITORY", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of("r42z"));
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
"Target NDK version r42z is not available. The following "
+ "versions are available: r9c, r9b, r9a");
resolver.getNdkOrThrow();
}
@Test
public void scanNdkInexactMatchVersion() throws IOException {
Path expectedPath =
createTmpNdkVersions(
NDK_POST_R11_VERSION_FILENAME,
"ndk-dir-r11",
"Pkg.Desc = Android NDK\nPkg.Revision = 11.2",
"ndk-dir-r12",
"Pkg.Desc = Android NDK\nPkg.Revision = 12.4",
"ndk-dir-r13",
"Pkg.Desc = Android NDK\nPkg.Revision = 13.2")[2];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK_REPOSITORY", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of("13"));
assertEquals(expectedPath, resolver.getNdkOrThrow());
}
@Test
public void scanNdkNewestVersion() throws IOException {
Path expectedPath =
createTmpNdkVersions(
NDK_POST_R11_VERSION_FILENAME,
"ndk-dir-r11",
"Pkg.Desc = Android NDK\nPkg.Revision = 11.2",
"ndk-dir-r12",
"Pkg.Desc = Android NDK\nPkg.Revision = 12.4",
"ndk-dir-r13",
"Pkg.Desc = Android NDK\nPkg.Revision = 13.2")[2];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK_REPOSITORY", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.empty());
assertEquals(expectedPath, resolver.getNdkOrThrow());
}
@Test
public void testFindAndroidNdkDirThrowOnUnsupportedVersion() throws IOException {
Path ndkDir = createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir", "r9q")[0];
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", ndkDir.toString()),
Optional.empty(),
Optional.of("r9e"));
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
"Buck is configured to use Android NDK version r9e at "
+ "ndk.dir or ANDROID_NDK or NDK_HOME. The found version is r9q located at "
+ ndkDir);
resolver.getNdkOrThrow();
}
@Test
public void notEqualWhenNdkIsDifferent() throws IOException {
createTmpNdkVersions(
"ndk-dir-r9a", "r9a",
"ndk-dir-r9b", "r9b");
DefaultAndroidDirectoryResolver resolver1 =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of("r9a"));
DefaultAndroidDirectoryResolver resolver2 =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of("r9b"));
assertNotEquals(resolver1, resolver2);
}
@Test
public void testFindAndroidNdkDirScanTakesVersionEmptyRequested() throws IOException {
createTmpNdkVersions(NDK_PRE_R11_VERSION_FILENAME, "ndk-dir-r9a", "r9a-rc2");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_NDK_REPOSITORY", tmpDir.getRoot().toString()),
Optional.empty(),
Optional.of(""));
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(NDK_TARGET_VERSION_IS_EMPTY_MESSAGE);
resolver.getNdkOrThrow();
}
@Test
public void buildToolsFallsBacktoPlatformTools() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "platform-tools");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
assertThat(resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("platform-tools"));
}
@Test
public void buildToolsAPIVersionFound() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "build-tools/android-4.2.2");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
assertThat(resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("android-4.2.2"));
}
@Test
public void buildToolsWithBuildToolsPrefix() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "build-tools/build-tools-17.2.2");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
assertThat(
resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("build-tools-17.2.2"));
}
@Test
public void buildToolsInvalidPrefixThrows() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "build-tools/foobar-17.2.2");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage("foobar-17.2.2");
resolver.getBuildToolsOrThrow();
}
@Test
public void buildToolsEmptyDirectoryThrows() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
sdkDir.resolve("build-tools").toFile().mkdir();
sdkDir.resolve("tools").toFile().mkdir();
Path toolsDir = sdkDir.resolve("tools").toAbsolutePath();
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage(
"null was empty, but should have contained a subdirectory "
+ "with build tools. Install them using the Android SDK Manager ("
+ toolsDir
+ File.separator
+ "android).");
resolver.getBuildToolsOrThrow();
}
@Test
public void buildToolsRCVersionsFound() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "build-tools/23.0.0_rc1");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
assertThat(resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("23.0.0_rc1"));
}
@Test
public void buildToolsRCAndNonRCMix() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "build-tools/22.0.0", "build-tools/23.0.0_rc1");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
assertThat(resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("23.0.0_rc1"));
}
@Test
public void preferBuildToolsVersionedFoldersOverAPIFolders() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(
sdkDir,
"build-tools/android-4.2.2",
"build-tools/android-4.1",
"build-tools/android-4.0.0",
"build-tools/build-tools-15.0.0",
"build-tools/17.0.0",
"build-tools/16.0.0");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.empty(),
Optional.empty());
assertThat(resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("17.0.0"));
}
@Test
public void targettedBuildToolsVersionIsSelected() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(
sdkDir, "build-tools/16.0.0", "build-tools/17.0.0", "build-tools/18.0.0");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.of("17.0.0"),
Optional.empty());
assertThat(resolver.getBuildToolsOrThrow().getFileName().toString(), equalTo("17.0.0"));
}
@Test
public void targettedBuildToolsVersionNotAvailableThrows() throws IOException {
Path sdkDir = tmpDir.newFolder("sdk");
createBuildToolsVersions(sdkDir, "build-tools/18.0.0");
DefaultAndroidDirectoryResolver resolver =
new DefaultAndroidDirectoryResolver(
tmpDir.getRoot().getFileSystem(),
ImmutableMap.of("ANDROID_SDK", sdkDir.toString()),
Optional.of("2.0.0"),
Optional.empty());
expectedException.expect(HumanReadableException.class);
expectedException.expectMessage("2.0.0");
resolver.getBuildToolsOrThrow();
}
private Path[] createTmpNdkVersions(String filename, String... directoryNamesAndVersionStrings)
throws IOException {
Path[] ret = new Path[directoryNamesAndVersionStrings.length / 2];
for (int i = 0; i < directoryNamesAndVersionStrings.length / 2; i++) {
String folderName = directoryNamesAndVersionStrings[i * 2];
String version = directoryNamesAndVersionStrings[(i * 2) + 1];
ret[i] = tmpDir.newFolder(folderName);
Path releaseFile = tmpDir.newFile(folderName + "/" + filename);
MoreFiles.writeLinesToFile(ImmutableList.of(version), releaseFile);
}
return ret;
}
private Path[] createBuildToolsVersions(Path sdkDir, String... directoryNames)
throws IOException {
Path[] ret = new Path[directoryNames.length];
for (int i = 0; i < directoryNames.length; i++) {
File folder = sdkDir.resolve(directoryNames[i]).toFile();
assertThat(folder.mkdirs(), Matchers.is(true));
ret[i] = folder.toPath();
}
return ret;
}
}