blob: b954c7a1131fdad0b87bd799c99ac1045c9c02fb [file] [log] [blame]
/*
* Copyright 2012-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 org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.android.SmartDexingStep.DxPseudoRule;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.rules.ImmutableSha1HashCode;
import com.facebook.buck.rules.Sha1HashCode;
import com.facebook.buck.step.CompositeStep;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.TestExecutionContext;
import com.facebook.buck.testutil.MoreAsserts;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
import org.easymock.EasyMockSupport;
import org.easymock.IAnswer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class SmartDexingStepTest extends EasyMockSupport {
@Rule
public TemporaryFolder tmpDir = new TemporaryFolder();
/**
* Tests whether pseudo rule cache detection is working properly.
*/
@Test
public void testDxPseudoRuleCaching() throws IOException {
ExecutionContext context = createMock(ExecutionContext.class);
replay(context);
File testIn = new File(tmpDir.getRoot(), "testIn");
ZipOutputStream zipOut = new ZipOutputStream(
new BufferedOutputStream(new FileOutputStream(testIn)));
try {
zipOut.putNextEntry(new ZipEntry("foobar"));
zipOut.write(new byte[] { 0 });
} finally {
zipOut.close();
}
File outputFile = tmpDir.newFile("out.dex");
Path outputHashFile = new File(tmpDir.getRoot(), "out.dex.hash").toPath();
Files.write("dummy", outputHashFile.toFile(), Charsets.UTF_8);
ProjectFilesystem filesystem = new ProjectFilesystem(tmpDir.getRoot().toPath());
Sha1HashCode actualHashCode = ImmutableSha1HashCode.of(Strings.repeat("a", 40));
DxPseudoRule rule = new DxPseudoRule(
filesystem,
ImmutableMap.of(testIn.toPath(), actualHashCode),
ImmutableSet.of(testIn.toPath()),
outputFile.toPath(),
outputHashFile,
EnumSet.of(DxStep.Option.NO_OPTIMIZE));
assertFalse("'dummy' is not a matching input hash", rule.checkIsCached());
// Write the real hash into the output hash file and ensure that checkIsCached now
// yields true.
String actualHash = rule.hashInputs();
assertFalse(actualHash.isEmpty());
Files.write(actualHash, outputHashFile.toFile(), Charsets.UTF_8);
assertTrue("Matching input hash should be considered cached", rule.checkIsCached());
}
@Test
public void testCreateDxStepForDxPseudoRuleWithXzOutput() {
ImmutableList<Path> filesToDex = ImmutableList.of(
Paths.get("foo.dex.jar"), Paths.get("bar.dex.jar"));
Path outputPath = Paths.get("classes.dex.jar.xz");
EnumSet<DxStep.Option> dxOptions = EnumSet.noneOf(DxStep.Option.class);
Step dxStep = SmartDexingStep.createDxStepForDxPseudoRule(filesToDex, outputPath, dxOptions);
assertTrue("Result should be a CompositeStep.", dxStep instanceof CompositeStep);
List<Step> steps = ImmutableList.copyOf((CompositeStep) dxStep);
String xmx = DxStep.XMX_OVERRIDE.isEmpty() ? "" : DxStep.XMX_OVERRIDE + " ";
MoreAsserts.assertSteps(
"Steps should repack zip entries and then compress using xz.",
ImmutableList.of(
"/usr/bin/dx " + xmx + "--dex --output classes.dex.tmp.jar foo.dex.jar bar.dex.jar",
"repack classes.dex.tmp.jar in classes.dex.jar",
"rm -f classes.dex.tmp.jar",
"dex_meta dexPath:classes.dex.jar dexMetaPath:classes.dex.jar.meta",
"xz -z -4 --check=crc32 classes.dex.jar"),
steps,
createMockedExecutionContext());
verifyAll();
}
@Test
public void testCreateDxStepForDxPseudoRuleWithDexOutput() {
ImmutableList<Path> filesToDex = ImmutableList.of(
Paths.get("foo.dex.jar"), Paths.get("bar.dex.jar"));
Path outputPath = Paths.get("classes.dex");
EnumSet<DxStep.Option> dxOptions = EnumSet.noneOf(DxStep.Option.class);
Step dxStep = SmartDexingStep.createDxStepForDxPseudoRule(filesToDex, outputPath, dxOptions);
String xmx = DxStep.XMX_OVERRIDE.isEmpty() ? "" : DxStep.XMX_OVERRIDE + " ";
assertEquals(
"/usr/bin/dx " + xmx + "--dex --output classes.dex foo.dex.jar bar.dex.jar",
dxStep.getDescription(createMockedExecutionContext()));
verifyAll();
}
@Test
public void testCreateDxStepForDxPseudoRuleWithDexJarOutput() {
ImmutableList<Path> filesToDex = ImmutableList.of(
Paths.get("foo.dex.jar"), Paths.get("bar.dex.jar"));
Path outputPath = Paths.get("classes.dex.jar");
EnumSet<DxStep.Option> dxOptions = EnumSet.noneOf(DxStep.Option.class);
Step dxStep = SmartDexingStep.createDxStepForDxPseudoRule(filesToDex, outputPath, dxOptions);
String xmx = DxStep.XMX_OVERRIDE.isEmpty() ? "" : DxStep.XMX_OVERRIDE + " ";
assertEquals(
"/usr/bin/dx " + xmx + "--dex --output classes.dex.jar " +
"foo.dex.jar bar.dex.jar && dex_meta dexPath:classes.dex.jar " +
"dexMetaPath:classes.dex.jar.meta",
dxStep.getDescription(createMockedExecutionContext()));
verifyAll();
}
@Test(expected = IllegalArgumentException.class)
public void testCreateDxStepForDxPseudoRuleWithUnrecognizedOutput() {
ImmutableList<Path> filesToDex = ImmutableList.of(
Paths.get("foo.dex.jar"), Paths.get("bar.dex.jar"));
Path outputPath = Paths.get("classes.flex");
EnumSet<DxStep.Option> dxOptions = EnumSet.noneOf(DxStep.Option.class);
SmartDexingStep.createDxStepForDxPseudoRule(filesToDex, outputPath, dxOptions);
}
private ExecutionContext createMockedExecutionContext() {
AndroidPlatformTarget androidPlatformTarget = createMock(AndroidPlatformTarget.class);
expect(androidPlatformTarget.getDxExecutable()).andStubReturn(Paths.get("/usr/bin/dx"));
ProjectFilesystem projectFilesystem = createMock(ProjectFilesystem.class);
expect(projectFilesystem.resolve(anyObject(Path.class))).andAnswer(
new IAnswer<Path>() {
@Override
public Path answer() throws Throwable {
return (Path) getCurrentArguments()[0];
}
}).anyTimes();
replayAll();
return TestExecutionContext.newBuilder()
.setAndroidPlatformTargetSupplier(Suppliers.ofInstance(androidPlatformTarget))
.setProjectFilesystem(projectFilesystem)
.build();
}
}