• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
checkGraphic(int expectedFileCount, String folderName, String baseName, GraphicGenerator generator, GraphicGenerator.Options options)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 
assertImageSimilar(String imageName, BufferedImage goldenImage, BufferedImage image, float maxPercentDifferent)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 
getTempDir()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 
loadImageResource(String path)198     public BufferedImage loadImageResource(String path) {
199         try {
200             return GraphicGenerator.getStencilImage(path);
201         } catch (IOException e) {
202             fail(e.toString());
203         }
204 
205         return null;
206     }
207 
208     /** Get the location to write missing golden files to */
getTargetDir()209     protected File getTargetDir() {
210         // Set $ADT_SDK_SOURCE_PATH to point to your git "sdk" directory
211         String sdk = System.getenv("ADT_SDK_SOURCE_PATH");
212         if (sdk != null) {
213             File sdkPath = new File(sdk);
214             if (sdkPath.exists()) {
215                 File testData = new File(sdkPath, TEST_DATA_REL_PATH.replace('/',
216                         File.separatorChar));
217                 if (testData.exists()) {
218                     return testData;
219                 }
220             }
221         }
222 
223         return null;
224     }
225 }
226