gt:         FATLogging.info(CLASS, methodName, "Request", priorRequestPath);
context: /*******************************************************************************
   * Copyright (c) 2020 IBM Corpo<ration and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  package io.openliberty.opentracing.internal.test;
  
  import java.io.File;
  import java.io.UnsupportedEncodingException;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.jboss.shrinkwrap.api.ShrinkWrap;
  import org.jboss.shrinkwrap.api.spec.WebArchive;
  import org.junit.AfterClass;
  import org.junit.Assert;
  import org.junit.BeforeClass;
  import org.junit.Test;
  
  import com.ibm.websphere.simplicity.ShrinkHelper;
  
  import componenttest.custom.junit.runner.Mode;
  import componenttest.custom.junit.runner.Mode.TestMode;
  import componenttest.topology.impl.LibertyServer;
  import componenttest.topology.impl.LibertyServerFactory;
  
  /**
   * <p>Open tracing FAT tests.</p>
   *
   * <p>The test suite:</p>
   *
   * <ul>
   * <li>{@link FATOpentracing#testImmediate}</li>
   * <li>{@link FATOpentracing#testManual}</li>
   * <li>{@link FATOpentracing#testDelayed2}</li>
   * <li>{@link FATOpentracing#testDelayed4}</li>
   * <li>{@link FATOpentracing#testDelayed6}</li>
   * <li>{@link FATOpentracing#testNested0}</li>
   * <li>{@link FATOpentracing#testNested1Sync}</li>
   * <li>{@link FATOpentracing#testNested1Async}</li>
   * <li>{@link FATOpentracing#testNested2Sync}</li>
   * <li>{@link FATOpentracing#testNested2Async}</li>
   * <li>{@link FATOpentracing#testNested4Sync}</li>
   * <li>{@link FATOpentracing#testNested4ASync}</li>
   * <li>{@link FATOpentracing#testExcludes}</li>
   * </ul>
   *
   * <p>Each test invokes an API within the FAT test service.  Two
   * verifiable results are produced:</p>
   *
   * <p>The API must answer the expected response value.  This does
   * not exercise the open-tracing feature, but is necessary to
   * demonstrate that the service which is being traced is functioning
   * correctly.</p>
   *
   * <p>The trace state, as obtained as a list of completed spans from
   * the injected tracer, and obtained through a special service API,
   * must contain the expected completed spans.</p>
   *
   * <p>See the several <code>verifySpans</code> methods, for example,
   * {@link FATOpentracing#verifyImmediateSpans}, each of which describes
   * the expected completed spans.</p>
   *
   * <p>The tail of the list of completed spans is examined.  Generally,
   * the list of completed spans must have at least an expected number
   * of spans.  The tail of the completed spans must match the pattern
   * of activity which is performed by the API which was used.</p>
   *
   * <p>Search for "***" within comments for specific tested conditions.</p>
   */
  @Mode(TestMode.FULL)
  public class FATOpentracing implements FATOpentracingConstants {
      private static final Class<? extends FATOpentracing> CLASS = FATOpentracing.class;
  
      private static void info(String methodName, String text, Object value) {
          System.out.println(CLASS.getSimpleName() + "." + methodName + ": " + text + " [ " + value + " ]");
          FATLogging.info(CLASS, methodName, text, value);
      }
  
      private static void info(String methodName, String text) {
          System.out.println(CLASS.getSimpleName() + "." + methodName + ": " + text);
          FATLogging.info(CLASS, methodName, text);
      }
  
      // OpenTrace FAT server ...
  
      private static LibertyServer server;
      private static final boolean usingMicroProfile = true;
  
      private static void setUpServer() throws Exception {
          server = LibertyServerFactory.getLibertyServer(OPENTRACING_FAT_SERVER1_NAME);
      }
  
      private static LibertyServer getServer() {
          return server;
      }
  
      private static String getHostName() {
          return server.getHostname();
      }
  
      private static int getPortNumber() {
          return server.getHttpDefaultPort();
      }
  
      private static void startServer() throws Exception {
          getServer().startServer(); // 'startServer' throws Exception
      }
  
      private static void stopServer() throws Exception {
          getServer().stopServer(); // 'stopServer' throws Exception
      }
  
      @BeforeClass
      public static void setUp() throws Exception {
          setUpServer();
  
          WebArchive serviceWar = createServiceWar();
          exportToServer(serviceWar);
          startServer();
      }
  
      @AfterClass
      public static void tearDown() throws Exception {
          stopServer();
      }
  
      /**
       * <p>Package up the service web module.</p>
       *
       * <p>The service classes are added, per
       * {@link FATOpentracingConstants#SERVICE_PACKAGE_NAME}.</p>
       *
       * <p>To make the tracer injection happen, a "beans.xml" must be added.</p>
       *
       * @return The packaged service web module.
       */
      private static WebArchive createServiceWar() {
          WebArchive serviceWar = ShrinkWrap.create(WebArchive.class, SERVICE_WAR_NAME);
          serviceWar.addPackages(true, SERVICE_PACKAGE_NAME);
          serviceWar.addAsWebInfResource(
              new File("test-applications/" + SERVICE_WAR_BASE_NAME + "/resources/beans.xml"));
          return serviceWar;
      }
  
      private static void exportToServer(WebArchive serviceWar) throws Exception {
          ShrinkHelper.exportAppToServer( getServer(), serviceWar );
          // 'exportAppToServer' throws Exception
      }
  
      // Parent conditions ...
      //
      // These are used by inner class 'ParentCondition' to select completed
      // spans.
      //
      // See method 'verifyNestedSpans2' for a comprehensive example.
  
      public static final String[] GET_IMMEDIATE_CONDITION = new String[] { GET_IMMEDIATE_PATH };
  
      public static final String[] GET_MANUAL_CONDITION = new String[] { GET_MANUAL_PATH };
      public static final String[] MANUAL_CONDITION = new String[] { "manualSpan" };
      public static final String[] GET_EXCLUDE_TEST_CONDITION = new String[] { GET_EXCLUDE_TEST_PATH };
  
      /**
       * <p>Answer the condition for a delayed get.</p>
       *
       * <p>Obtained using a method, since the condition depends on the particular
       * delay of the get.</p>
       *
       * @param delay The delay of the completed span.
       *
       * @return A string array of conditions for a delayed get.
       */
      public String[] getDelayedCondition(int delay) {
          return new String[] { GET_DELAYED_PATH, DELAY_PARAM_NAME + "=" + Integer.toString(delay) };
      }
  
      /**
       * <p>Answer the condition for a nested get.</p>
       *
       * <p>Obtained using a method, since the condition depends on the particular
       * nesting level of the get.</p>
       *
       * @param nestDepth The nesting depth of the completed span.
       *
       * @return A string array of conditions for a nested get.
       */
      public String[] getNestedCondition(int nestDepth) {
          return new String[] { GET_NESTED_PATH, NEST_DEPTH_PARAM_NAME + "=" + Integer.toString(nestDepth) };
      }
  
      // Verification utility ...
  
      /**
       * <p>Assertion utility for comparing two values.  Provides a more useful assertion
       * message to {@link Assert#assertEquals}.</p>
       *
       * @param valueName The name of the values which are being compared.
       * @param expectedValue The expected value.
       * @param actualValue The actual value.
       */
      public void assertEq(String valueName, Object expectedValue, Object actualValue) {
          Assert.assertEquals("EQ [ " + valueName + " ]", expectedValue, actualValue);
      }
  
      /**
       * <p>Assertion utility for comparing two values with a less-than or equal-to test.
       * Provides a more useful assertion message to {@link Assert#assertTrue}.</p>
       *
       * @param valueName The name of the values which are being compared.
       * @param lesserValue The expected lesser value.
       * @param greaterValue The expected greater value.
       */
      public void assertLtEq(String valueName, long lesserValue, long greaterValue) {
          Assert.assertTrue(
                  "LT_EQ [ " + valueName + " ] : [ " + Long.toString(lesserValue) + " ] [ " + greaterValue + " ]",
                  (lesserValue <= greaterValue));
      }
  
      /**
       * <p>Assertion utility for comparing two values with a less-than test.
       * Provides a more useful assertion message to {@link Assert#assertTrue}.</p>
       *
       * @param valueName The name of the values which are being compared.
       * @param lesserValue The expected lesser value.
       * @param greaterValue The expected greater value.
       */
      public void assertLt(String valueName, long lesserValue, long greaterValue) {
          Assert.assertTrue(
                  "LT [ " + valueName + " ] : [ " + Long.toString(lesserValue) + " ] [ " + greaterValue + " ]",
                  (lesserValue < greaterValue));
      }
  
      // Span verification ...
  
      /**
       * <p>Select a span from a completed spans collection.  Select based on a specified
       * span kind, and specified selection text.</p>
       *
       * <p>A subset of the completed spans collection is examined, working from the end
       * of the collection.</p>
       *
       * <p>Selection is current against the operation of the completed span.  See
       * {@link FATUtilsSpans.CompletedSpan#getOperation()}.  Additional selection
       * capabilities could be added.</p>
       *
       * <p>The first matching span is selected and returned.  No more than one matching
       * span is expected.  (This is not verified.)</p>
       *
       * @param completedSpans The overall collection of completed spans which is to
       *     be searched.
       * @param tailSize The number of spans to examine, starting from the end of the
       *     collection of completed spans.
       * @param spanKind The type of span to select.
       * @param selectText Text to search for in the operations of the completed spans.
       *
       * @return The span which matches the span kind and selection text.  Null if
       *     no span is selected.
       */
      public FATUtilsSpans.CompletedSpan findSpan(
          List<FATUtilsSpans.CompletedSpan> completedSpans, int tailSize,
          FATUtilsSpans.SpanKind spanKind, String... selectText) {
  
          String methodName = "findSpan";
  
          int numSpans = completedSpans.size();
          for ( int spanNo = numSpans - tailSize; spanNo < numSpans; spanNo++ ) {
              FATUtilsSpans.CompletedSpan completedSpan = completedSpans.get(spanNo);
              if ( !completedSpan.isSpanKind(spanKind) ) {
                  continue;
              }
  
              String operation = completedSpan.getTag("http.url");
              
              // If operation is null, it's a manual span
              
              if (operation == null) {
                  operation = completedSpan.getOperation();
              }
  
              boolean foundAll = true;
              for ( String text : selectText ) {
                  if ( !operation.contains(text) ) {
                      foundAll = false;
                      break;
                  }
              }
              if ( foundAll ) {
                  return completedSpan;
              }
          }
  
          info(methodName, "Span selection:");
          info(methodName, "SpanKind: " + spanKind + "(" + spanKind.getTagValue() + ")");
          info(methodName, "Selection: " + selectionText(selectText));
          info(methodName, "Total spans: " + Integer.toString(numSpans));
          info(methodName, "Tail size: " + Integer.toString(tailSize));
          for ( int spanNo = numSpans - tailSize; spanNo < numSpans; spanNo++ ) {
              FATUtilsSpans.CompletedSpan completedSpan = completedSpans.get(spanNo);
              info(methodName, "  [ " + Integer.toString(spanNo) + " ] [ " + completedSpan + " ]");
          }
  
          return null;
      }
      
      public void verifyContiguousSpans(
                                        List<FATUtilsSpans.CompletedSpan> completedSpans,
                                        int expectedSpanCount) {
          verifyContiguousSpans(completedSpans, expectedSpanCount, 1);
      }
  
      /**
       * <p>Verify that a contiguous collection of spans is present as the tail
       * of a collection of completed spans.</p>
       *
       * <p>All of the completed spans of the tail of the spans collection must have
       * the same span ID (see {@link FATUtilsSpans.CompletedSpan#getTraceId()}, and
       * exactly one of the spans must be a root span.</p>
       *
       * <p>All of the completed spans must have a non-null span ID, trace ID,
       * and parent ID.  (A span with no parent is assigned "0" as its parent ID.)</p>
       *
       * <p>Fail with an assertion error if the verification fails.</p>
       *
       * @param completedSpans The overall collection of completed spans which is to
       *    be examined.
       * @param expectedSpanCount The number of spans which must be present
       *    in the spans collection, and which must have the same span ID.
       */
      public void verifyContiguousSpans(
          List<FATUtilsSpans.CompletedSpan> completedSpans,
          int expectedSpanCount, int expectedRootSpanCount) {
  
          String methodName = "verifyContiguousSpans";
  
          // *** The expected number of spans must be present. ***
  
          int actualSpanCount = completedSpans.size();
          assertLtEq("Completed Spans", expectedSpanCount, actualSpanCount);
  
          int rootCount = 0;
  
          String initialTraceId = null;
  
          for ( int spanNo = actualSpanCount - expectedSpanCount; spanNo < actualSpanCount; spanNo++ ) {
              FATUtilsSpans.CompletedSpan nextSpan = completedSpans.get(spanNo);
              
              String nextTraceId = nextSpan.getTraceId();
              String nextSpanId = nextSpan.getSpanId();
              String nextParentId = nextSpan.getParentId();
  
              // *** The trace, span, and parent IDs of each span cannot be null. ***
  
              Assert.assertNotNull("Trace id", nextTraceId);
              Assert.assertNotNull("Span id", nextSpanId);
              Assert.assertNotNull("Parent id", nextParentId);
  
              if ( initialTraceId == null ) {
                  initialTraceId = nextTraceId;
              } else {
                  assertEq("Trace id", nextTraceId, initialTraceId);
              }
  
              if ( nextSpan.isRoot() ) {
                  rootCount++;
              }
          }
  
          if ( rootCount != 1 ) {
              for ( int spanNo = actualSpanCount - expectedSpanCount; spanNo < actualSpanCount; spanNo++ ) {
                  FATUtilsSpans.CompletedSpan nextSpan = completedSpans.get(spanNo);
                  info(methodName,
                      "Span [ " + Integer.toString(spanNo) + " ]" +
                      " SpanId [ " + nextSpan.getSpanId() + " ]" +
                      " ParentId [ " + nextSpan.getParentId() + " ]");
              }
          }
  
          // *** Exactly one root span must be present. ***
  
          assertEq("Root spans", Integer.valueOf(expectedRootSpanCount), Integer.valueOf(rootCount));
      }
  
      public static final boolean IS_CONTAINER = true;
      public static final boolean IS_CLIENT = false;
  
      /**
       * <p>Build a print string for a selection array.  This is
       * used for building strings for assertions.</p>
       *
       * @param selections An array of strings to convert to a single print string.
       *
       * @return A print string for the string array.
       */
      public static String selectionText(String[] selections) {
          StringBuilder builder = new StringBuilder();
          builder.append("{");
          boolean isFirst = true;
          for ( String selection : selections ) {
              String prefix;
              if ( isFirst ) {
                  prefix = " ";
                  isFirst = false;
              } else {
                  prefix = ", ";
              }
              builder.append(prefix);
  
              builder.append("\"");
              builder.append(selection);
              builder.append("\"");
          }
          builder.append(" }");
          return builder.toString();
      }
  
      /**
       * <p>Type used for selecting completed spans.</p>
       *
       * <p>Conditions are specified for a pair of spans which are
       * expected to have a parent-child relationship.</p>
       *
       * <p>A null condition may be specified for the parent span,
       * in which case the child span is expected to be a root span.</p>
       *
       * <p>See {@link #findSpan}, which select spans based on the
       * span kind and selection array as specified by the parent
       * and child condition information expressed by a parent
       * condition.</p>
       */
      public static class ParentCondition {
          public final FATUtilsSpans.SpanKind parentKind;
          public final String[] parentSelection;
  
          public final FATUtilsSpans.SpanKind childKind;
          public final String[] childSelection;
  
          public ParentCondition(
              FATUtilsSpans.SpanKind parentKind, String[] parentSelection,
              FATUtilsSpans.SpanKind childKind, String[] childSelection) {
  
              this.parentKind = parentKind;
              this.parentSelection = parentSelection;
  
              this.childKind = childKind;
              this.childSelection = childSelection;
          }
      }
  
      /**
       * <p>Verify the parent-child relationships of a collection of
       * completed spans against a collection of parent-child relationship
       * conditions.</p>
       *
       * <p>Examine a subset of the spans, starting from the end of the spans
       * collection.</p>
       *
       * <p>For each of the specified parent-child relationship conditions,
       * select the parent and child spans according to that condition, and
       * verify that parent ID of the child span is the same as the span ID of
       * the parent span.</p>
       *
       * @param completedSpans The overall collection of spans which is to
       *     be tested.
       * @param tailSize The number of spans to examine within the completed
       *     spans collection.
       * @param parentConditions Conditions to verify within the completed
       *     spans collection.
       */
      public void verifyParents(
              List<FATUtilsSpans.CompletedSpan> completedSpans, int tailSize,
              ParentCondition... parentConditions) {
  
          String methodName = "verifyParents";
  
          for ( ParentCondition parentCondition : parentConditions ) {
              // *** Verify that a child span was selected.  A child span must always be selected. ***
  
              FATUtilsSpans.CompletedSpan childSpan =
                  findSpan(completedSpans, tailSize,
                           parentCondition.childKind,
                           parentCondition.childSelection);
              Assert.assertNotNull("Child span selection [ " + selectionText(parentCondition.childSelection) + " ]", childSpan);
  
              FATUtilsSpans.CompletedSpan parentSpan;
              if ( parentCondition.parentSelection == null ) {
                  // *** When the parent selection is null, verify that the child span is a root span. ***
  
                  parentSpan = null;
  
                  if ( !childSpan.isRoot() ) {
                      info(methodName, "Span [ " + selectionText(parentCondition.childSelection) + " ] [ " + childSpan + " ] should be root");
                  }
                  Assert.assertTrue("Span [ " + selectionText(parentCondition.childSelection) + " ] [ " + childSpan + " ] is root", childSpan.isRoot());
  
              } else {
                  // *** When the parent selection is not null, verify that a parent span was selected. ***
  
                  parentSpan = findSpan(
                      completedSpans, tailSize,
                       parentCondition.parentKind,
                       parentCondition.parentSelection);
                  Assert.assertNotNull("Parent span selection [ " + selectionText(parentCondition.parentSelection) + " ]", parentSpan);
  
                  // *** And verify that the parent ID of the child span matches the span ID of the parent span. ***
  
                  String expectedParentId = parentSpan.getSpanId();
                  String actualParentId = childSpan.getParentId();
                  assertEq(
                      "Parent [ " + selectionText(parentCondition.parentSelection) + " ] [ " + parentSpan + " ]" +
                      " of [ " + selectionText(parentCondition.childSelection) + " ] [ " + childSpan + " ]",
                      expectedParentId, actualParentId);
              }
          }
      }
  
      /**
       * <p>Retrieve the completed span state by making a service call to the
       * open tracing FAT service.</p>
       *
       * @param priorRequestPath The immediately preceding request made to the
       *     open tracing FAT service.  Used for to match this request within
       *     the overall flow of FAT service request.
       *
       * @return The list of completed spans obtained from the opening tracing FAT
       *     service.
       *
       * @throws Exception Thrown if the service request failed, or if the completed
       *     spans could not be marshalled from the text obtained from the FAT service.
       */
      public List<FATUtilsSpans.CompletedSpan> getCompletedSpans(String priorRequestPath) throws Exception {
          String methodName = "getCompletedSpans";
  
          String requestUrl = getRequestUrl(GET_TRACER_STATE_PATH);
  