All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.tools.framework.model.testing.ConfigBaselineTestCase Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 Google Inc.
 *
 * 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 com.google.api.tools.framework.model.testing;

import com.google.api.Service;
import com.google.api.tools.framework.model.Diag;
import com.google.api.tools.framework.model.ExperimentsImpl;
import com.google.api.tools.framework.model.Model;
import com.google.api.tools.framework.model.testing.TestModelGenerator.ModelTestInfo;
import com.google.api.tools.framework.setup.StandardSetup;
import com.google.api.tools.framework.snippet.Doc;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;

/**
 * A baseline test case which prepares a model from proto and yaml config and handles printing the
 * result of a test run to the baseline.
 */
public abstract class ConfigBaselineTestCase extends BaselineTestCase {

  private static final ImmutableList EXPERIMENTS_ENABLED_FOR_TESTS =
      ImmutableList.of("use-new-visibility-derived-data");

  private final List experiments = Lists.newArrayList(EXPERIMENTS_ENABLED_FOR_TESTS);

  @Rule public TemporaryFolder tempDir = new TemporaryFolder();

  /** The test configuration. */
  protected TestConfig testConfig;

  /**
   * Determine if location information from the {@link Diag} should be printed in the baseline
   * files.
   */
  protected boolean showDiagLocation = true;

  /**
   * Map of regex Pattern to the replacement strings to be used for sanitizing/stabilizing diag
   * message strings.
   */
  protected Map diagPatternReplacements = ImmutableMap.of();

  /** List of suppression directives that should be added to the model. */
  protected List suppressionDirectives = Lists.newArrayList("versioning-config");

  /** The model on which the test runs. */
  protected Model model;

  /**
   * A text formatter which converts Any to clear text for baselines. Add any instances which should
   * be converted via {@link TextFormatForTest#registerAnyInstance(String, Message)}.
   */
  protected TextFormatForTest formatter =
      new TextFormatForTest();

  /**
   * Run test specific logic. The returned object will be printed to the baseline if not null. The
   * object can be a map from string to object, in which case the map will be decomposed for the
   * baseline output. If a {@link Doc} appears it will be pretty printed before writing it.
   */
  @Nullable
  protected abstract Object run() throws Exception;

  /**
   * Configures standard setup of processors and config aspects. Override to use a non-standard
   * setup.
   */
  protected void setupModel() {
    StandardSetup.registerStandardProcessors(model);
    StandardSetup.registerStandardConfigAspects(model);
  }

  /** Whether to suppress outputting diags to the baseline file. */
  protected boolean suppressDiagnosis() {
    return false;
  }

  /** Add the given experiment on the model. */
  public void enableExperiment(String experiment) {
    experiments.add(experiment);
  }

  /** Returns the test config. */
  protected final TestConfig getTestConfig() {
    return testConfig;
  }

  /** Run a test, using defaults for proto compilation, etc. */
  protected void test(String... baseNames) throws Exception {
    test(new TestModelGenerator(getTestDataLocator(), tempDir), baseNames);
  }

  /**
   * Run a test for the given file base name(s). Collects all .proto and .yaml files with the given
   * base name (i.e. baseName.proto or baseName.yaml), constructs model, and calls {@link #run()}.
   * Post that, prints diags and the result of the run to the baseline.
   */
  protected void test(TestModelGenerator testModelGenerator, String... baseNames) throws Exception {
    test(testModelGenerator, Arrays.asList(baseNames));
  }

  protected void test(TestModelGenerator testModelGenerator, Iterable baseNames)
      throws Exception {
    String firstBaseName = baseNames.iterator().next();
    ModelTestInfo modelTestInfo =
        testModelGenerator.buildModel(baseNames, new ExperimentsImpl(experiments));
    this.model = modelTestInfo.getModel();
    this.testConfig = modelTestInfo.getTestConfig();
    // Setup
    setupModel();

    if (suppressionDirectives != null) {
      for (String suppressionDirective : suppressionDirectives) {
        model
            .getDiagReporter()
            .getDiagSuppressor()
            .addSuppressionDirective(model, suppressionDirective, model.getConfigAspects());
      }
    }

    // Run test specific logic.
    Object result = run();

    // Output diag into baseline file.
    if (!suppressDiagnosis()) {
      for (Diag diag : model.getDiagReporter().getDiagCollector().getDiags()) {
        printDiag(diag);
      }
    }

    if (!model.getDiagReporter().getDiagCollector().hasErrors() && result != null) {
      // Output the result depending on its type.
      if (result instanceof Map) {
        @SuppressWarnings("unchecked")
        Map map = (Map) result;
        for (Map.Entry entry : map.entrySet()) {
          testOutput().printf("============== file: %s ==============%n", entry.getKey());
          testOutput().println(displayValue(entry.getValue()));
        }
      } else {
        testOutput().println(displayValue(result));
      }
    }
  }

  /**
   * Prints diag to the testOutput.
   *
   */
  protected void printDiag(final Diag diag) {
    // There are some diag messages that include strings that change over time (e.g. list of valid
    // region names), so try to filter messages to make the baseline output a bit more stable.
    Diag diagToPrint = diag;
    if (!diagPatternReplacements.isEmpty()) {
      String message = diagToPrint.getMessage();
      for (Map.Entry entry : diagPatternReplacements.entrySet()) {
        message = message.replaceAll(entry.getKey(), entry.getValue());
      }
      if (!message.equals(diagToPrint.getMessage())) {
        diagToPrint = Diag.create(diag.getLocation(), "%s", diag.getKind(), message);
      }
    }
    String message = DiagUtils.getDiagMessage(diagToPrint);
    if (showDiagLocation) {
      testOutput()
          .printf(
              String.format(
                      "%s: %s: %s",
                      diagToPrint.getKind().toString(),
                      getLocationWithoutFullPath(diagToPrint),
                      message)
                  + "%n");
    } else {
      testOutput().printf("%s: %s%n", diagToPrint.getKind(), message);
    }
  }

  private String getLocationWithoutFullPath(final Diag diag) {
    String location = diag.getLocation().getDisplayString();
    int firstSlashIndex = location.indexOf("/");
    int lastSlashIndex = location.lastIndexOf("/");
    if (firstSlashIndex != -1) {
      String toReplace = location.substring(firstSlashIndex, lastSlashIndex + 1);
      location = location.replace(toReplace, "");
    }
    return location;
  }

  /**
   * Set a filter for warnings based on regular expression for aspect name. Only warnings containing
   * the aspect name pattern are produced.
   */
  public void setWarningFilter(@Nullable String aspectNamePattern) {
    // Add as a pattern to the model.
    model
        .getDiagReporter()
        .getDiagSuppressor()
        .addPattern(model, String.format("^(?!.*(%s)).*", aspectNamePattern));
  }

  /** Fetches content from various values for a content source (File, Doc, etc.) */
  private String displayValue(Object value) throws IOException {
    if (value instanceof Doc) {
      return ((Doc) value).prettyPrint(100);
    } else if (value instanceof File) {
      return Files.asCharSource((File) value, StandardCharsets.UTF_8).read();
    } else if (value instanceof MessageOrBuilder) {
      // Convert proto to text format, considering any instances.
      return formatter.printToString((MessageOrBuilder) value);
    } else {
      return value.toString();
    }
  }

  /**
   * Takes a service configuration and converts it into a string suitable for baseline tests. This
   * sanitizes the config scrubbing empty configuration sections, which are semantically irrelevant.
   */
  public String toBaselineString(Service config) {
    Service.Builder builder = ServiceConfigTestingUtil.clearIrrelevantData(config.toBuilder());
    return formatter.printToString(builder);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy