1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.qa.glnm.galen.specs.imagecomparison;
21
22 import static io.wcm.qa.glnm.configuration.GaleniumConfiguration.getActualImagesDirectory;
23 import static io.wcm.qa.glnm.configuration.GaleniumConfiguration.getExpectedImagesDirectory;
24 import static io.wcm.qa.glnm.util.FileHandlingUtil.constructRelativePath;
25
26 import java.awt.image.BufferedImage;
27 import java.io.File;
28 import java.io.IOException;
29 import java.util.Collection;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import javax.imageio.ImageIO;
36
37 import org.apache.commons.io.FileUtils;
38 import org.apache.commons.io.FilenameUtils;
39 import org.apache.commons.lang3.StringUtils;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import com.galenframework.parser.SyntaxException;
44 import com.galenframework.speclang2.specs.SpecReader;
45 import com.galenframework.specs.Spec;
46 import com.galenframework.validation.ImageComparison;
47 import com.galenframework.validation.ValidationError;
48 import com.galenframework.validation.ValidationResult;
49
50 import io.wcm.qa.glnm.exceptions.GaleniumException;
51 import io.wcm.qa.glnm.selectors.base.Selector;
52 import io.wcm.qa.glnm.util.FileHandlingUtil;
53
54
55
56
57 final class IcUtil {
58
59 private static final BufferedImage DUMMY_IMAGE = new BufferedImage(20, 20, BufferedImage.TYPE_3BYTE_BGR);
60
61 private static final String DUMMY_IMAGE_FORMAT = "png";
62
63 private static final Logger LOG = LoggerFactory.getLogger(IcUtil.class);
64
65 private static final String REGEX_IMAGE_FILENAME = ".*image file ([^,]*\\.png).*";
66 static final Pattern REGEX_PATTERN_IMAGE_FILENAME = Pattern.compile(REGEX_IMAGE_FILENAME);
67
68 private IcUtil() {
69
70 }
71
72 private static String getImageComparisonSpecText(String folder, String fileName, String error, int offset, List<Selector> toIgnore) {
73 StringBuilder specText = new StringBuilder()
74
75 .append("image file ")
76
77 .append(getImageOrDummySamplePath(folder, fileName));
78
79
80 if (StringUtils.isNotBlank(error)) {
81 specText.append(", error ")
82 .append(error);
83 }
84 if (offset > 0) {
85 specText.append(", analyze-offset ");
86 specText.append(offset);
87 }
88 if (!toIgnore.isEmpty()) {
89 List<Selector> objects = toIgnore;
90 specText.append(", ignore-objects ");
91 if (objects.size() == 1) {
92 specText.append(objects.get(0));
93 }
94 else {
95 specText.append("[");
96 Collection<String> elementNames = new HashSet<String>();
97
98 for (Selector object : objects) {
99 elementNames.add(object.elementName());
100 }
101
102 specText.append(StringUtils.join(elementNames, ", "));
103 specText.append("]");
104 }
105 }
106
107 return specText.toString();
108 }
109
110 private static String getImageOrDummySamplePath(String folder, String fileName) {
111 String fullFilePath;
112
113
114 if (StringUtils.isNotBlank(folder)) {
115 fullFilePath = FilenameUtils.concat(folder, fileName);
116 }
117 else {
118
119 fullFilePath = fileName;
120 }
121
122 createDummyIfSampleDoesNotExist(fullFilePath);
123
124 return fullFilePath;
125 }
126
127 private static File getOriginalFilteredImage(ValidationResult result) {
128 ImageComparison imageComparison = getImageComparison(result);
129 if (imageComparison == null) {
130 return null;
131 }
132 File actualImage = imageComparison.getOriginalFilteredImage();
133 if (actualImage == null) {
134 LOG.debug("could not find sampled image in image comparison.");
135 }
136
137 return actualImage;
138 }
139
140 private static File getSampleTargetFile(Spec spec) {
141 String targetPath = getTargetPathFrom(spec);
142 File imageFile = new File(targetPath);
143 FileHandlingUtil.ensureParent(imageFile);
144 return imageFile;
145 }
146
147 private static String getTargetPathFrom(Spec spec) {
148 File rootDirectory = new File(getExpectedImagesDirectory());
149 String imagePathFromSpec = getImagePathFrom(spec);
150 String relativeImagePath = constructRelativePath(rootDirectory, new File(imagePathFromSpec));
151 return getActualImagesDirectory() + File.separator + relativeImagePath;
152 }
153
154 private static boolean isExpectedImageSampleMissing(String fullFilePath) {
155 return !new File(fullFilePath).isFile();
156 }
157
158 private static File writeDummySample(File targetFile) {
159 try {
160 if (LOG.isTraceEnabled()) {
161 LOG.trace("begin writing dummy image '" + targetFile);
162 }
163 FileHandlingUtil.ensureParent(targetFile);
164 if (ImageIO.write(DUMMY_IMAGE, DUMMY_IMAGE_FORMAT, targetFile)) {
165 if (LOG.isDebugEnabled()) {
166 LOG.debug("done writing dummy image '" + targetFile);
167 }
168 }
169 else if (LOG.isInfoEnabled()) {
170 LOG.info("could not write dummy image '" + targetFile);
171 }
172 return targetFile;
173 }
174 catch (IOException ex) {
175 throw new GaleniumException("could not write dummy image.", ex);
176 }
177 }
178
179 static void createDummyIfSampleDoesNotExist(String fullFilePath) {
180 if (IcUtil.isExpectedImageSampleMissing(fullFilePath)) {
181 if (LOG.isInfoEnabled()) {
182 LOG.info("Cannot find sample. Substituting dummy for '" + fullFilePath + "'");
183 }
184
185
186 File targetFile = new File(fullFilePath);
187
188 writeDummySample(targetFile);
189 }
190 }
191
192 static ImageComparison getImageComparison(ValidationResult result) {
193 ValidationError error = result.getError();
194 if (error == null) {
195 LOG.debug("could not find error in validation result.");
196 return null;
197 }
198
199 ImageComparison imageComparison = error.getImageComparison();
200 if (imageComparison == null) {
201 LOG.debug("could not find image comparison in validation error.");
202 return null;
203 }
204
205 return imageComparison;
206 }
207
208 static String getImageComparisonSpecText(IcsDefinition def) {
209 return IcUtil.getImageComparisonSpecText(
210 def.getFoldername(),
211 def.getFilename(),
212 def.getAllowedError(),
213 def.getAllowedOffset(),
214 def.getObjectsToIgnore());
215 }
216
217 static String getImagePathFrom(Spec spec) {
218 Matcher matcher = REGEX_PATTERN_IMAGE_FILENAME.matcher(spec.toText());
219 if (matcher.matches() && matcher.groupCount() >= 1) {
220 return matcher.group(1);
221 }
222 return "";
223 }
224
225 static File getSampleSourceFile(Spec spec, ValidationResult result) {
226 File imageFile = getOriginalFilteredImage(result);
227 if (imageFile != null) {
228 if (LOG.isDebugEnabled()) {
229 LOG.debug("sample source file: " + imageFile.getPath());
230 }
231 return imageFile;
232 }
233 String imagePath = getImagePathFrom(spec);
234 if (StringUtils.isBlank(imagePath)) {
235 if (LOG.isWarnEnabled()) {
236 LOG.warn("could not extract image name from: " + spec.toText());
237 }
238 return null;
239 }
240 if (LOG.isDebugEnabled()) {
241 LOG.debug("sample source path: " + imagePath);
242 }
243 return new File(imagePath);
244 }
245
246 static Spec getSpecForText(String specText) {
247 try {
248 return new SpecReader().read(specText);
249 }
250 catch (IllegalArgumentException | SyntaxException ex) {
251 String msg = "when parsing spec text: '" + specText + "'";
252 LOG.error(msg);
253 throw new GaleniumException(msg, ex);
254 }
255 }
256
257 static String getZeroToleranceImageComparisonSpecText(IcsDefinition def) {
258 return getImageComparisonSpecText(
259 def.getFoldername(),
260 def.getFilename(),
261 "",
262 0,
263 def.getObjectsToIgnore());
264 }
265
266 static boolean isImageComparisonSpec(Spec spec) {
267 return StringUtils.contains(spec.toText(), "image file ");
268 }
269
270 static void saveSample(String objectName, Spec spec, ValidationResult result) {
271 if (LOG.isDebugEnabled()) {
272 LOG.debug("checking for image file: " + spec.toText() + " (with regex: " + REGEX_PATTERN_IMAGE_FILENAME.pattern() + ")");
273 }
274 File source = getSampleSourceFile(spec, result);
275 if (source == null) {
276 if (LOG.isDebugEnabled()) {
277 LOG.debug("did not find source file: " + objectName);
278 }
279 return;
280 }
281
282 File target = getSampleTargetFile(spec);
283 if (LOG.isTraceEnabled()) {
284 LOG.trace("begin copying image '" + source + "' -> '" + target + "'");
285 }
286 try {
287 FileUtils.copyFile(source, target);
288 }
289 catch (GaleniumException | IOException ex) {
290 String msg = "could not write image: " + target;
291 LOG.error(msg, ex);
292 }
293 if (LOG.isTraceEnabled()) {
294 LOG.trace("done copying image '" + source + "' -> '" + target + "'");
295 }
296 }
297
298 }