// Copyright 2019 The Nomulus Authors. All Rights Reserved. // // 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 google.registry.webdriver; import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap; import static com.google.common.hash.Hashing.sha256; import static com.google.common.io.MoreFiles.asByteSource; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.common.collect.Maps; import com.google.common.io.MoreFiles; import google.registry.tools.params.ParameterFactory; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.stream.Stream; /** A tool to find the new golden image by selecting the screenshot which appears the most times. */ @Parameters( separators = " =", commandDescription = "Find the new golden images from the given screenshots.") public class GoldenImageFinder { @Parameter( names = {"--screenshots_for_goldens_dir"}, description = "Directory to store screenshots generated as candidate of golden images.", required = true) private Path screenshotsForGoldensDir; @Parameter( names = {"--new_goldens_dir"}, description = "Directory to store the new golden images selected by this application.", required = true) private Path newGoldensDir; @Parameter( names = {"--existing_goldens_dir"}, description = "Directory to store the existing golden images.", required = true) private Path existingGoldensDir; @Parameter( names = {"--override_existing_goldens"}, description = "If set to true, the new golden images are copied to override the existing ones.") private boolean overrideExistingGoldens = false; private void run() { try (Stream allScreenshots = Files.find(screenshotsForGoldensDir, 3, (file, attr) -> attr.isRegularFile())) { if (newGoldensDir.toFile().isDirectory()) { MoreFiles.deleteRecursively(newGoldensDir); } newGoldensDir.toFile().mkdirs(); allScreenshots .collect( toImmutableSetMultimap( imagePath -> imagePath.toFile().getName(), imagePath -> imagePath)) .asMap() .forEach( (imageFileName, screenshots) -> { Map occurrenceByHash = Maps.newHashMap(); int currNumOccurrence = 0; Path currImagePath = null; for (Path screenshot : screenshots) { String imageHash; try { imageHash = asByteSource(screenshot).hash(sha256()).toString(); } catch (IOException e) { throw new UncheckedIOException(e); } int numOccurrence = occurrenceByHash.getOrDefault(imageHash, 0); occurrenceByHash.put(imageHash, numOccurrence + 1); if (occurrenceByHash.get(imageHash) > currNumOccurrence) { currNumOccurrence = occurrenceByHash.get(imageHash); currImagePath = screenshot; } } try { Files.copy(currImagePath, newGoldensDir.resolve(imageFileName)); if (overrideExistingGoldens) { Files.copy(currImagePath, existingGoldensDir.resolve(imageFileName)); } } catch (IOException e) { throw new UncheckedIOException(e); } }); } catch (IOException e) { throw new UncheckedIOException(e); } } public static void main(String[] args) { GoldenImageFinder finder = new GoldenImageFinder(); JCommander jCommander = new JCommander(finder); jCommander.addConverterFactory(new ParameterFactory()); jCommander.parse(args); finder.run(); } }