001 /*
002 * Created on Jun 7, 2007
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005 * the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011 * specific language governing permissions and limitations under the License.
012 *
013 * Copyright @2007-2009 the original author or authors.
014 */
015 package org.fest.assertions;
016
017 import static java.lang.String.valueOf;
018 import static org.fest.assertions.ErrorMessages.unexpectedEqual;
019 import static org.fest.assertions.ErrorMessages.unexpectedNotEqual;
020 import static org.fest.assertions.Formatting.inBrackets;
021 import static org.fest.assertions.Threshold.threshold;
022 import static org.fest.util.Objects.areEqual;
023 import static org.fest.util.Strings.concat;
024 import static org.fest.util.Strings.quote;
025
026 import java.awt.Dimension;
027 import java.awt.image.BufferedImage;
028 import java.io.File;
029 import java.io.IOException;
030
031 /**
032 * Understands assertion methods for images. To create a new instance of this class use the method
033 * <code>{@link Assertions#assertThat(BufferedImage)}</code>.
034 *
035 * @author Yvonne Wang
036 * @author Alex Ruiz
037 * @author Ansgar Konermann
038 */
039 public class ImageAssert extends GenericAssert<BufferedImage> {
040
041 private static final Threshold ZERO_THRESHOLD = threshold(0);
042
043 private static ImageReader imageReader = new ImageReader();
044
045 /**
046 * Reads the image in the specified path.
047 * @param imageFilePath the path of the image to read.
048 * @return the read image.
049 * @throws NullPointerException if the given path is <code>null</code>.
050 * @throws IllegalArgumentException if the given path does not belong to a file.
051 * @throws IOException if any I/O error occurred while reading the image.
052 */
053 public static BufferedImage read(String imageFilePath) throws IOException {
054 if (imageFilePath == null) throw new NullPointerException("The path of the image to read should not be null");
055 File imageFile = new File(imageFilePath);
056 if (!imageFile.isFile())
057 throw new IllegalArgumentException(concat("The path ", quote(imageFilePath), " does not belong to a file"));
058 return imageReader.read(imageFile);
059 }
060
061 /**
062 * Creates a new </code>{@link ImageAssert}</code>.
063 * @param actual the target to verify.
064 */
065 protected ImageAssert(BufferedImage actual) {
066 super(actual);
067 }
068
069 /** {@inheritDoc} */
070 public ImageAssert as(String description) {
071 description(description);
072 return this;
073 }
074
075 /** {@inheritDoc} */
076 public ImageAssert describedAs(String description) {
077 return as(description);
078 }
079
080 /** {@inheritDoc} */
081 public ImageAssert as(Description description) {
082 description(description);
083 return this;
084 }
085
086 /** {@inheritDoc} */
087 public ImageAssert describedAs(Description description) {
088 return as(description);
089 }
090
091 /**
092 * Verifies that the actual image satisfies the given condition.
093 * @param condition the given condition.
094 * @return this assertion object.
095 * @throws NullPointerException if the given condition is <code>null</code>.
096 * @throws AssertionError if the actual image does not satisfy the given condition.
097 * @see #is(Condition)
098 */
099 public ImageAssert satisfies(Condition<BufferedImage> condition) {
100 assertSatisfies(condition);
101 return this;
102 }
103
104 /**
105 * Verifies that the actual image does not satisfy the given condition.
106 * @param condition the given condition.
107 * @return this assertion object.
108 * @throws NullPointerException if the given condition is <code>null</code>.
109 * @throws AssertionError if the actual image satisfies the given condition.
110 * @see #isNot(Condition)
111 */
112 public ImageAssert doesNotSatisfy(Condition<BufferedImage> condition) {
113 assertDoesNotSatisfy(condition);
114 return this;
115 }
116
117 /**
118 * Alias for <code>{@link #satisfies(Condition)}</code>.
119 * @param condition the given condition.
120 * @return this assertion object.
121 * @throws NullPointerException if the given condition is <code>null</code>.
122 * @throws AssertionError if the actual image does not satisfy the given condition.
123 * @since 1.2
124 */
125 public ImageAssert is(Condition<BufferedImage> condition) {
126 assertIs(condition);
127 return this;
128 }
129
130 /**
131 * Alias for <code>{@link #doesNotSatisfy(Condition)}</code>.
132 * @param condition the given condition.
133 * @return this assertion object.
134 * @throws NullPointerException if the given condition is <code>null</code>.
135 * @throws AssertionError if the actual image satisfies the given condition.
136 * @since 1.2
137 */
138 public ImageAssert isNot(Condition<BufferedImage> condition) {
139 assertIsNot(condition);
140 return this;
141 }
142
143 /**
144 * Verifies that the actual image is equal to the given one. Two images are equal if they have the same size and the
145 * pixels at the same coordinates have the same color.
146 * @param expected the given image to compare the actual image to.
147 * @return this assertion object.
148 * @throws AssertionError if the actual image is not equal to the given one.
149 */
150 public ImageAssert isEqualTo(BufferedImage expected) {
151 return isEqualTo(expected, ZERO_THRESHOLD);
152 }
153
154 /**
155 * Verifies that the actual image is equal to the given one. Two images are equal if:
156 * <ol>
157 * <li>they have the same size</li>
158 * <li>the difference between the RGB values of the color of each pixel is less than or equal to the given
159 * threshold</li>
160 * </ol>
161 * @param expected the given image to compare the actual image to.
162 * @param threshold the threshold to use to decide if the color of two pixels are similar: two pixels that are
163 * identical to the human eye may still have slightly different color values. For example, by using a threshold of 1
164 * we can indicate that a blue value of 60 is similar to a blue value of 61.
165 * @return this assertion object.
166 * @throws AssertionError if the actual image is not equal to the given one.
167 * @since 1.1
168 */
169 public ImageAssert isEqualTo(BufferedImage expected, Threshold threshold) {
170 if (areEqual(actual, expected)) return this;
171 failIfNull(expected);
172 failIfNotEqual(sizeOf(actual), sizeOf(expected));
173 failIfNotEqualColor(expected, threshold);
174 return this;
175 }
176
177 private void failIfNull(BufferedImage expected) {
178 if (expected != null) return;
179 failIfCustomMessageIsSet();
180 fail(unexpectedNotEqual(actual, null));
181 }
182
183 private void failIfNotEqual(Dimension a, Dimension e) {
184 if (areEqual(a, e)) return;
185 failIfCustomMessageIsSet();
186 fail(concat("image size, expected:", inBrackets(e), " but was:", inBrackets(a)));
187 }
188
189 private void failIfNotEqualColor(BufferedImage expected, Threshold threshold) {
190 int w = actual.getWidth();
191 int h = actual.getHeight();
192 for (int x = 0; x < w; x++)
193 for (int y = 0; y < h; y++)
194 failIfNotEqual(new RGBColor(actual.getRGB(x, y)), new RGBColor(expected.getRGB(x, y)), threshold, x, y);
195 }
196
197 private void failIfNotEqual(RGBColor a, RGBColor e, Threshold threshold, int x, int y) {
198 if (a.isEqualTo(e, threshold.value())) return;
199 failIfCustomMessageIsSet();
200 fail(concat("expected:", inBrackets(a), " but was:", inBrackets(e), " at pixel [", valueOf(x), ",", valueOf(y), "]"));
201 }
202
203 /**
204 * Verifies that the actual image is not equal to the given one. Two images are equal if they have the same size and
205 * the pixels at the same coordinates have the same color.
206 * @param image the given image to compare the actual image to.
207 * @return this assertion object.
208 * @throws AssertionError if the actual image is equal to the given one.
209 */
210 public ImageAssert isNotEqualTo(BufferedImage image) {
211 if (areEqual(actual, image)) {
212 failIfCustomMessageIsSet();
213 throw failure(unexpectedEqual(actual, image));
214 }
215 if (image == null) return this;
216 if (areEqual(sizeOf(actual), sizeOf(image)) && hasEqualColor(image)) {
217 failIfCustomMessageIsSet();
218 throw failure("images are equal");
219 }
220 return this;
221 }
222
223 private static Dimension sizeOf(BufferedImage image) {
224 return new Dimension(image.getWidth(), image.getHeight());
225 }
226
227 private boolean hasEqualColor(BufferedImage expected) {
228 int w = actual.getWidth();
229 int h = actual.getHeight();
230 for (int x = 0; x < w; x++)
231 for (int y = 0; y < h; y++)
232 if (actual.getRGB(x, y) != expected.getRGB(x, y)) return false;
233 return true;
234 }
235
236 /**
237 * Verifies that the actual image is not <code>null</code>.
238 * @return this assertion object.
239 * @throws AssertionError if the actual image is <code>null</code>.
240 */
241 public ImageAssert isNotNull() {
242 assertNotNull();
243 return this;
244 }
245
246 /**
247 * Verifies that the actual image is not the same as the given one.
248 * @param expected the given image to compare the actual image to.
249 * @return this assertion object.
250 * @throws AssertionError if the actual image is the same as the given one.
251 */
252 public ImageAssert isNotSameAs(BufferedImage expected) {
253 assertNotSameAs(expected);
254 return this;
255 }
256
257 /**
258 * Verifies that the actual image is the same as the given one.
259 * @param expected the given image to compare the actual image to.
260 * @return this assertion object.
261 * @throws AssertionError if the actual image is not the same as the given one.
262 */
263 public ImageAssert isSameAs(BufferedImage expected) {
264 assertSameAs(expected);
265 return this;
266 }
267
268 /**
269 * Verifies that the size of the actual image is equal to the given one.
270 * @param expected the expected size of the actual image.
271 * @return this assertion object.
272 * @throws AssertionError if the actual image is <code>null</code>.
273 * @throws NullPointerException if the given size is <code>null</code>.
274 * @throws AssertionError if the size of the actual image is not equal to the given one.
275 */
276 public ImageAssert hasSize(Dimension expected) {
277 isNotNull();
278 if (expected == null)
279 throw new NullPointerException(formattedErrorMessage("The size to compare to should not be null"));
280 Dimension actualDimension = new Dimension(actual.getWidth(), actual.getHeight());
281 Fail.failIfNotEqual(customErrorMessage(), rawDescription(), actualDimension, expected);
282 return this;
283 }
284
285 /** {@inheritDoc} */
286 public ImageAssert overridingErrorMessage(String message) {
287 replaceDefaultErrorMessagesWith(message);
288 return this;
289 }
290
291 static void imageReader(ImageReader newImageReader) {
292 imageReader = newImageReader;
293 }
294 }