001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client.trace.hamcrest; 019 020import static org.apache.hadoop.hbase.client.trace.hamcrest.AttributesMatchers.containsEntry; 021import static org.hamcrest.Matchers.equalTo; 022import static org.hamcrest.Matchers.is; 023 024import io.opentelemetry.api.common.Attributes; 025import io.opentelemetry.api.trace.SpanKind; 026import io.opentelemetry.api.trace.StatusCode; 027import io.opentelemetry.sdk.trace.data.EventData; 028import io.opentelemetry.sdk.trace.data.SpanData; 029import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; 030import java.time.Duration; 031import java.util.Objects; 032import org.hamcrest.Description; 033import org.hamcrest.FeatureMatcher; 034import org.hamcrest.Matcher; 035import org.hamcrest.TypeSafeMatcher; 036 037/** 038 * Helper methods for matching against instances of {@link SpanData}. 039 */ 040public final class SpanDataMatchers { 041 042 private SpanDataMatchers() { 043 } 044 045 public static Matcher<SpanData> hasAttributes(Matcher<Attributes> matcher) { 046 return new FeatureMatcher<SpanData, Attributes>(matcher, "SpanData having attributes that ", 047 "attributes") { 048 @Override 049 protected Attributes featureValueOf(SpanData item) { 050 return item.getAttributes(); 051 } 052 }; 053 } 054 055 public static Matcher<SpanData> hasDuration(Matcher<Duration> matcher) { 056 return new FeatureMatcher<SpanData, Duration>(matcher, "SpanData having duration that ", 057 "duration") { 058 @Override 059 protected Duration featureValueOf(SpanData item) { 060 return Duration.ofNanos(item.getEndEpochNanos() - item.getStartEpochNanos()); 061 } 062 }; 063 } 064 065 public static Matcher<SpanData> hasEnded() { 066 return new TypeSafeMatcher<SpanData>() { 067 @Override 068 protected boolean matchesSafely(SpanData item) { 069 return item.hasEnded(); 070 } 071 072 @Override 073 public void describeTo(Description description) { 074 description.appendText("SpanData that hasEnded"); 075 } 076 }; 077 } 078 079 public static Matcher<SpanData> hasEvents(Matcher<Iterable<? super EventData>> matcher) { 080 return new FeatureMatcher<SpanData, Iterable<? super EventData>>(matcher, 081 "SpanData having events that", "events") { 082 @Override 083 protected Iterable<? super EventData> featureValueOf(SpanData item) { 084 return item.getEvents(); 085 } 086 }; 087 } 088 089 public static Matcher<SpanData> hasExceptionWithType(Matcher<? super String> matcher) { 090 return hasException(containsEntry(is(SemanticAttributes.EXCEPTION_TYPE), matcher)); 091 } 092 093 public static Matcher<SpanData> hasException(Matcher<? super Attributes> matcher) { 094 return new FeatureMatcher<SpanData, Attributes>(matcher, 095 "SpanData having Exception with Attributes that", "exception attributes") { 096 @Override 097 protected Attributes featureValueOf(SpanData actual) { 098 return actual.getEvents().stream() 099 .filter(e -> Objects.equals(SemanticAttributes.EXCEPTION_EVENT_NAME, e.getName())) 100 .map(EventData::getAttributes).findFirst().orElse(null); 101 } 102 }; 103 } 104 105 public static Matcher<SpanData> hasKind(SpanKind kind) { 106 return new FeatureMatcher<SpanData, SpanKind>(equalTo(kind), "SpanData with kind that", 107 "SpanKind") { 108 @Override 109 protected SpanKind featureValueOf(SpanData item) { 110 return item.getKind(); 111 } 112 }; 113 } 114 115 public static Matcher<SpanData> hasName(String name) { 116 return hasName(equalTo(name)); 117 } 118 119 public static Matcher<SpanData> hasName(Matcher<String> matcher) { 120 return new FeatureMatcher<SpanData, String>(matcher, "SpanKind with a name that", "name") { 121 @Override 122 protected String featureValueOf(SpanData item) { 123 return item.getName(); 124 } 125 }; 126 } 127 128 public static Matcher<SpanData> hasParentSpanId(String parentSpanId) { 129 return hasParentSpanId(equalTo(parentSpanId)); 130 } 131 132 public static Matcher<SpanData> hasParentSpanId(SpanData parent) { 133 return hasParentSpanId(parent.getSpanId()); 134 } 135 136 public static Matcher<SpanData> hasParentSpanId(Matcher<String> matcher) { 137 return new FeatureMatcher<SpanData, String>(matcher, "SpanKind with a parentSpanId that", 138 "parentSpanId") { 139 @Override 140 protected String featureValueOf(SpanData item) { 141 return item.getParentSpanId(); 142 } 143 }; 144 } 145 146 public static Matcher<SpanData> hasStatusWithCode(Matcher<StatusCode> matcher) { 147 return new FeatureMatcher<SpanData, StatusCode>(matcher, "SpanData with StatusCode that", 148 "statusWithCode") { 149 @Override 150 protected StatusCode featureValueOf(SpanData actual) { 151 return actual.getStatus().getStatusCode(); 152 } 153 }; 154 } 155 156 public static Matcher<SpanData> hasStatusWithCode(StatusCode statusCode) { 157 return hasStatusWithCode(is(equalTo(statusCode))); 158 } 159 160 public static Matcher<SpanData> hasTraceId(String traceId) { 161 return hasTraceId(is(equalTo(traceId))); 162 } 163 164 public static Matcher<SpanData> hasTraceId(Matcher<String> matcher) { 165 return new FeatureMatcher<SpanData, String>(matcher, "SpanData with a traceId that ", 166 "traceId") { 167 @Override 168 protected String featureValueOf(SpanData item) { 169 return item.getTraceId(); 170 } 171 }; 172 } 173}