Home | History | Annotate | Download | only in assetstudiolib
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.assetstudiolib;
     18 
     19 import java.awt.Color;
     20 import java.awt.Graphics;
     21 import java.awt.image.BufferedImage;
     22 import java.io.File;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.util.ArrayList;
     26 import java.util.HashMap;
     27 import java.util.List;
     28 import java.util.Map;
     29 
     30 import javax.imageio.ImageIO;
     31 
     32 import junit.framework.TestCase;
     33 
     34 /**
     35  * Shared test infrastructure for code generator
     36  */
     37 public abstract class GeneratorTest extends TestCase implements GraphicGeneratorContext {
     38     private static final String TEST_DATA_REL_PATH =
     39             "assetstudio/tests/src/com/android/assetstudiolib/testdata";
     40 
     41     protected void checkGraphic(int expectedFileCount, String folderName, String baseName,
     42             GraphicGenerator generator, GraphicGenerator.Options options)
     43             throws IOException {
     44         Map<String, Map<String, BufferedImage>> categoryMap =
     45                 new HashMap<String, Map<String, BufferedImage>>();
     46         options.sourceImage = GraphicGenerator.getClipartImage("android.png");
     47         generator.generate(null, categoryMap, this, options, baseName);
     48 
     49         File targetDir = getTargetDir();
     50 
     51         List<String> errors = new ArrayList<String>();
     52         int fileCount = 0;
     53         for (Map<String, BufferedImage> previews : categoryMap.values()) {
     54             for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) {
     55                 String relativePath = entry.getKey();
     56                 BufferedImage image = entry.getValue();
     57 
     58                 String path = "testdata" + File.separator + folderName + File.separator
     59                         + relativePath;
     60                 InputStream is = GeneratorTest.class.getResourceAsStream(path);
     61                 if (is == null) {
     62                     if (targetDir == null) {
     63                         fail("Did not find " + path
     64                                 + ". Set ADT_SDK_SOURCE_PATH to have it created automatically");
     65                     }
     66                     File fileName = new File(targetDir, folderName + File.separator
     67                             + relativePath);
     68                     assertFalse(fileName.exists());
     69                     if (!fileName.getParentFile().exists()) {
     70                         boolean mkdir = fileName.getParentFile().mkdirs();
     71                         assertTrue(fileName.getParent(), mkdir);
     72                     }
     73 
     74                     ImageIO.write(image, "PNG", fileName);
     75                     errors.add("File did not exist, created " + fileName.getPath());
     76                 } else {
     77                     BufferedImage goldenImage = ImageIO.read(is);
     78                     assertImageSimilar(relativePath, goldenImage, image, 5.0f);
     79                 }
     80             }
     81 
     82             fileCount += previews.values().size();
     83         }
     84         if (errors.size() > 0) {
     85             fail(errors.toString());
     86         }
     87 
     88         assertEquals("Wrong number of generated files", expectedFileCount, fileCount);
     89     }
     90 
     91     private void assertImageSimilar(String imageName, BufferedImage goldenImage,
     92             BufferedImage image, float maxPercentDifferent) throws IOException {
     93         assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getWidth()
     94                 - image.getWidth()) < 2);
     95         assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getHeight()
     96                 - image.getHeight()) < 2);
     97 
     98         assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
     99 
    100         if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
    101             BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
    102                     BufferedImage.TYPE_INT_ARGB);
    103             temp.getGraphics().drawImage(goldenImage, 0, 0, null);
    104             goldenImage = temp;
    105         }
    106         assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
    107 
    108         int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
    109         int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
    110 
    111         // Blur the images to account for the scenarios where there are pixel
    112         // differences
    113         // in where a sharp edge occurs
    114         // goldenImage = blur(goldenImage, 6);
    115         // image = blur(image, 6);
    116 
    117         int width = 3 * imageWidth;
    118         int height = imageHeight;
    119         BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    120         Graphics g = deltaImage.getGraphics();
    121 
    122         // Compute delta map
    123         long delta = 0;
    124         for (int y = 0; y < imageHeight; y++) {
    125             for (int x = 0; x < imageWidth; x++) {
    126                 int goldenRgb = goldenImage.getRGB(x, y);
    127                 int rgb = image.getRGB(x, y);
    128                 if (goldenRgb == rgb) {
    129                     deltaImage.setRGB(imageWidth + x, y, 0x00808080);
    130                     continue;
    131                 }
    132 
    133                 // If the pixels have no opacity, don't delta colors at all
    134                 if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
    135                     deltaImage.setRGB(imageWidth + x, y, 0x00808080);
    136                     continue;
    137                 }
    138 
    139                 int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
    140                 int newR = 128 + deltaR & 0xFF;
    141                 int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
    142                 int newG = 128 + deltaG & 0xFF;
    143                 int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
    144                 int newB = 128 + deltaB & 0xFF;
    145 
    146                 int avgAlpha = ((((goldenRgb & 0xFF000000) >>> 24)
    147                         + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
    148 
    149                 int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
    150                 deltaImage.setRGB(imageWidth + x, y, newRGB);
    151 
    152                 delta += Math.abs(deltaR);
    153                 delta += Math.abs(deltaG);
    154                 delta += Math.abs(deltaB);
    155             }
    156         }
    157 
    158         // 3 different colors, 256 color levels
    159         long total = imageHeight * imageWidth * 3L * 256L;
    160         float percentDifference = (float) (delta * 100 / (double) total);
    161 
    162         if (percentDifference > maxPercentDifferent) {
    163             // Expected on the left
    164             // Golden on the right
    165             g.drawImage(goldenImage, 0, 0, null);
    166             g.drawImage(image, 2 * imageWidth, 0, null);
    167 
    168             // Labels
    169             if (imageWidth > 80) {
    170                 g.setColor(Color.RED);
    171                 g.drawString("Expected", 10, 20);
    172                 g.drawString("Actual", 2 * imageWidth + 10, 20);
    173             }
    174 
    175             File output = new File(getTempDir(), "delta-"
    176                     + imageName.replace(File.separatorChar, '_'));
    177             if (output.exists()) {
    178                 output.delete();
    179             }
    180             ImageIO.write(deltaImage, "PNG", output);
    181             String message = String.format("Images differ (by %.1f%%) - see details in %s",
    182                     percentDifference, output);
    183             System.out.println(message);
    184             fail(message);
    185         }
    186 
    187         g.dispose();
    188     }
    189 
    190     protected File getTempDir() {
    191         if (System.getProperty("os.name").equals("Mac OS X")) {
    192             return new File("/tmp"); //$NON-NLS-1$
    193         }
    194 
    195         return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
    196     }
    197 
    198     @Override
    199     public BufferedImage loadImageResource(String path) {
    200         try {
    201             return GraphicGenerator.getStencilImage(path);
    202         } catch (IOException e) {
    203             fail(e.toString());
    204         }
    205 
    206         return null;
    207     }
    208 
    209     /** Get the location to write missing golden files to */
    210     protected File getTargetDir() {
    211         // Set $ADT_SDK_SOURCE_PATH to point to your git "sdk" directory
    212         String sdk = System.getenv("ADT_SDK_SOURCE_PATH");
    213         if (sdk != null) {
    214             File sdkPath = new File(sdk);
    215             if (sdkPath.exists()) {
    216                 File testData = new File(sdkPath, TEST_DATA_REL_PATH.replace('/',
    217                         File.separatorChar));
    218                 if (testData.exists()) {
    219                     return testData;
    220                 }
    221             }
    222         }
    223 
    224         return null;
    225     }
    226 }
    227