ImageComparisonSpecDefinition.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2017 wcm.io
 * %%
 * 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.
 * #L%
 */
package io.wcm.qa.glnm.galen.specs.imagecomparison;

import static com.google.common.collect.Lists.newArrayList;
import static java.util.Locale.ENGLISH;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.galenframework.validation.ValidationListener;

import io.wcm.qa.glnm.configuration.GaleniumConfiguration;
import io.wcm.qa.glnm.differences.base.Difference;
import io.wcm.qa.glnm.differences.generic.MutableDifferences;
import io.wcm.qa.glnm.galen.specs.page.GalenCorrection;
import io.wcm.qa.glnm.galen.specs.page.GalenCorrectionRect;
import io.wcm.qa.glnm.selectors.base.Selector;

/**
 * Mutable definition for image comparison specs.
 *
 * @since 4.0.0
 */
public class ImageComparisonSpecDefinition implements IcsDefinition {

  private static final String FILENAME_SUBSTITUTION = "[^A-Za-z0-9-]";

  private static final String DEFAULT_PAGE_SECTION_NAME = "Image Comparison";

  private String allowedError;
  private int allowedOffset;
  private GalenCorrectionRect corrections;
  private boolean cropIfOutside;
  private MutableDifferences differences = new MutableDifferences();
  private String elementName;
  private String foldername;
  private List<Selector> objectsToIgnore = new ArrayList<Selector>();
  private String sectionName = DEFAULT_PAGE_SECTION_NAME;
  private Selector selector;
  private boolean zeroToleranceWarning;

  /**
   * Default constructor.
   *
   * @since 5.0.0
   */
  public ImageComparisonSpecDefinition() {
    super();
  }

  /**
   * Copying constructor.
   *
   * @param source used to initialize this new instance
   * @since 5.0.0
   */
  public ImageComparisonSpecDefinition(IcsDefinition source) {
    this(source.getSelector(), source.getElementName());
    allowedError = source.getAllowedError();
    setAllowedOffset(source.getAllowedOffset());
    setCorrections(source.getCorrections());
    setCropIfOutside(source.isCropIfOutside());
    if (source instanceof ImageComparisonSpecDefinition) {
      for (Difference difference : ((ImageComparisonSpecDefinition)source).getDifferences()) {
        addDifference(difference);
      }
    }
    setObjectsToIgnore(newArrayList(source.getObjectsToIgnore()));
    setZeroToleranceWarning(source.isZeroToleranceWarning());
  }

  /**
   * <p>
   * Constructor for ImageComparisonSpecDefinition.
   * </p>
   *
   * @param selector selector for main object
   * @since 4.0.0
   */
  public ImageComparisonSpecDefinition(Selector selector) {
    this();
    setSelector(selector);
  }

  /**
   * <p>
   * Constructor for ImageComparisonSpecDefinition.
   * </p>
   *
   * @param selector selector for main object
   * @param elementName object name to use
   * @since 4.0.0
   */
  public ImageComparisonSpecDefinition(Selector selector, String elementName) {
    this(selector);
    setElementName(elementName);
  }

  /**
   * <p>
   * Add all differences.
   * </p>
   *
   * @param toBeAppended differences to be appended
   * @return true if this list changed as a result of the call
   * @since 4.0.0
   */
  public boolean addAll(Collection<? extends Difference> toBeAppended) {
    return getDifferences().addAll(toBeAppended);
  }

  /**
   * <p>
   * Appends a difference.
   * </p>
   *
   * @param difference to append
   * @since 4.0.0
   */
  public void addDifference(Difference difference) {
    getDifferences().add(difference);
  }

  /**
   * <p>
   * addObjectToIgnore.
   * </p>
   *
   * @param selectorToIgnore the area of this object will be ignored in image comparison
   * @since 4.0.0
   */
  public void addObjectToIgnore(Selector selectorToIgnore) {
    getObjectsToIgnore().add(selectorToIgnore);
  }

  /**
   * Removes all differences added to this factory.
   *
   * @since 4.0.0
   */
  public void clearDifferences() {
    getDifferences().clear();
  }

  /** {@inheritDoc} */
  @Override
  public void correctForSrollPosition(int yCorrection) {
    GalenCorrection top = GalenCorrection.adjust(-yCorrection);
    setCorrections(new GalenCorrectionRect().withTop(top));
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>allowedError</code>.</p>
   */
  @Override
  public String getAllowedError() {
    return allowedError;
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>allowedOffset</code>.</p>
   */
  @Override
  public int getAllowedOffset() {
    return allowedOffset;
  }

  /**
   * {@inheritDoc}
   *
   * <p>
   * Getter for the field <code>differences</code>.
   * </p>
   */
  @Override
  public GalenCorrectionRect getCorrections() {
    return corrections;
  }

  /**
   * <p>Getter for the field <code>differences</code>.</p>
   *
   * @return a  {@link io.wcm.qa.glnm.differences.generic.MutableDifferences} object.
   * @since 4.0.0
   */
  public MutableDifferences getDifferences() {
    return differences;
  }
  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>elementName</code>.</p>
   */
  @Override
  public String getElementName() {
    if (elementName == null) {
      return getSelector().elementName();
    }
    return elementName;
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>filename</code>.</p>
   */
  @Override
  public String getFilename() {
    return getElementName().replaceAll(FILENAME_SUBSTITUTION, "-").toLowerCase(ENGLISH) + ".png";
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>foldername</code>.</p>
   */
  @Override
  public String getFoldername() {
    if (foldername != null) {
      return foldername;
    }
    StringBuilder stringBuilder = new StringBuilder()
        .append(GaleniumConfiguration.getExpectedImagesDirectory())
        .append("/")
        .append(getDifferences().getKey().replace('.', '/'));
    return stringBuilder.toString();
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>objectsToIgnore</code>.</p>
   */
  @Override
  public List<Selector> getObjectsToIgnore() {
    return objectsToIgnore;
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>sectionName</code>.</p>
   */
  @Override
  public String getSectionName() {
    if (sectionName == null) {
      return DEFAULT_PAGE_SECTION_NAME + " for " + getElementName();
    }
    return sectionName;
  }

  /**
   * {@inheritDoc}
   *
   * <p>Getter for the field <code>selector</code>.</p>
   */
  @Override
  public Selector getSelector() {
    return selector;
  }

  /**
   * <p>
   * Getter for the field <code>validationListener</code>.
   * </p>
   *
   * @return a {@link com.galenframework.validation.ValidationListener} object.
   * @since 4.0.0
   */
  public ValidationListener getValidationListener() {
    return new IcValidationListener();
  }

  /** {@inheritDoc} */
  @Override
  public boolean isCropIfOutside() {
    return cropIfOutside;
  }

  /** {@inheritDoc} */
  @Override
  public boolean isZeroToleranceWarning() {
    return zeroToleranceWarning;
  }

  /**
   * <p>
   * setAllowedErrorPercent.
   * </p>
   *
   * @param allowedErrorPercentage zero or negative will result in zero tolerance
   * @since 4.0.0
   */
  public void setAllowedErrorPercent(Double allowedErrorPercentage) {
    if (allowedErrorPercentage > 0) {
      this.allowedError = allowedErrorPercentage + "%";
    }
    else {
      this.allowedError = StringUtils.EMPTY;
    }
  }

  /**
   * <p>
   * setAllowedErrorPixel.
   * </p>
   *
   * @param allowedErrorPixels zero or negative will result in zero tolerance
   * @since 4.0.0
   */
  public void setAllowedErrorPixel(Integer allowedErrorPixels) {
    if (allowedErrorPixels > 0) {
      this.allowedError = allowedErrorPixels + "px";
    }
    else {
      this.allowedError = StringUtils.EMPTY;
    }
  }

  /**
   * <p>
   * Setter for the field <code>allowedOffset</code>.
   * </p>
   *
   * @param allowedOffset a int.
   * @since 4.0.0
   */
  public void setAllowedOffset(int allowedOffset) {
    this.allowedOffset = allowedOffset;
  }

  /**
   * <p>
   * Setter for the field <code>corrections</code>.
   * </p>
   *
   * @param corrections a {@link com.galenframework.specs.page.CorrectionsRect} object.
   * @since 4.0.0
   */
  public void setCorrections(GalenCorrectionRect corrections) {
    this.corrections = corrections;
  }

  /**
   * <p>Setter for the field <code>cropIfOutside</code>.</p>
   *
   * @param cropIfOutside a boolean.
   * @since 4.0.0
   */
  public void setCropIfOutside(boolean cropIfOutside) {
    this.cropIfOutside = cropIfOutside;
  }

  /**
   * <p>
   * Setter for the field <code>differences</code>.
   * </p>
   *
   * @param differences a {@link io.wcm.qa.glnm.differences.generic.MutableDifferences} object.
   * @since 4.0.0
   */
  public void setDifferences(MutableDifferences differences) {
    this.differences = differences;
  }

  /**
   * <p>
   * Setter for the field <code>elementName</code>.
   * </p>
   *
   * @param elementName a {@link java.lang.String} object.
   * @since 4.0.0
   */
  public void setElementName(String elementName) {
    this.elementName = elementName;
  }

  /**
   * <p>
   * Setter for the field <code>foldername</code>.
   * </p>
   *
   * @param foldername a {@link java.lang.String} object.
   * @since 4.0.0
   */
  public void setFoldername(String foldername) {
    this.foldername = foldername;
  }

  /**
   * <p>
   * Setter for the field <code>objectsToIgnore</code>.
   * </p>
   *
   * @param objectsToIgnore a {@link java.util.List} object.
   * @since 4.0.0
   */
  public void setObjectsToIgnore(List<Selector> objectsToIgnore) {
    this.objectsToIgnore = objectsToIgnore;
  }

  /**
   * <p>
   * Setter for the field <code>sectionName</code>.
   * </p>
   *
   * @param sectionName a {@link java.lang.String} object.
   * @since 4.0.0
   */
  public void setSectionName(String sectionName) {
    this.sectionName = sectionName;
  }

  /**
   * <p>
   * Setter for the field <code>selector</code>.
   * </p>
   *
   * @param selector a {@link io.wcm.qa.glnm.selectors.base.Selector} object.
   * @since 4.0.0
   */
  public void setSelector(Selector selector) {
    this.selector = selector;
  }

  /**
   * <p>
   * Setter for the field <code>zeroToleranceWarning</code>.
   * </p>
   *
   * @param zeroToleranceWarning a boolean.
   * @since 4.0.0
   */
  public void setZeroToleranceWarning(boolean zeroToleranceWarning) {
    this.zeroToleranceWarning = zeroToleranceWarning;
  }

}