/*
* Copyright (C) 2016 Patrick Favre-Bulle
*
* 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 at.favre.tools.dconvert.test;
import at.favre.tools.dconvert.arg.*;
import at.favre.tools.dconvert.converters.ConverterCallback;
import at.favre.tools.dconvert.converters.IPlatformConverter;
import at.favre.tools.dconvert.converters.Result;
import at.favre.tools.dconvert.converters.descriptors.PostfixDescriptor;
import at.favre.tools.dconvert.util.ImageUtil;
import at.favre.tools.dconvert.util.MiscUtil;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.*;
/**
* Unit tests for {@link at.favre.tools.dconvert.converters.IPlatformConverter}
*/
public abstract class AConverterTest {
static final float DEFAULT_SCALE = 3;
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
File defaultDst;
File defaultSrc;
IPlatformConverter converter;
ConverterCallback defaultCallback;
@BeforeClass
public static void oneTimeSetUp() {
}
@AfterClass
public static void oneTimeTearDown() {
}
@Before
public void setUp() throws IOException {
defaultSrc = temporaryFolder.newFolder("converter-test", "src");
defaultDst = temporaryFolder.newFolder("converter-test", "out");
converter = getType().getConverter();
}
@After
public void tearDown() {
defaultDst = defaultSrc = null;
converter = null;
defaultCallback = null;
}
protected abstract EPlatform getType();
protected abstract void checkOutDir(File dstDir, Arguments arguments, List<File> files, EPlatform type) throws IOException;
@Test
public void testSinglePng() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example1_alpha_144.png");
defaultTest(files);
}
@Test
public void testSingleJpeg() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "jpg_example_1920.jpg");
defaultTest(files);
}
@Test
public void testSingleGif() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "gif_example_640.gif");
defaultTest(files);
}
@Test
public void testSingleBmp() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "bmp_example_256.bmp");
defaultTest(files);
}
@Test
public void testSingleTiff() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "tiff_example_256.tif");
defaultTest(files);
}
@Test
public void testSinglePsd() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "psd_example_827.psd");
defaultTest(files);
}
@Test
public void testSingleSvg() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "svg_example_512.svg");
defaultTest(files);
}
@Test
public void testRoundMode() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example1_alpha_144.png");
test(new Arguments.Builder(defaultSrc, 2.33f).dstFolder(defaultDst).scaleRoundingStragy(RoundingHandler.Strategy.FLOOR).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testScaleWidthInDp() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, 24).dstFolder(defaultDst).scaleMode(EScaleMode.DP_WIDTH).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testMultipleScaleWidthInDp() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example1_alpha_144.png", "png_example4_500.png", "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, 48).dstFolder(defaultDst).scaleMode(EScaleMode.DP_WIDTH).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testScaleHeightInDp() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, 128).dstFolder(defaultDst).scaleMode(EScaleMode.DP_HEIGHT).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testMultipleScaleHeightInDp() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example1_alpha_144.png", "png_example4_500.png", "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, 48).dstFolder(defaultDst).scaleMode(EScaleMode.DP_HEIGHT).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testMultiplePng() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example1_alpha_144.png", "png_example2_alpha_144.png", "png_example3_alpha_128.png");
defaultTest(files);
}
@Test
public void testPngAndJpegKeepCompressions() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example2_alpha_144.png", "jpg_example_1920.jpg");
defaultTest(files);
}
@Test
public void testMixedCompressionsShouldBeMostlyPng() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example2_alpha_144.png", "gif_example_640.gif", "psd_example_827.psd", "bmp_example_256.bmp", "jpg_example_1920.jpg");
defaultTest(files);
}
@Test
public void testMixedCompressionsShouldKeepCompressions() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example2_alpha_144.png", "gif_example_640.gif", "tiff_example_256.tif", "bmp_example_256.bmp", "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, DEFAULT_SCALE).compression(EOutputCompressionMode.SAME_AS_INPUT_STRICT, 0.5f)
.dstFolder(defaultDst).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testMixedCompressionsShouldCreateJpg() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example2_alpha_144.png", "gif_example_640.gif", "psd_example_827.psd", "bmp_example_256.bmp", "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, DEFAULT_SCALE).compression(EOutputCompressionMode.AS_JPG, 0.0f)
.dstFolder(defaultDst).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testMixedCompressionsShouldCreateJpgAndPng() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example2_alpha_144.png", "gif_example_640.gif", "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, DEFAULT_SCALE).compression(EOutputCompressionMode.AS_JPG_AND_PNG, 0.0f)
.dstFolder(defaultDst).platform(Collections.singleton(getType())).build(), files);
}
@Test
public void testDryRun() throws Exception {
List<File> files = copyToTestPath(defaultSrc, "png_example2_alpha_144.png", "gif_example_640.gif", "jpg_example_1920.jpg");
test(new Arguments.Builder(defaultSrc, DEFAULT_SCALE).compression(EOutputCompressionMode.AS_JPG_AND_PNG, 0.0f)
.dstFolder(defaultDst).platform(Collections.singleton(getType())).dryRun(true).build(), files);
}
protected void defaultTest(List<File> files) throws Exception {
test(new Arguments.Builder(defaultSrc, DEFAULT_SCALE).compression(EOutputCompressionMode.SAME_AS_INPUT_PREF_PNG, 0.5f)
.dstFolder(defaultDst).platform(Collections.singleton(getType())).build(), files);
}
protected void test(Arguments arg, List<File> files) throws Exception {
for (File fileToProcess : arg.filesToProcess) {
Result result = converter.convert(fileToProcess, arg);
assertNull("should be no exception: " + result.exception, result.exception);
}
if (arg.dryRun) {
assertEquals("destination folder should be empty", 0, arg.dst.list().length);
} else {
checkOutDir(arg.dst, arg, files, getType());
}
}
public static List<File> copyToTestPath(File defaultSrc, String... resourceNames) throws Exception {
List<File> copiedFiles = new ArrayList<>();
for (String resourceName : resourceNames) {
File dstFile = new File(defaultSrc, resourceName);
Files.copy(new File(AConverterTest.class.getClassLoader().getResource(resourceName).getFile()).toPath(), dstFile.toPath());
copiedFiles.add(dstFile);
}
return copiedFiles;
}
public static void checkMultiPlatformConvert(File dst, Arguments arg, List<File> files) throws IOException {
AndroidConverterTest.checkOutDirAndroid(new File(dst, "android"), arg, files);
IOSConverterTest.checkOutDirIos(new File(dst, "ios"), arg, files);
WindowsConverterTest.checkOutDirWindows(new File(dst, "windows"), arg, files);
WebConverterTest.checkOutDirWeb(new File(dst, "web"), arg, files);
}
protected static Map<File, Dimension> createDimensionMap(List<File> files) throws IOException {
Map<File, Dimension> map = new HashMap<>();
for (File file : files) {
map.put(file, ImageUtil.getImageDimension(file));
}
return map;
}
protected static Dimension getScaledDimension(File srcFile, Arguments args, Dimension dimension, float scale, boolean isNinePatch) throws IOException {
double baseWidth;
double baseHeight;
if (args.scaleMode == EScaleMode.DP_WIDTH) {
Dimension srcDimension = ImageUtil.getImageDimension(srcFile);
float scaleFactor = args.scale / (float) srcDimension.width;
baseWidth = (int) args.round(args.scale);
baseHeight = (int) args.round(scaleFactor * (float) srcDimension.height);
} else if (args.scaleMode == EScaleMode.DP_HEIGHT) {
Dimension srcDimension = ImageUtil.getImageDimension(srcFile);
float scaleFactor = args.scale / (float) srcDimension.height;
baseWidth = (int) args.round(scaleFactor * (float) srcDimension.width);
baseHeight = (int) args.round(args.scale);
} else {
baseWidth = (double) dimension.width / args.scale;
baseHeight = (double) dimension.height / args.scale;
}
if (isNinePatch) {
return new Dimension((int) args.round(((baseWidth + 1) * scale) + 2),
(int) args.round(((baseHeight + 1) * scale) + 2));
} else {
return new Dimension((int) args.round(baseWidth * scale),
(int) args.round(baseHeight * scale));
}
}
protected static class ImageInfo {
public final File srcFile;
public final String targetFileName;
public final float scale;
public boolean found = false;
public ImageInfo(File srcFile, String targetFileName, float scale) {
this.srcFile = srcFile;
this.targetFileName = targetFileName;
this.scale = scale;
}
}
public static void checkOutDirPostfixDescr(File dstRootDir, Arguments arguments, List<File> files, List<PostfixDescriptor> densityDescriptors) throws IOException {
Map<File, Dimension> dimensionMap = createDimensionMap(files);
if (!files.isEmpty()) {
assertTrue("src files and dst folder count should match", dstRootDir.listFiles().length >= files.size());
List<ImageInfo> expectedFiles = new ArrayList<>();
for (File srcImageFile : files) {
for (PostfixDescriptor descriptor : densityDescriptors) {
expectedFiles.addAll(Arguments.getOutCompressionForType(arguments.outputCompressionMode, Arguments.getImageType(srcImageFile)).stream().map(compression -> new ImageInfo(srcImageFile, MiscUtil.getFileNameWithoutExtension(srcImageFile) + descriptor.postFix + "." + compression.extension, descriptor.scale)).collect(Collectors.toList()));
}
}
for (File imageFile : dstRootDir.listFiles()) {
for (ImageInfo expectedFile : expectedFiles) {
if (expectedFile.targetFileName.equals(imageFile.getName())) {
expectedFile.found = true;
Dimension expectedDimension = getScaledDimension(expectedFile.srcFile, arguments, dimensionMap.get(expectedFile.srcFile), expectedFile.scale, false);
assertEquals("dimensions should match", expectedDimension, ImageUtil.getImageDimension(imageFile));
}
}
}
for (ImageInfo expectedFile : expectedFiles) {
assertTrue(expectedFile.targetFileName + " expected in folder " + dstRootDir, expectedFile.found);
}
System.out.println("found " + expectedFiles.size() + " files in " + dstRootDir);
} else {
assertTrue(dstRootDir.list() == null || dstRootDir.list().length == 0);
}
}
}