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

org.apache.calcite.util.TestUtil Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you 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 org.apache.calcite.util;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableSortedSet;

import org.junit.jupiter.api.Assertions;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;
import java.util.SortedSet;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Static utilities for JUnit tests.
 */
public abstract class TestUtil {
  //~ Static fields/initializers ---------------------------------------------

  private static final Pattern LINE_BREAK_PATTERN =
      Pattern.compile("\r\n|\r|\n");

  private static final Pattern TAB_PATTERN = Pattern.compile("\t");

  private static final String LINE_BREAK =
      "\\\\n\"" + Util.LINE_SEPARATOR + " + \"";

  private static final String JAVA_VERSION =
      System.getProperties().getProperty("java.version");

  private static final Supplier GUAVA_MAJOR_VERSION =
      Suppliers.memoize(TestUtil::computeGuavaMajorVersion)::get;

  /** Matches a number with at least four zeros after the point. */
  private static final Pattern TRAILING_ZERO_PATTERN =
      Pattern.compile("-?[0-9]+\\.([0-9]*[1-9])?(00000*[0-9][0-9]?)");

  /** Matches a number with at least four nines after the point. */
  private static final Pattern TRAILING_NINE_PATTERN =
      Pattern.compile("-?[0-9]+\\.([0-9]*[0-8])?(99999*[0-9][0-9]?)");

  /** This is to be used by {@link #rethrow(Throwable, String)} to add extra information via
   * {@link Throwable#addSuppressed(Throwable)}. */
  private static class ExtraInformation extends Throwable {
    ExtraInformation(String message) {
      super(message);
    }
  }

  //~ Methods ----------------------------------------------------------------

  public static void assertEqualsVerbose(
      String expected,
      String actual) {
    Assertions.assertEquals(expected, actual,
        () -> "Expected:\n"
            + expected
            + "\nActual:\n"
            + actual
            + "\nActual java:\n"
            + toJavaString(actual) + '\n');
  }

  /**
   * Converts a string (which may contain quotes and newlines) into a java
   * literal.
   *
   * 

For example, *

string with "quotes" split
   * across lines
* *

becomes * *

"string with \"quotes\" split" + NL +
   *  "across lines"
*/ public static String quoteForJava(String s) { s = Util.replace(s, "\\", "\\\\"); s = Util.replace(s, "\"", "\\\""); s = LINE_BREAK_PATTERN.matcher(s).replaceAll(LINE_BREAK); s = TAB_PATTERN.matcher(s).replaceAll("\\\\t"); s = "\"" + s + "\""; final String spurious = " + \n\"\""; if (s.endsWith(spurious)) { s = s.substring(0, s.length() - spurious.length()); } return s; } /** * Converts a string (which may contain quotes and newlines) into a java * literal. * *

For example,

* *
string with "quotes" split
   * across lines
* *

becomes

* *
TestUtil.fold(
   *  "string with \"quotes\" split\n",
   *  + "across lines")
*/ public static String toJavaString(String s) { // Convert [string with "quotes" split // across lines] // into [fold( // "string with \"quotes\" split\n" // + "across lines")] // s = Util.replace(s, "\"", "\\\""); s = LINE_BREAK_PATTERN.matcher(s).replaceAll(LINE_BREAK); s = TAB_PATTERN.matcher(s).replaceAll("\\\\t"); s = "\"" + s + "\""; String spurious = "\n \\+ \"\""; if (s.endsWith(spurious)) { s = s.substring(0, s.length() - spurious.length()); } return s; } /** * Combines an array of strings, each representing a line, into a single * string containing line separators. */ public static String fold(String... strings) { StringBuilder buf = new StringBuilder(); for (String string : strings) { buf.append(string); buf.append('\n'); } return buf.toString(); } /** Quotes a string for Java or JSON. */ public static String escapeString(String s) { return escapeString(new StringBuilder(), s).toString(); } /** Quotes a string for Java or JSON, into a builder. */ public static StringBuilder escapeString(StringBuilder buf, String s) { buf.append('"'); int n = s.length(); char lastChar = 0; for (int i = 0; i < n; ++i) { char c = s.charAt(i); switch (c) { case '\\': buf.append("\\\\"); break; case '"': buf.append("\\\""); break; case '\n': buf.append("\\n"); break; case '\r': if (lastChar != '\n') { buf.append("\\r"); } break; default: buf.append(c); break; } lastChar = c; } return buf.append('"'); } /** * Quotes a pattern. */ public static String quotePattern(String s) { return s.replace("\\", "\\\\") .replace(".", "\\.") .replace("+", "\\+") .replace("{", "\\{") .replace("}", "\\}") .replace("|", "\\||") .replace("$", "\\$") .replace("?", "\\?") .replace("*", "\\*") .replace("(", "\\(") .replace(")", "\\)") .replace("[", "\\[") .replace("]", "\\]"); } /** Removes floating-point rounding errors from the end of a string. * *

{@code 12.300000006} becomes {@code 12.3}; * {@code -12.37999999991} becomes {@code -12.38}. */ public static String correctRoundedFloat(String s) { if (s == null) { return s; } final Matcher m = TRAILING_ZERO_PATTERN.matcher(s); if (m.matches()) { s = s.substring(0, s.length() - m.group(2).length()); } final Matcher m2 = TRAILING_NINE_PATTERN.matcher(s); if (m2.matches()) { s = s.substring(0, s.length() - m2.group(2).length()); if (s.length() > 0) { final char c = s.charAt(s.length() - 1); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': // '12.3499999996' became '12.34', now we make it '12.35' s = s.substring(0, s.length() - 1) + (char) (c + 1); break; case '.': // '12.9999991' became '12.', which we leave as is. break; } } } return s; } /** * Returns the Java major version: 7 for JDK 1.7, 8 for JDK 8, 10 for * JDK 10, etc. depending on current system property {@code java.version}. */ public static int getJavaMajorVersion() { return majorVersionFromString(JAVA_VERSION); } /** * Detects java major version given long format of full JDK version. * See JEP 223: New Version-String Scheme. * * @param version current version as string usually from {@code java.version} property. * @return major java version ({@code 8, 9, 10, 11} etc.) */ @VisibleForTesting static int majorVersionFromString(String version) { Objects.requireNonNull(version, "version"); if (version.startsWith("1.")) { // running on version <= 8 (expecting string of type: x.y.z*) final String[] versions = version.split("\\."); return Integer.parseInt(versions[1]); } // probably running on > 8 (just get first integer which is major version) Matcher matcher = Pattern.compile("^\\d+").matcher(version); if (!matcher.lookingAt()) { throw new IllegalArgumentException("Can't parse (detect) JDK version from " + version); } return Integer.parseInt(matcher.group()); } /** Returns the Guava major version. */ public static int getGuavaMajorVersion() { return GUAVA_MAJOR_VERSION.get(); } /** Computes the Guava major version. */ private static int computeGuavaMajorVersion() { // A list of classes and the Guava version that they were introduced. // The list should not contain any classes that are removed in future // versions of Guava. return new VersionChecker() .tryClass(2, "com.google.common.collect.ImmutableList") .tryClass(14, "com.google.common.reflect.Parameter") .tryClass(17, "com.google.common.base.VerifyException") .tryClass(21, "com.google.common.io.RecursiveDeleteOption") .tryClass(23, "com.google.common.util.concurrent.FluentFuture") .tryClass(26, "com.google.common.util.concurrent.ExecutionSequencer") .bestVersion; } /** Returns the JVM vendor. */ public static String getJavaVirtualMachineVendor() { return System.getProperty("java.vm.vendor"); } /** Given a list, returns the number of elements that are not between an * element that is less and an element that is greater. */ public static > SortedSet outOfOrderItems(List list) { E previous = null; final ImmutableSortedSet.Builder b = ImmutableSortedSet.naturalOrder(); for (E e : list) { if (previous != null && previous.compareTo(e) > 0) { b.add(e); } previous = e; } return b.build(); } /** Checks if exceptions have give substring. That is handy to prevent logging SQL text twice */ public static boolean hasMessage(Throwable t, String substring) { while (t != null) { String message = t.getMessage(); if (message != null && message.contains(substring)) { return true; } t = t.getCause(); } return false; } /** Rethrows given exception keeping stacktraces clean and compact. */ public static RuntimeException rethrow(Throwable e) throws E { if (e instanceof InvocationTargetException) { e = e.getCause(); } throw (E) e; } /** Rethrows given exception keeping stacktraces clean and compact. */ public static RuntimeException rethrow(Throwable e, String message) throws E { e.addSuppressed(new ExtraInformation(message)); throw (E) e; } /** Returns string representation of the given {@link Throwable}. */ public static String printStackTrace(Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); t.printStackTrace(pw); pw.flush(); return sw.toString(); } /** Checks whether a given class exists, and updates a version if it does. */ private static class VersionChecker { int bestVersion = -1; VersionChecker tryClass(int version, String className) { try { Class.forName(className); bestVersion = Math.max(version, bestVersion); } catch (ClassNotFoundException e) { // ignore } return this; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy