diff --git a/Jenkinsfile-E2E b/Jenkinsfile-E2E
new file mode 100644
index 0000000000000000000000000000000000000000..d43016eed41d96348c4ef5bc8b7a869d283c2c8b
--- /dev/null
+++ b/Jenkinsfile-E2E
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ *
+ */
+
+pipeline {
+ agent {
+ label 'xenial'
+ }
+
+ tools {
+ jdk 'JDK 1.8 (latest)'
+ }
+
+ stages {
+ stage('Checkout Source Code') {
+ steps {
+ deleteDir()
+ checkout scm
+ sh 'git submodule update --init'
+ }
+ }
+
+ stage('Prepare Distribution Package') {
+ steps {
+ sh './mvnw -DskipTests clean package'
+ sh 'tar -zxf dist/apache-skywalking-apm-bin.tar.gz -C dist'
+ }
+ }
+
+ stage('Run End-to-End Tests') {
+ steps {
+ sh './mvnw -Dbuild.id=${BUILD_ID} -f test/e2e/pom.xml clean verify'
+ }
+ }
+ }
+
+ post {
+ always {
+ // "Abort old build on update" will interrupt the job completely,
+ // we need to clean up when there are containers started by the e2e tests
+ sh 'docker ps'
+ sh 'docker ps | grep -e "skywalking-e2e-container-${BUILD_ID}" | awk \'{print $1}\' | xargs --no-run-if-empty docker stop'
+ deleteDir()
+ }
+ }
+}
diff --git a/apm-dist/bin/oapService.sh b/apm-dist/bin/oapService.sh
index ed49bc9aad2a56e98a1d73e15e174f5f7e672aaa..962691b88a6a47faabf9dcdcd2824a15c165d7c5 100644
--- a/apm-dist/bin/oapService.sh
+++ b/apm-dist/bin/oapService.sh
@@ -20,10 +20,10 @@ PRG="$0"
PRGDIR=`dirname "$PRG"`
[ -z "$OAP_HOME" ] && OAP_HOME=`cd "$PRGDIR/.." >/dev/null; pwd`
-OAP_LOG_DIR="${OAP_HOME}/logs"
+OAP_LOG_DIR="${OAP_LOG_DIR:-${OAP_HOME}/logs}"
JAVA_OPTS=" -Xms256M -Xmx512M"
-if [ ! -d "${OAP_HOME}/logs" ]; then
+if [ ! -d "${OAP_LOG_DIR}" ]; then
mkdir -p "${OAP_LOG_DIR}"
fi
diff --git a/apm-dist/bin/webappService.sh b/apm-dist/bin/webappService.sh
index df0040dc765d37fbb2f10c7ef697bea7497e071b..890ea481b95ee7267d5a1d122638bd2c3d70a159 100644
--- a/apm-dist/bin/webappService.sh
+++ b/apm-dist/bin/webappService.sh
@@ -20,11 +20,11 @@ PRG="$0"
PRGDIR=`dirname "$PRG"`
[ -z "$WEBAPP_HOME" ] && WEBAPP_HOME=`cd "$PRGDIR/.." >/dev/null; pwd`
-WEBAPP_LOG_DIR="${WEBAPP_HOME}/logs"
+WEBAPP_LOG_DIR="${WEBAPP_LOG_DIR:-${WEBAPP_HOME}/logs}"
JAVA_OPTS=" -Xms256M -Xmx512M"
JAR_PATH="${WEBAPP_HOME}/webapp"
-if [ ! -d "${WEBAPP_HOME}/logs" ]; then
+if [ ! -d "${WEBAPP_LOG_DIR}" ]; then
mkdir -p "${WEBAPP_LOG_DIR}"
fi
diff --git a/test/e2e/e2e-base/pom.xml b/test/e2e/e2e-base/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6979adb5643416806c5eb3119061f7163f92bf77
--- /dev/null
+++ b/test/e2e/e2e-base/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ apache-skywalking-e2e
+ org.apache.skywalking
+ 1.0.0
+
+
+ 4.0.0
+
+ e2e-base
+
+
+
+ org.yaml
+ snakeyaml
+ ${snake.version}
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${jackson.version}
+
+
+
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/AbstractQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/AbstractQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..3bb8e47703a6f3a9d76a3728740144e6ec51d627
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/AbstractQuery.java
@@ -0,0 +1,90 @@
+/*
+ * 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.skywalking.e2e;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author kezhenxu94
+ */
+public abstract class AbstractQuery> {
+ private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
+ private static final DateTimeFormatter MINUTE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmm");
+
+ private String start;
+ private String end;
+ private String step = "SECOND";
+
+ public String start() {
+ if (start != null) {
+ return start;
+ }
+ return "SECOND".equals(step())
+ ? LocalDateTime.now(ZoneOffset.UTC).minusMinutes(5).format(TIME_FORMATTER)
+ : LocalDateTime.now(ZoneOffset.UTC).minusMinutes(5).format(MINUTE_TIME_FORMATTER);
+ }
+
+ public T start(String start) {
+ this.start = start;
+ return (T) this;
+ }
+
+ public T start(LocalDateTime start) {
+ if ("MINUTE".equals(step())) {
+ this.start = start.format(MINUTE_TIME_FORMATTER);
+ } else if ("SECOND".equals(step())) {
+ this.start = start.format(TIME_FORMATTER);
+ }
+ return (T) this;
+ }
+
+ public String end() {
+ if (end != null) {
+ return end;
+ }
+ return "SECOND".equals(step())
+ ? LocalDateTime.now(ZoneOffset.UTC).format(TIME_FORMATTER)
+ : LocalDateTime.now(ZoneOffset.UTC).format(MINUTE_TIME_FORMATTER);
+ }
+
+ public AbstractQuery end(String end) {
+ this.end = end;
+ return this;
+ }
+
+ public T end(LocalDateTime end) {
+ if ("MINUTE".equals(step())) {
+ this.end = end.format(MINUTE_TIME_FORMATTER);
+ } else if ("SECOND".equals(step())) {
+ this.end = end.format(TIME_FORMATTER);
+ }
+ return (T) this;
+ }
+
+ public String step() {
+ return step;
+ }
+
+ public T step(String step) {
+ this.step = step;
+ return (T) this;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/GQLResponse.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/GQLResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..134f10b8fe9c6c38e0c81d8cfef9e7ccdeb257e7
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/GQLResponse.java
@@ -0,0 +1,43 @@
+/*
+ * 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.skywalking.e2e;
+
+/**
+ * GraphQL response for easily test
+ *
+ * @author kezhenxu94
+ */
+public class GQLResponse {
+ private T data;
+
+ public T getData() {
+ return data;
+ }
+
+ public void setData(final T data) {
+ this.data = data;
+ }
+
+ @Override
+ public String toString() {
+ return "GQLResponse{" +
+ "data=" + data +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a12c79acb33da07d3b5171fb2d33806a0b33388
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/SimpleQueryClient.java
@@ -0,0 +1,202 @@
+/*
+ * 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.skywalking.e2e;
+
+import com.google.common.io.Resources;
+import org.apache.skywalking.e2e.metrics.Metrics;
+import org.apache.skywalking.e2e.metrics.MetricsData;
+import org.apache.skywalking.e2e.metrics.MetricsQuery;
+import org.apache.skywalking.e2e.service.Service;
+import org.apache.skywalking.e2e.service.ServicesData;
+import org.apache.skywalking.e2e.service.ServicesQuery;
+import org.apache.skywalking.e2e.service.endpoint.EndpointQuery;
+import org.apache.skywalking.e2e.service.endpoint.Endpoints;
+import org.apache.skywalking.e2e.service.instance.Instances;
+import org.apache.skywalking.e2e.service.instance.InstancesQuery;
+import org.apache.skywalking.e2e.topo.TopoData;
+import org.apache.skywalking.e2e.topo.TopoQuery;
+import org.apache.skywalking.e2e.topo.TopoResponse;
+import org.apache.skywalking.e2e.trace.Trace;
+import org.apache.skywalking.e2e.trace.TracesData;
+import org.apache.skywalking.e2e.trace.TracesQuery;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.RequestEntity;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author kezhenxu94
+ */
+public class SimpleQueryClient {
+ private final RestTemplate restTemplate = new RestTemplate();
+
+ private final String endpointUrl;
+
+ public SimpleQueryClient(String endpointUrl) {
+ this.endpointUrl = endpointUrl;
+ }
+
+ public List traces(final TracesQuery query) throws Exception {
+ final URL queryFileUrl = Resources.getResource("traces.gql");
+ final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8"))
+ .stream()
+ .filter(it -> !it.startsWith("#"))
+ .collect(Collectors.joining())
+ .replace("{start}", query.start())
+ .replace("{end}", query.end())
+ .replace("{step}", query.step())
+ .replace("{traceState}", query.traceState())
+ .replace("{pageNum}", query.pageNum())
+ .replace("{pageSize}", query.pageSize())
+ .replace("{needTotal}", query.needTotal())
+ .replace("{queryOrder}", query.queryOrder());
+ final ResponseEntity> responseEntity = restTemplate.exchange(
+ new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+ new ParameterizedTypeReference>() {
+ }
+ );
+
+ if (responseEntity.getStatusCode() != HttpStatus.OK) {
+ throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+ }
+
+ return Objects.requireNonNull(responseEntity.getBody()).getData().getTraces().getData();
+ }
+
+ public List services(final ServicesQuery query) throws Exception {
+ final URL queryFileUrl = Resources.getResource("services.gql");
+ final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8"))
+ .stream()
+ .filter(it -> !it.startsWith("#"))
+ .collect(Collectors.joining())
+ .replace("{start}", query.start())
+ .replace("{end}", query.end())
+ .replace("{step}", query.step());
+ final ResponseEntity> responseEntity = restTemplate.exchange(
+ new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+ new ParameterizedTypeReference>() {
+ }
+ );
+
+ if (responseEntity.getStatusCode() != HttpStatus.OK) {
+ throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+ }
+
+ return Objects.requireNonNull(responseEntity.getBody()).getData().getServices();
+ }
+
+ public Instances instances(final InstancesQuery query) throws Exception {
+ final URL queryFileUrl = Resources.getResource("instances.gql");
+ final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8"))
+ .stream()
+ .filter(it -> !it.startsWith("#"))
+ .collect(Collectors.joining())
+ .replace("{serviceId}", query.serviceId())
+ .replace("{start}", query.start())
+ .replace("{end}", query.end())
+ .replace("{step}", query.step());
+ final ResponseEntity> responseEntity = restTemplate.exchange(
+ new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+ new ParameterizedTypeReference>() {
+ }
+ );
+
+ if (responseEntity.getStatusCode() != HttpStatus.OK) {
+ throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+ }
+
+ return Objects.requireNonNull(responseEntity.getBody()).getData();
+ }
+
+ public Endpoints endpoints(final EndpointQuery query) throws Exception {
+ final URL queryFileUrl = Resources.getResource("endpoints.gql");
+ final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8"))
+ .stream()
+ .filter(it -> !it.startsWith("#"))
+ .collect(Collectors.joining())
+ .replace("{serviceId}", query.serviceId());
+ final ResponseEntity> responseEntity = restTemplate.exchange(
+ new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+ new ParameterizedTypeReference>() {
+ }
+ );
+
+ if (responseEntity.getStatusCode() != HttpStatus.OK) {
+ throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+ }
+
+ return Objects.requireNonNull(responseEntity.getBody()).getData();
+ }
+
+ public TopoData topo(final TopoQuery query) throws Exception {
+ final URL queryFileUrl = Resources.getResource("topo.gql");
+ final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8"))
+ .stream()
+ .filter(it -> !it.startsWith("#"))
+ .collect(Collectors.joining())
+ .replace("{step}", query.step())
+ .replace("{start}", query.start())
+ .replace("{end}", query.end());
+ final ResponseEntity> responseEntity = restTemplate.exchange(
+ new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+ new ParameterizedTypeReference>() {
+ }
+ );
+
+ if (responseEntity.getStatusCode() != HttpStatus.OK) {
+ throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+ }
+
+ return Objects.requireNonNull(responseEntity.getBody()).getData().getTopo();
+ }
+
+ public Metrics metrics(final MetricsQuery query) throws Exception {
+ final URL queryFileUrl = Resources.getResource("metrics.gql");
+ final String queryString = Resources.readLines(queryFileUrl, Charset.forName("UTF8"))
+ .stream()
+ .filter(it -> !it.startsWith("#"))
+ .collect(Collectors.joining())
+ .replace("{step}", query.step())
+ .replace("{start}", query.start())
+ .replace("{end}", query.end())
+ .replace("{metricsName}", query.metricsName())
+ .replace("{id}", query.id());
+ final ResponseEntity> responseEntity = restTemplate.exchange(
+ new RequestEntity<>(queryString, HttpMethod.POST, URI.create(endpointUrl)),
+ new ParameterizedTypeReference>() {
+ }
+ );
+
+ if (responseEntity.getStatusCode() != HttpStatus.OK) {
+ throw new RuntimeException("Response status != 200, actual: " + responseEntity.getStatusCode());
+ }
+
+ return Objects.requireNonNull(responseEntity.getBody()).getData().getMetrics();
+ }
+
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/AtLeastOneOfMetricsMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/AtLeastOneOfMetricsMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a47b0029504a1241ccd1dd14ccfbb693139051a
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/AtLeastOneOfMetricsMatcher.java
@@ -0,0 +1,62 @@
+/*
+ * 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.skywalking.e2e.metrics;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+import org.assertj.core.api.Condition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+public class AtLeastOneOfMetricsMatcher extends AbstractMatcher {
+ private MetricsValueMatcher value;
+
+ @Override
+ public void verify(Metrics metrics) {
+ assertThat(metrics.getValues()).isNotEmpty();
+ assertThat(metrics.getValues()).areAtLeastOne(new Condition(){
+ @Override
+ public boolean matches(MetricsValue value) {
+ try {
+ AtLeastOneOfMetricsMatcher.this.getValue().verify(value);
+ return true;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+ });
+ }
+
+ public MetricsValueMatcher getValue() {
+ return value;
+ }
+
+ public void setValue(MetricsValueMatcher value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "OneOfMetricsMatcher{" +
+ "value=" + value +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/Metrics.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/Metrics.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0cf0e387bba27f476cc69096d2b68aed0d18a9c
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/Metrics.java
@@ -0,0 +1,44 @@
+/*
+ * 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.skywalking.e2e.metrics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class Metrics {
+ private List values = new ArrayList<>();
+
+ public List getValues() {
+ return values;
+ }
+
+ public void setValues(List values) {
+ this.values = values;
+ }
+
+ @Override
+ public String toString() {
+ return "Metrics{" +
+ "values=" + values +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsData.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsData.java
new file mode 100644
index 0000000000000000000000000000000000000000..a78f50f8582654e7a52dd090709268f62cf38c3f
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsData.java
@@ -0,0 +1,41 @@
+/*
+ * 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.skywalking.e2e.metrics;
+
+/**
+ * @author kezhenxu94
+ */
+public class MetricsData {
+ private Metrics metrics;
+
+ public Metrics getMetrics() {
+ return metrics;
+ }
+
+ public void setMetrics(Metrics metrics) {
+ this.metrics = metrics;
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsData{" +
+ "metrics=" + metrics +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..edcbce1d161e52e83a57acd8fa60717b0720cc17
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsQuery.java
@@ -0,0 +1,71 @@
+/*
+ * 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.skywalking.e2e.metrics;
+
+import org.apache.skywalking.e2e.AbstractQuery;
+
+/**
+ * @author kezhenxu94
+ */
+public class MetricsQuery extends AbstractQuery {
+ public static String SERVICE_P99 = "service_p99";
+ public static String SERVICE_P95 = "service_p95";
+ public static String SERVICE_P90 = "service_p90";
+ public static String SERVICE_P75 = "service_p75";
+ public static String SERVICE_P50 = "service_p50";
+
+ public static String ENDPOINT_P99 = "endpoint_p99";
+ public static String ENDPOINT_P95 = "endpoint_p95";
+ public static String ENDPOINT_P90 = "endpoint_p90";
+ public static String ENDPOINT_P75 = "endpoint_p75";
+ public static String ENDPOINT_P50 = "endpoint_p50";
+
+ public static String SERVICE_INSTANCE_RESP_TIME = "service_instance_resp_time";
+ public static String SERVICE_INSTANCE_CPM = "service_instance_cpm";
+ public static String SERVICE_INSTANCE_SLA = "service_instance_sla";
+
+ private String id;
+ private String metricsName;
+
+ public String id() {
+ return id;
+ }
+
+ public MetricsQuery id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String metricsName() {
+ return metricsName;
+ }
+
+ public MetricsQuery metricsName(String metricsName) {
+ this.metricsName = metricsName;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsQuery{" +
+ "id='" + id + '\'' +
+ ", metricsName='" + metricsName + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsValue.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b27ce96a428df1b9bb36acd94e21828595dfd4f
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsValue.java
@@ -0,0 +1,42 @@
+/*
+ * 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.skywalking.e2e.metrics;
+
+/**
+ * @author kezhenxu94
+ */
+public class MetricsValue {
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ public MetricsValue setValue(String value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsValue{" +
+ "value='" + value + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsValueMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsValueMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3e70f3071e21200c95c29e18616f44e63aa3145
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/metrics/MetricsValueMatcher.java
@@ -0,0 +1,52 @@
+/*
+ * 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.skywalking.e2e.metrics;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.Objects;
+
+/**
+ * @author kezhenxu94
+ */
+public class MetricsValueMatcher extends AbstractMatcher {
+ private String value;
+
+ @Override
+ public void verify(MetricsValue metricsValue) {
+ if (Objects.nonNull(getValue())) {
+ doVerify(getValue(), metricsValue.getValue());
+ }
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "MetricsValueMatcher{" +
+ "value='" + value + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/Service.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/Service.java
new file mode 100644
index 0000000000000000000000000000000000000000..3df175c02f267e30397fca8e1a2aef91e731cefc
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/Service.java
@@ -0,0 +1,53 @@
+/*
+ * 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.skywalking.e2e.service;
+
+/**
+ * @author kezhenxu94
+ */
+public class Service {
+ private String key;
+ private String label;
+
+ public String getKey() {
+ return key;
+ }
+
+ public Service setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public Service setLabel(String label) {
+ this.label = label;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "Service{" +
+ "key='" + key + '\'' +
+ ", label='" + label + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServiceMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServiceMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..00fcdc65e507bc04d4dd9eb94be4c2373977b26a
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServiceMatcher.java
@@ -0,0 +1,75 @@
+/*
+ * 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.skywalking.e2e.service;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.Objects;
+
+/**
+ * A simple matcher to verify the given {@code Service} is expected
+ *
+ * @author kezhenxu94
+ */
+public class ServiceMatcher extends AbstractMatcher {
+
+ private String key;
+ private String label;
+
+ @Override
+ public void verify(final Service service) {
+ if (Objects.nonNull(getKey())) {
+ verifyKey(service);
+ }
+
+ if (Objects.nonNull(getLabel())) {
+ verifyLabel(service);
+ }
+ }
+
+ private void verifyKey(Service service) {
+ final String expected = this.getKey();
+ final String actual = service.getKey();
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyLabel(Service service) {
+ final String expected = this.getLabel();
+ final String actual = String.valueOf(service.getLabel());
+
+ doVerify(expected, actual);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesData.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesData.java
new file mode 100644
index 0000000000000000000000000000000000000000..c14a9d25df21b72ff2128570bd45482411e9c8ab
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesData.java
@@ -0,0 +1,36 @@
+/*
+ * 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.skywalking.e2e.service;
+
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class ServicesData {
+ private List services;
+
+ public List getServices() {
+ return services;
+ }
+
+ public void setServices(List services) {
+ this.services = services;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b33d7f1f28ed15ee963662611885066caf2566d
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesMatcher.java
@@ -0,0 +1,53 @@
+/*
+ * 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.skywalking.e2e.service;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+public class ServicesMatcher {
+ private List services;
+
+ public ServicesMatcher() {
+ this.services = new LinkedList<>();
+ }
+
+ public List getServices() {
+ return services;
+ }
+
+ public void setServices(List services) {
+ this.services = services;
+ }
+
+ public void verify(final List services) {
+ assertThat(services).hasSameSizeAs(this.getServices());
+
+ int size = this.getServices().size();
+
+ for (int i = 0; i < size; i++) {
+ this.getServices().get(i).verify(services.get(i));
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..d1400f4f1e51610c844c40c6a4d83744d00365b0
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/ServicesQuery.java
@@ -0,0 +1,27 @@
+/*
+ * 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.skywalking.e2e.service;
+
+import org.apache.skywalking.e2e.AbstractQuery;
+
+/**
+ * @author kezhenxu94
+ */
+public class ServicesQuery extends AbstractQuery {
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/Endpoint.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/Endpoint.java
new file mode 100644
index 0000000000000000000000000000000000000000..b23f45064c4aa0a3d8ccd25fd5006ae51e60e5ff
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/Endpoint.java
@@ -0,0 +1,51 @@
+/*
+ * 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.skywalking.e2e.service.endpoint;
+
+/**
+ * @author kezhenxu94
+ */
+public class Endpoint {
+ private String key;
+ private String label;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return "Endpoint{" +
+ "key='" + key + '\'' +
+ ", label='" + label + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..f35da29ac3f506dd4eab74a61ecff96d45f3d6c9
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointMatcher.java
@@ -0,0 +1,57 @@
+/*
+ * 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.skywalking.e2e.service.endpoint;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.Objects;
+
+/**
+ * @author kezhenxu94
+ */
+public class EndpointMatcher extends AbstractMatcher {
+ private String key;
+ private String label;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public void verify(final Endpoint endpoint) {
+ if (Objects.nonNull(getKey())) {
+ doVerify(getKey(), endpoint.getKey());
+ }
+ if (Objects.nonNull(getLabel())) {
+ doVerify(getLabel(), endpoint.getLabel());
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6be61d7ec15f380c3149f35f9354eb27a42599e
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointQuery.java
@@ -0,0 +1,53 @@
+/*
+ * 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.skywalking.e2e.service.endpoint;
+
+/**
+ * @author kezhenxu94
+ */
+public class EndpointQuery {
+ private String serviceId;
+ private String keyword;
+
+ public String serviceId() {
+ return serviceId;
+ }
+
+ public EndpointQuery serviceId(String serviceId) {
+ this.serviceId = serviceId;
+ return this;
+ }
+
+ public String keyword() {
+ return keyword;
+ }
+
+ public EndpointQuery keyword(String keyword) {
+ this.keyword = keyword;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "EndpointQuery{" +
+ "serviceId='" + serviceId + '\'' +
+ ", keyword='" + keyword + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/Endpoints.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/Endpoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd89860cc7545a511470dda54b294cc269831c25
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/Endpoints.java
@@ -0,0 +1,43 @@
+/*
+ * 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.skywalking.e2e.service.endpoint;
+
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class Endpoints {
+ private List endpoints;
+
+ public List getEndpoints() {
+ return endpoints;
+ }
+
+ public void setEndpoints(List endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ @Override
+ public String toString() {
+ return "Endpoints{" +
+ "endpoints=" + endpoints +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointsMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointsMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..89c980b2a76c9e6253c0e915bd9e071df0d43ea3
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/endpoint/EndpointsMatcher.java
@@ -0,0 +1,61 @@
+/*
+ * 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.skywalking.e2e.service.endpoint;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.List;
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+public class EndpointsMatcher extends AbstractMatcher {
+ private List endpoints;
+
+ public List getEndpoints() {
+ return endpoints;
+ }
+
+ public void setEndpoints(List endpoints) {
+ this.endpoints = endpoints;
+ }
+
+ @Override
+ public void verify(Endpoints endpoints) {
+ if (Objects.nonNull(getEndpoints())) {
+ assertThat(endpoints.getEndpoints()).hasSameSizeAs(getEndpoints());
+
+ int size = getEndpoints().size();
+
+ for (int i = 0; i < size; i++) {
+ getEndpoints().get(i).verify(endpoints.getEndpoints().get(i));
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "EndpointsMatcher{" +
+ "endpoints=" + endpoints +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Attribute.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Attribute.java
new file mode 100644
index 0000000000000000000000000000000000000000..c81b3b608e65ce019799c01d2276d9dc3a95c872
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Attribute.java
@@ -0,0 +1,51 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+/**
+ * @author kezhenxu94
+ */
+public class Attribute {
+ private String name;
+ private String value;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "Attribute{" +
+ "name='" + name + '\'' +
+ ", value='" + value + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/AttributeMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/AttributeMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..2293389c4719f0ad86f26afed4ec3a0c30352313
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/AttributeMatcher.java
@@ -0,0 +1,63 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.Objects;
+
+/**
+ * @author kezhenxu94
+ */
+public class AttributeMatcher extends AbstractMatcher {
+ private String name;
+ private String value;
+
+ @Override
+ public void verify(final Attribute attribute) {
+ if (Objects.nonNull(attribute.getName())) {
+ doVerify(getName(), attribute.getName());
+ doVerify(getValue(), attribute.getValue());
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "Attribute{" +
+ "name='" + name + '\'' +
+ ", value='" + value + '\'' +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Instance.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Instance.java
new file mode 100644
index 0000000000000000000000000000000000000000..57e6a567047767756fa7771c2aa0826134a99dbe
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Instance.java
@@ -0,0 +1,65 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class Instance {
+ private String key;
+ private String label;
+ private List attributes;
+
+ public String getKey() {
+ return key;
+ }
+
+ public Instance setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public Instance setLabel(String label) {
+ this.label = label;
+ return this;
+ }
+
+ public List getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(List attributes) {
+ this.attributes = attributes;
+ }
+
+ @Override
+ public String toString() {
+ return "Instance{" +
+ "key='" + key + '\'' +
+ ", label='" + label + '\'' +
+ ", attributes=" + attributes +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstanceMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstanceMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..af6434afd913bf54d73a319880c9da235330832a
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstanceMatcher.java
@@ -0,0 +1,113 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.List;
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A simple matcher to verify the given {@code Service} is expected
+ *
+ * @author kezhenxu94
+ */
+public class InstanceMatcher extends AbstractMatcher {
+
+ private String key;
+ private String label;
+ private List attributes;
+
+ @Override
+ public void verify(final Instance instance) {
+ if (Objects.nonNull(getKey())) {
+ verifyKey(instance);
+ }
+
+ if (Objects.nonNull(getLabel())) {
+ verifyLabel(instance);
+ }
+
+ if (Objects.nonNull(getAttributes())) {
+ verifyAttributes(instance);
+ }
+ }
+
+ private void verifyKey(Instance instance) {
+ final String expected = this.getKey();
+ final String actual = instance.getKey();
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyLabel(Instance instance) {
+ final String expected = this.getLabel();
+ final String actual = String.valueOf(instance.getLabel());
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyAttributes(Instance instance) {
+ final List expected = this.getAttributes();
+ final List actual = instance.getAttributes();
+
+ assertThat(actual).hasSameSizeAs(expected);
+
+ int size = expected.size();
+
+ for (int i = 0; i < size; i++) {
+ expected.get(i).verify(actual.get(i));
+ }
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public List getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(List attributes) {
+ this.attributes = attributes;
+ }
+
+ @Override
+ public String toString() {
+ return "InstanceMatcher{" +
+ "key='" + key + '\'' +
+ ", label='" + label + '\'' +
+ ", attributes=" + attributes +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Instances.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Instances.java
new file mode 100644
index 0000000000000000000000000000000000000000..6896a03f101c42ac30c060d5ef2a9829f839b8a2
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/Instances.java
@@ -0,0 +1,36 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class Instances {
+ private List instances;
+
+ public List getInstances() {
+ return instances;
+ }
+
+ public void setInstances(List instances) {
+ this.instances = instances;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstancesMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstancesMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..c367958a61eae8d4eed17d4ffb7befb144f2cacf
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstancesMatcher.java
@@ -0,0 +1,55 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+import org.assertj.core.api.Assertions;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class InstancesMatcher extends AbstractMatcher {
+ private List instances;
+
+ public InstancesMatcher() {
+ this.instances = new LinkedList<>();
+ }
+
+ public List getInstances() {
+ return instances;
+ }
+
+ public void setInstances(List instances) {
+ this.instances = instances;
+ }
+
+ @Override
+ public void verify(final Instances instances) {
+ Assertions.assertThat(instances.getInstances()).hasSameSizeAs(this.getInstances());
+
+ int size = this.getInstances().size();
+
+ for (int i = 0; i < size; i++) {
+ this.getInstances().get(i).verify(instances.getInstances().get(i));
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstancesQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstancesQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..59878f9d9bc868c22639fd5dcda201ad66857b58
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/service/instance/InstancesQuery.java
@@ -0,0 +1,80 @@
+/*
+ * 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.skywalking.e2e.service.instance;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author kezhenxu94
+ */
+public class InstancesQuery {
+ private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
+
+ private String start = LocalDateTime.now(ZoneOffset.UTC).format(TIME_FORMATTER);
+ private String end = LocalDateTime.now(ZoneOffset.UTC).minusMinutes(15).format(TIME_FORMATTER);
+ private String step = "SECOND";
+ private String serviceId;
+
+ public String serviceId() {
+ return serviceId;
+ }
+
+ public InstancesQuery serviceId(String serviceId) {
+ this.serviceId = serviceId;
+ return this;
+ }
+
+ public String start() {
+ return start;
+ }
+
+ public InstancesQuery start(String start) {
+ this.start = start;
+ return this;
+ }
+
+ public InstancesQuery start(LocalDateTime start) {
+ this.start = start.format(TIME_FORMATTER);
+ return this;
+ }
+
+ public String end() {
+ return end;
+ }
+
+ public InstancesQuery end(String end) {
+ this.end = end;
+ return this;
+ }
+
+ public InstancesQuery end(LocalDateTime end) {
+ this.end = end.format(TIME_FORMATTER);
+ return this;
+ }
+
+ public String step() {
+ return step;
+ }
+
+ public InstancesQuery step(String step) {
+ this.step = step;
+ return this; }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/Call.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/Call.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef39714956230346b2392d13d384087f36116b86
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/Call.java
@@ -0,0 +1,67 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class Call {
+ private String id;
+ private String source;
+ private List detectPoints;
+ private String target;
+
+ public String getId() {
+ return id;
+ }
+
+ public Call setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public Call setSource(String source) {
+ this.source = source;
+ return this;
+ }
+
+ public List getDetectPoints() {
+ return detectPoints;
+ }
+
+ public Call setDetectPoints(List detectPoints) {
+ this.detectPoints = detectPoints;
+ return this;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public Call setTarget(String target) {
+ this.target = target;
+ return this;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/CallMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/CallMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..e69b0eb6a23addfa1cc19967473113e4c3c10347
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/CallMatcher.java
@@ -0,0 +1,104 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.List;
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+public class CallMatcher extends AbstractMatcher {
+ private String id;
+ private String source;
+ private List detectPoints;
+ private String target;
+
+ @Override
+ public void verify(final Call call) {
+ if (Objects.nonNull(getId())) {
+ final String expected = this.getId();
+ final String actual = call.getId();
+
+ doVerify(expected, actual);
+ }
+
+ if (Objects.nonNull(getSource())) {
+ final String expected = this.getSource();
+ final String actual = call.getSource();
+
+ doVerify(expected, actual);
+ }
+
+ if (Objects.nonNull(getDetectPoints())) {
+ assertThat(getDetectPoints()).hasSameSizeAs(call.getDetectPoints());
+ int size = getDetectPoints().size();
+
+ for (int i = 0; i < size; i++) {
+ final String expected = getDetectPoints().get(i);
+ final String actual = call.getDetectPoints().get(i);
+
+ doVerify(expected, actual);
+ }
+ }
+
+ if (Objects.nonNull(getTarget())) {
+ final String expected = this.getTarget();
+ final String actual = call.getTarget();
+
+ doVerify(expected, actual);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public List getDetectPoints() {
+ return detectPoints;
+ }
+
+ public void setDetectPoints(List detectPoints) {
+ this.detectPoints = detectPoints;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/Node.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/Node.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb796afd8f7cc25eb59909695e3900baf556f9c2
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/Node.java
@@ -0,0 +1,65 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+/**
+ * @author kezhenxu94
+ */
+public class Node {
+ private String id;
+ private String name;
+ private String type;
+ private String isReal;
+
+ public String getId() {
+ return id;
+ }
+
+ public Node setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Node setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Node setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public String getReal() {
+ return isReal;
+ }
+
+ public Node setIsReal(String real) {
+ isReal = real;
+ return this;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/NodeMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/NodeMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4f264bf3ecfef37d64503c17e37807e21a74a4d
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/NodeMatcher.java
@@ -0,0 +1,96 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.Objects;
+
+/**
+ * @author kezhenxu94
+ */
+public class NodeMatcher extends AbstractMatcher {
+ private String id;
+ private String name;
+ private String type;
+ private String isReal;
+
+ @Override
+ public void verify(final Node node) {
+ if (Objects.nonNull(getId())) {
+ final String expected = this.getId();
+ final String actual = node.getId();
+
+ doVerify(expected, actual);
+ }
+
+ if (Objects.nonNull(getName())) {
+ final String expected = this.getName();
+ final String actual = node.getName();
+
+ doVerify(expected, actual);
+ }
+
+ if (Objects.nonNull(getType())) {
+ final String expected = this.getType();
+ final String actual = node.getType();
+
+ doVerify(expected, actual);
+ }
+
+ if (Objects.nonNull(getIsReal())) {
+ final String expected = this.getIsReal();
+ final String actual = String.valueOf(node.getReal());
+
+ doVerify(expected, actual);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getIsReal() {
+ return isReal;
+ }
+
+ public void setIsReal(String isReal) {
+ this.isReal = isReal;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb6c8870ab5cfedbfad3c4dff7392897d379933c
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoData.java
@@ -0,0 +1,53 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class TopoData {
+ private List nodes;
+ private List calls;
+
+ public TopoData() {
+ nodes = new ArrayList<>();
+ calls = new ArrayList<>();
+ }
+
+ public List getNodes() {
+ return nodes;
+ }
+
+ public TopoData setNodes(List nodes) {
+ this.nodes = nodes;
+ return this;
+ }
+
+ public List getCalls() {
+ return calls;
+ }
+
+ public TopoData setCalls(List calls) {
+ this.calls = calls;
+ return this;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa64b04c0e8b35d69624ded83ae101ec54a59831
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoMatcher.java
@@ -0,0 +1,84 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.List;
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A simple matcher to verify the given {@code Service} is expected
+ *
+ * @author kezhenxu94
+ */
+public class TopoMatcher extends AbstractMatcher {
+
+ private List nodes;
+ private List calls;
+
+ @Override
+ public void verify(final TopoData topoData) {
+ if (Objects.nonNull(getNodes())) {
+ verifyNodes(topoData);
+ }
+
+ if (Objects.nonNull(getCalls())) {
+ verifyCalls(topoData);
+ }
+ }
+
+ private void verifyNodes(TopoData topoData) {
+ assertThat(topoData.getNodes()).hasSameSizeAs(getNodes());
+
+ int size = getNodes().size();
+
+ for (int i = 0; i < size; i++) {
+ getNodes().get(i).verify(topoData.getNodes().get(i));
+ }
+ }
+
+ private void verifyCalls(TopoData topoData) {
+ assertThat(topoData.getCalls()).hasSameSizeAs(getCalls());
+
+ int size = getCalls().size();
+
+ for (int i = 0; i < size; i++) {
+ getCalls().get(i).verify(topoData.getCalls().get(i));
+ }
+ }
+
+ public List getNodes() {
+ return nodes;
+ }
+
+ public void setNodes(List nodes) {
+ this.nodes = nodes;
+ }
+
+ public List getCalls() {
+ return calls;
+ }
+
+ public void setCalls(List calls) {
+ this.calls = calls;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea318a234364dab8af5aa8efcf0b5bd489a87f7e
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoQuery.java
@@ -0,0 +1,27 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+import org.apache.skywalking.e2e.AbstractQuery;
+
+/**
+ * @author kezhenxu94
+ */
+public class TopoQuery extends AbstractQuery {
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoResponse.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..6aadb9d5124dde6576870b72baf847bcd664736c
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/topo/TopoResponse.java
@@ -0,0 +1,34 @@
+/*
+ * 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.skywalking.e2e.topo;
+
+/**
+ * @author kezhenxu94
+ */
+public class TopoResponse {
+ private TopoData topo;
+
+ public TopoData getTopo() {
+ return topo;
+ }
+
+ public void setTopo(TopoData topo) {
+ this.topo = topo;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/Trace.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/Trace.java
new file mode 100644
index 0000000000000000000000000000000000000000..6296d79633b7222edf0700beae8047958977242b
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/Trace.java
@@ -0,0 +1,95 @@
+/*
+ * 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.skywalking.e2e.trace;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class Trace {
+ private String key;
+ private final List endpointNames;
+ private int duration;
+ private String start;
+ private boolean isError;
+ private final List traceIds;
+
+ public Trace() {
+ this.endpointNames = new ArrayList<>();
+ this.traceIds = new ArrayList<>();
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Trace setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public List getEndpointNames() {
+ return endpointNames;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public Trace setDuration(int duration) {
+ this.duration = duration;
+ return this;
+ }
+
+ public String getStart() {
+ return start;
+ }
+
+ public Trace setStart(String start) {
+ this.start = start;
+ return this;
+ }
+
+ public boolean isError() {
+ return isError;
+ }
+
+ public Trace setError(boolean error) {
+ isError = error;
+ return this;
+ }
+
+ public List getTraceIds() {
+ return traceIds;
+ }
+
+ @Override
+ public String toString() {
+ return "Trace{" +
+ "key='" + key + '\'' +
+ ", endpointNames=" + endpointNames +
+ ", duration=" + duration +
+ ", start='" + start + '\'' +
+ ", isError=" + isError +
+ ", traceIds=" + traceIds +
+ '}';
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TraceMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TraceMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..30a537e3229199f9c15f6c18d83005413804eb72
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TraceMatcher.java
@@ -0,0 +1,171 @@
+/*
+ * 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.skywalking.e2e.trace;
+
+import com.google.common.base.Strings;
+import org.apache.skywalking.e2e.verification.AbstractMatcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A simple matcher to verify the given {@code Trace} is expected
+ *
+ * @author kezhenxu94
+ */
+public class TraceMatcher extends AbstractMatcher {
+ private String key;
+ private List endpointNames;
+ private String duration;
+ private String start;
+ private String isError;
+ private List traceIds;
+
+ @Override
+ public void verify(final Trace trace) {
+ if (Objects.nonNull(getKey())) {
+ verifyKey(trace);
+ }
+
+ if (Objects.nonNull(getEndpointNames())) {
+ verifyEndpointName(trace);
+ }
+
+ if (Objects.nonNull(getDuration())) {
+ verifyDuration(trace);
+ }
+
+ if (Objects.nonNull(getStart())) {
+ verifyStart(trace);
+ }
+
+ if (Objects.nonNull(getIsError())) {
+ verifyIsError(trace);
+ }
+
+ if (Objects.nonNull(getTraceIds())) {
+ verifyTraceIds(trace);
+ }
+ }
+
+ private void verifyKey(Trace trace) {
+ final String expected = this.getKey();
+ final String actual = trace.getKey();
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyEndpointName(Trace trace) {
+ assertThat(trace.getEndpointNames()).hasSameSizeAs(getEndpointNames());
+
+ int size = getEndpointNames().size();
+
+ for (int i = 0; i < size; i++) {
+ final String expected = getEndpointNames().get(i);
+ final String actual = Strings.nullToEmpty(trace.getEndpointNames().get(i));
+
+ doVerify(expected, actual);
+ }
+ }
+
+ private void verifyDuration(Trace trace) {
+ final String expected = this.getDuration();
+ final String actual = String.valueOf(trace.getDuration());
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyStart(Trace trace) {
+ final String expected = this.getStart();
+ final String actual = trace.getStart();
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyIsError(Trace trace) {
+ final String expected = this.getIsError();
+ final String actual = Strings.nullToEmpty(String.valueOf(trace.isError()));
+
+ doVerify(expected, actual);
+ }
+
+ private void verifyTraceIds(Trace trace) {
+ assertThat(trace.getTraceIds()).hasSameSizeAs(getTraceIds());
+
+ int size = getTraceIds().size();
+
+ for (int i = 0; i < size; i++) {
+ final String expected = getTraceIds().get(i);
+ final String actual = trace.getTraceIds().get(i);
+
+ doVerify(expected, actual);
+ }
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public void setEndpointNames(List endpointNames) {
+ this.endpointNames = endpointNames;
+ }
+
+ public List getEndpointNames() {
+ return endpointNames != null ? endpointNames : new ArrayList<>();
+ }
+
+ public String getDuration() {
+ return duration;
+ }
+
+ public void setDuration(String duration) {
+ this.duration = duration;
+ }
+
+ public String getStart() {
+ return start;
+ }
+
+ public void setStart(String start) {
+ this.start = start;
+ }
+
+ public String getIsError() {
+ return isError;
+ }
+
+ public void setIsError(String isError) {
+ this.isError = isError;
+ }
+
+ public void setTraceIds(List traceIds) {
+ this.traceIds = traceIds;
+ }
+
+ public List getTraceIds() {
+ return traceIds != null ? traceIds : new ArrayList<>();
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesData.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesData.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa5b49a2f60e992b57825a6a66277f6a3c076951
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesData.java
@@ -0,0 +1,49 @@
+/*
+ * 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.skywalking.e2e.trace;
+
+import java.util.List;
+
+/**
+ * @author kezhenxu94
+ */
+public class TracesData {
+ public static class Traces {
+ private List data;
+
+ public List getData() {
+ return data;
+ }
+
+ public Traces setData(List data) {
+ this.data = data;
+ return this;
+ }
+ }
+
+ private Traces traces;
+
+ public Traces getTraces() {
+ return traces;
+ }
+
+ public void setTraces(final Traces traces) {
+ this.traces = traces;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..cade9ccdbeb0e928218ece08d881fef1a063c414
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesMatcher.java
@@ -0,0 +1,53 @@
+/*
+ * 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.skywalking.e2e.trace;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+public class TracesMatcher {
+ private List traces;
+
+ public TracesMatcher() {
+ this.traces = new LinkedList<>();
+ }
+
+ public List getTraces() {
+ return traces;
+ }
+
+ public void setTraces(List traces) {
+ this.traces = traces;
+ }
+
+ public void verify(final List traces) {
+ assertThat(traces).hasSameSizeAs(this.traces);
+
+ int size = this.traces.size();
+
+ for (int i = 0; i < size; i++) {
+ this.traces.get(i).verify(traces.get(i));
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesQuery.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesQuery.java
new file mode 100644
index 0000000000000000000000000000000000000000..22b2455d19aa03d5b8935e6d13b7b93b82681eb2
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/trace/TracesQuery.java
@@ -0,0 +1,92 @@
+/*
+ * 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.skywalking.e2e.trace;
+
+import org.apache.skywalking.e2e.AbstractQuery;
+
+/**
+ * @author kezhenxu94
+ */
+public class TracesQuery extends AbstractQuery {
+ private String traceState = "ALL";
+ private String pageNum = "1";
+ private String pageSize = "15";
+ private String needTotal = "true";
+ private String queryOrder = "BY_DURATION";
+
+ public String traceState() {
+ return traceState;
+ }
+
+ public TracesQuery traceState(String traceState) {
+ this.traceState = traceState;
+ return this;
+ }
+
+ public String pageNum() {
+ return pageNum;
+ }
+
+ public TracesQuery pageNum(String pageNum) {
+ this.pageNum = pageNum;
+ return this;
+ }
+
+ public TracesQuery pageNum(int pageNum) {
+ this.pageNum = String.valueOf(pageNum);
+ return this;
+ }
+
+ public String pageSize() {
+ return pageSize;
+ }
+
+ public TracesQuery pageSize(String pageSize) {
+ this.pageSize = pageSize;
+ return this;
+ }
+
+ public String needTotal() {
+ return needTotal;
+ }
+
+ public TracesQuery needTotal(boolean needTotal) {
+ this.needTotal = String.valueOf(needTotal);
+ return this;
+ }
+
+ public String queryOrder() {
+ return queryOrder;
+ }
+
+ public TracesQuery queryOrder(String queryOrder) {
+ this.queryOrder = queryOrder;
+ return this;
+ }
+
+ public TracesQuery orderByDuration() {
+ this.queryOrder = "BY_DURATION";
+ return this;
+ }
+
+ public TracesQuery pageSize(int pageSize) {
+ this.pageSize = String.valueOf(pageSize);
+ return this;
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/verification/AbstractMatcher.java b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/verification/AbstractMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..a47f5f5f156d1611bc4ebd56ef4708524c7edc17
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/java/org/apache/skywalking/e2e/verification/AbstractMatcher.java
@@ -0,0 +1,71 @@
+/*
+ * 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.skywalking.e2e.verification;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+public abstract class AbstractMatcher {
+ private static final Pattern NE_MATCHER = Pattern.compile("ne\\s+(?.+)");
+ private static final Pattern GT_MATCHER = Pattern.compile("gt\\s+(?.+)");
+ private static final Pattern GE_MATCHER = Pattern.compile("ge\\s+(?.+)");
+ private static final Pattern NN_MATCHER = Pattern.compile("^not null$");
+
+ public abstract void verify(T t);
+
+ protected void doVerify(String expected, String actual) {
+ Matcher matcher = NN_MATCHER.matcher(expected);
+ if (matcher.find()) {
+ assertThat(actual).isNotNull();
+ return;
+ }
+
+ matcher = NE_MATCHER.matcher(expected);
+ if (matcher.find()) {
+ assertThat(actual).isNotEqualTo(matcher.group("val"));
+ return;
+ }
+
+ matcher = GT_MATCHER.matcher(expected);
+ if (matcher.find()) {
+ String val = matcher.group("val");
+
+ assertThat(val).isNotBlank();
+ assertThat(Double.parseDouble(actual)).isGreaterThan(Double.parseDouble(val));
+ return;
+ }
+
+ matcher = GE_MATCHER.matcher(expected);
+ if (matcher.find()) {
+ String val = matcher.group("val");
+
+ assertThat(val).isNotBlank();
+ assertThat(Double.parseDouble(actual)).isGreaterThanOrEqualTo(Double.parseDouble(val));
+ return;
+ }
+
+ assertThat(actual).isEqualTo(expected);
+ }
+
+}
diff --git a/test/e2e/e2e-base/src/main/resources/endpoints.gql b/test/e2e/e2e-base/src/main/resources/endpoints.gql
new file mode 100644
index 0000000000000000000000000000000000000000..c35cd32d554859024d694a544974925f9edd8df7
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/resources/endpoints.gql
@@ -0,0 +1,28 @@
+# 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.
+
+{
+ "query":"query queryEndpoints($serviceId: ID!, $keyword: String!) {
+ endpoints: searchEndpoint(serviceId: $serviceId, keyword: $keyword, limit: 100) {
+ key: id
+ label: name
+ }
+ }",
+ "variables": {
+ "serviceId": "{serviceId}",
+ "keyword": ""
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/resources/instances.gql b/test/e2e/e2e-base/src/main/resources/instances.gql
new file mode 100644
index 0000000000000000000000000000000000000000..949c793a4e302c907a40c92d4c01aa829537996b
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/resources/instances.gql
@@ -0,0 +1,36 @@
+# 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.
+
+{
+ "query":"query queryInstances($serviceId: ID!, $duration: Duration!) {
+ instances: getServiceInstances(duration: $duration, serviceId: $serviceId) {
+ key: id
+ label: name
+ attributes {
+ name
+ value
+ }
+ }
+ }",
+ "variables": {
+ "serviceId": "{serviceId}",
+ "duration": {
+ "start": "{start}",
+ "end": "{end}",
+ "step": "{step}"
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/resources/metrics.gql b/test/e2e/e2e-base/src/main/resources/metrics.gql
new file mode 100644
index 0000000000000000000000000000000000000000..38f72530bbd85b3abc7ebfda056afad481ac922a
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/resources/metrics.gql
@@ -0,0 +1,36 @@
+# 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.
+
+{
+ "query":"query ($id: ID!, $duration: Duration!) {
+ metrics: getLinearIntValues(metric: {
+ name: \"{metricsName}\"
+ id: $id
+ }, duration: $duration) {
+ values {
+ value
+ }
+ }
+ }",
+ "variables": {
+ "duration": {
+ "start": "{start}",
+ "end": "{end}",
+ "step": "{step}"
+ },
+ "id": "{id}"
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/resources/services.gql b/test/e2e/e2e-base/src/main/resources/services.gql
new file mode 100644
index 0000000000000000000000000000000000000000..1855d6ef68a43f6e7fbd3484fc1ec49965f9c00d
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/resources/services.gql
@@ -0,0 +1,31 @@
+# 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.
+
+{
+ "query": "query queryServices($duration: Duration!) {
+ services: getAllServices(duration: $duration) {
+ key: id
+ label: name
+ }
+ }",
+ "variables": {
+ "duration": {
+ "start": "{start}",
+ "end": "{end}",
+ "step": "{step}"
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/resources/topo.gql b/test/e2e/e2e-base/src/main/resources/topo.gql
new file mode 100644
index 0000000000000000000000000000000000000000..94febab8bf1a02f9115dc777bfe04d7674027edc
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/resources/topo.gql
@@ -0,0 +1,41 @@
+# 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.
+
+{
+ "query": "query queryTopo($duration: Duration!) {
+ topo: getGlobalTopology(duration: $duration) {
+ nodes {
+ id
+ name
+ type
+ isReal
+ }
+ calls {
+ id
+ source
+ detectPoints
+ target
+ }
+ }
+ }",
+ "variables": {
+ "duration": {
+ "start": "{start}",
+ "end": "{end}",
+ "step": "{step}"
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/main/resources/traces.gql b/test/e2e/e2e-base/src/main/resources/traces.gql
new file mode 100644
index 0000000000000000000000000000000000000000..a573f57badeae8e90877a5a6c00db6af00e74b7b
--- /dev/null
+++ b/test/e2e/e2e-base/src/main/resources/traces.gql
@@ -0,0 +1,42 @@
+# 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.
+
+{
+ "query": "query queryTraces($condition: TraceQueryCondition) {
+ traces: queryBasicTraces(condition: $condition) {
+ data: traces {
+ key: segmentId endpointNames duration start isError traceIds
+ }
+ total
+ }
+ }",
+ "variables": {
+ "condition": {
+ "queryDuration": {
+ "start": "{start}",
+ "end": "{end}",
+ "step": "{step}"
+ },
+ "traceState": "{traceState}",
+ "paging": {
+ "pageNum": {pageNum},
+ "pageSize": {pageSize},
+ "needTotal": {needTotal}
+ },
+ "queryOrder": "{queryOrder}"
+ }
+ }
+}
diff --git a/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestMatcher.java b/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..44e5f69cc90e4cbe7a65fd320edabd4554c4b738
--- /dev/null
+++ b/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestMatcher.java
@@ -0,0 +1,92 @@
+/*
+ * 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.skywalking.e2e;
+
+import org.apache.skywalking.e2e.trace.Trace;
+import org.apache.skywalking.e2e.trace.TraceMatcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author kezhenxu94
+ */
+public class TestMatcher {
+ private InputStream expectedInputStream;
+ private TraceMatcher traceMatcher;
+
+ @Before
+ public void setUp() throws IOException {
+ expectedInputStream = new ClassPathResource("test.yml").getInputStream();
+ traceMatcher = new Yaml().loadAs(expectedInputStream, TraceMatcher.class);
+ }
+
+ @Test
+ public void shouldSuccess() {
+ final Trace trace = new Trace()
+ .setKey("abc")
+ .setStart("1")
+ .setError(false);
+ trace.getEndpointNames().add("e2e/test");
+ trace.getTraceIds().add("id1");
+ trace.getTraceIds().add("id2");
+ traceMatcher.verify(trace);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void shouldVerifyNotNull() {
+ final Trace trace = new Trace()
+ .setStart("1")
+ .setError(false);
+ trace.getEndpointNames().add("e2e/test");
+ trace.getTraceIds().add("id1");
+ trace.getTraceIds().add("id2");
+ traceMatcher.verify(trace);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void shouldVerifyGreaterOrEqualTo() {
+ final Trace trace = new Trace()
+ .setKey("abc")
+ .setDuration(-1)
+ .setStart("1")
+ .setError(false);
+ trace.getEndpointNames().add("e2e/test");
+ trace.getTraceIds().add("id1");
+ trace.getTraceIds().add("id2");
+ traceMatcher.verify(trace);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void shouldVerifyGreaterThan() {
+ final Trace trace = new Trace()
+ .setKey("abc")
+ .setDuration(1)
+ .setStart("0")
+ .setError(false);
+ trace.getEndpointNames().add("e2e/test");
+ trace.getTraceIds().add("id1");
+ trace.getTraceIds().add("id2");
+ traceMatcher.verify(trace);
+ }
+}
diff --git a/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestMetricsMatcher.java b/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestMetricsMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..096032d5dd2485f593e602480cc1d34e097379d8
--- /dev/null
+++ b/test/e2e/e2e-base/src/test/java/org/apache/skywalking/e2e/TestMetricsMatcher.java
@@ -0,0 +1,52 @@
+/*
+ * 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.skywalking.e2e;
+
+import org.apache.skywalking.e2e.metrics.AtLeastOneOfMetricsMatcher;
+import org.apache.skywalking.e2e.metrics.Metrics;
+import org.apache.skywalking.e2e.metrics.MetricsValue;
+import org.apache.skywalking.e2e.metrics.MetricsValueMatcher;
+import org.junit.Test;
+
+/**
+ * @author kezhenxu94
+ */
+public class TestMetricsMatcher {
+ @Test
+ public void shouldVerifyOneOf() {
+ Metrics metrics = new Metrics();
+ metrics.getValues().add(new MetricsValue().setValue("12"));
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("gt 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(metrics);
+ }
+
+ @Test(expected = AssertionError.class)
+ public void shouldFailedVerifyOneOf() {
+ Metrics metrics = new Metrics();
+ metrics.getValues().add(new MetricsValue().setValue("0"));
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("gt 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(metrics);
+ }
+}
diff --git a/test/e2e/e2e-base/src/test/resources/test.yml b/test/e2e/e2e-base/src/test/resources/test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f5fe51ac7164a2294ddcfa4e4b002fac8e672991
--- /dev/null
+++ b/test/e2e/e2e-base/src/test/resources/test.yml
@@ -0,0 +1,31 @@
+# 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.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+key: not null
+endpointNames:
+ - e2e/test
+duration: ge 0
+start: gt 0
+isError: false
+traceIds:
+ - id1
+ - id2
diff --git a/test/e2e/e2e-single-service/pom.xml b/test/e2e/e2e-single-service/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..866ef62132cca16c78d07858ba0d8b4e37f691f5
--- /dev/null
+++ b/test/e2e/e2e-single-service/pom.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+ apache-skywalking-e2e
+ org.apache.skywalking
+ 1.0.0
+
+
+ 4.0.0
+
+ e2e-single-service
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+ ${spring.boot.version}
+
+
+ com.h2database
+ h2
+ ${h2.version}
+
+
+
+ org.apache.skywalking
+ e2e-base
+ ${project.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+ true
+ true
+ true
+
+
+
+
+ repackage
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+ %a-%t
+
+
+ skyapm/e2e-container:${e2e.container.version}
+ skywalking-e2e-container-${build.id}
+
+
+ ${project.build.finalName}.jar
+
+
+ +webapp.host:webapp.port:8080
+ +client.host:client.port:9090
+
+
+
+
+ ${project.basedir}/../../../dist/apache-skywalking-apm-bin:/skywalking
+
+
+ ${project.build.directory}:/home
+
+
+
+
+
+
+ http://${docker.host.address}:${client.port}/e2e/health-check
+
+ GET
+ 200
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ ${webapp.host}
+
+
+ ${webapp.port}
+
+
+ ${client.host}
+
+
+ ${client.port}
+
+
+
+
+
+
+ verify
+
+
+
+
+
+
+
diff --git a/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..a55dbd685769d4033780cfed97becf8b0258a097
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/SampleClientApplication.java
@@ -0,0 +1,34 @@
+/*
+ * 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.skywalking.e2e.sample.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+/**
+ * @author kezhenxu94
+ */
+@EnableJpaRepositories
+@SpringBootApplication
+public class SampleClientApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(SampleClientApplication.class, args);
+ }
+}
diff --git a/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfbe5a25258013b4c9e0ada0c6670bce8d0cbb0a
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/TestController.java
@@ -0,0 +1,48 @@
+/*
+ * 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.skywalking.e2e.sample.client;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author kezhenxu94
+ */
+@RestController
+@RequestMapping("/e2e")
+public class TestController {
+ private final UserRepo userRepo;
+
+ public TestController(final UserRepo userRepo) {
+ this.userRepo = userRepo;
+ }
+
+ @GetMapping("/health-check")
+ public String hello() {
+ return "healthy";
+ }
+
+ @PostMapping("/users")
+ public User createAuthor(@RequestBody final User user) {
+ return userRepo.save(user);
+ }
+}
diff --git a/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/User.java b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..4589c7f2c424632c9387f031bb2870329064869a
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/User.java
@@ -0,0 +1,56 @@
+/*
+ * 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.skywalking.e2e.sample.client;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author kezhenxu94
+ */
+@Entity
+public class User {
+ public User() {
+ }
+
+ @Id
+ @GeneratedValue
+ private Long id;
+
+ @Column
+ private String name;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(final Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+}
diff --git a/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/UserRepo.java b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/UserRepo.java
new file mode 100644
index 0000000000000000000000000000000000000000..882af92a82117e6250a626b870516fd5a64f606d
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/main/java/org/apache/skywalking/e2e/sample/client/UserRepo.java
@@ -0,0 +1,27 @@
+/*
+ * 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.skywalking.e2e.sample.client;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author kezhenxu94
+ */
+public interface UserRepo extends JpaRepository {
+}
diff --git a/test/e2e/e2e-single-service/src/main/resources/application.yml b/test/e2e/e2e-single-service/src/main/resources/application.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ef3ed01cd41127298e112d5fad0dae7714aadc3d
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/main/resources/application.yml
@@ -0,0 +1,35 @@
+# 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.
+
+server:
+ port: 9090
+
+spring:
+ main:
+ banner-mode: 'off'
+ datasource:
+ url: jdbc:h2:mem:testdb
+ driver-class-name: org.h2.Driver
+ data-username: sa
+ password: sa
+ platform: org.hibernate.dialect.H2Dialect
+ jpa:
+ generate-ddl: true
+ hibernate:
+ ddl-auto: create-drop
+ properties:
+ hibernate.format_sql: true
+ show-sql: true
diff --git a/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java b/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb0445930426dd0146c6be172a7890d8cc828311
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/test/java/org/apache/skywalking/e2e/SampleVerificationITCase.java
@@ -0,0 +1,292 @@
+/*
+ * 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.skywalking.e2e;
+
+import org.apache.skywalking.e2e.metrics.AtLeastOneOfMetricsMatcher;
+import org.apache.skywalking.e2e.metrics.Metrics;
+import org.apache.skywalking.e2e.metrics.MetricsQuery;
+import org.apache.skywalking.e2e.metrics.MetricsValueMatcher;
+import org.apache.skywalking.e2e.service.Service;
+import org.apache.skywalking.e2e.service.ServicesMatcher;
+import org.apache.skywalking.e2e.service.ServicesQuery;
+import org.apache.skywalking.e2e.service.endpoint.Endpoint;
+import org.apache.skywalking.e2e.service.endpoint.EndpointQuery;
+import org.apache.skywalking.e2e.service.endpoint.Endpoints;
+import org.apache.skywalking.e2e.service.endpoint.EndpointsMatcher;
+import org.apache.skywalking.e2e.service.instance.Instance;
+import org.apache.skywalking.e2e.service.instance.Instances;
+import org.apache.skywalking.e2e.service.instance.InstancesMatcher;
+import org.apache.skywalking.e2e.service.instance.InstancesQuery;
+import org.apache.skywalking.e2e.topo.TopoData;
+import org.apache.skywalking.e2e.topo.TopoMatcher;
+import org.apache.skywalking.e2e.topo.TopoQuery;
+import org.apache.skywalking.e2e.trace.Trace;
+import org.apache.skywalking.e2e.trace.TracesMatcher;
+import org.apache.skywalking.e2e.trace.TracesQuery;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.client.RestTemplate;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.InputStream;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ENDPOINT_P50;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ENDPOINT_P75;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ENDPOINT_P90;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ENDPOINT_P95;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.ENDPOINT_P99;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_INSTANCE_CPM;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_INSTANCE_RESP_TIME;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_INSTANCE_SLA;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_P50;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_P75;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_P90;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_P95;
+import static org.apache.skywalking.e2e.metrics.MetricsQuery.SERVICE_P99;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author kezhenxu94
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+public class SampleVerificationITCase {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SampleVerificationITCase.class);
+
+ private final RestTemplate restTemplate = new RestTemplate();
+
+ private SimpleQueryClient queryClient;
+ private String instrumentedServiceUrl;
+
+ @Before
+ public void setUp() {
+ final String swWebappHost = System.getProperty("sw.webapp.host", "127.0.0.1");
+ final String swWebappPort = System.getProperty("sw.webapp.port", "32783");
+ final String instrumentedServiceHost0 = System.getProperty("client.host", "127.0.0.1");
+ final String instrumentedServicePort0 = System.getProperty("client.port", "32782");
+ final String queryClientUrl = "http://" + swWebappHost + ":" + swWebappPort + "/graphql";
+ queryClient = new SimpleQueryClient(queryClientUrl);
+ instrumentedServiceUrl = "http://" + instrumentedServiceHost0 + ":" + instrumentedServicePort0;
+ }
+
+ @Test
+ @DirtiesContext
+ public void verify() throws Exception {
+ final LocalDateTime minutesAgo = LocalDateTime.now(ZoneOffset.UTC);
+
+ final Map user = new HashMap<>();
+ user.put("name", "SkyWalking");
+ final ResponseEntity responseEntity = restTemplate.postForEntity(
+ instrumentedServiceUrl + "/e2e/users",
+ user,
+ String.class
+ );
+ assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+ Thread.sleep(5000);
+
+ verifyTraces(minutesAgo);
+
+ verifyServices(minutesAgo);
+
+ verifyTopo(minutesAgo);
+ }
+
+ private void verifyTopo(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final TopoData topoData = queryClient.topo(
+ new TopoQuery()
+ .step("MINUTE")
+ .start(minutesAgo.minusDays(1))
+ .end(now)
+ );
+
+ InputStream expectedInputStream =
+ new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml").getInputStream();
+
+ final TopoMatcher topoMatcher = new Yaml().loadAs(expectedInputStream, TopoMatcher.class);
+ topoMatcher.verify(topoData);
+ }
+
+ private void verifyServices(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final List services = queryClient.services(
+ new ServicesQuery()
+ .start(minutesAgo)
+ .end(now)
+ );
+
+ InputStream expectedInputStream =
+ new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml").getInputStream();
+
+ final ServicesMatcher servicesMatcher = new Yaml().loadAs(expectedInputStream, ServicesMatcher.class);
+ servicesMatcher.verify(services);
+
+ for (Service service : services) {
+ LOGGER.info("verifying service instances: {}", service);
+
+ verifyServiceMetrics(service);
+
+ Instances instances = verifyServiceInstances(minutesAgo, now, service);
+
+ verifyInstancesMetrics(instances);
+
+ Endpoints endpoints = verifyServiceEndpoints(minutesAgo, now, service);
+
+ verifyEndpointsMetrics(endpoints);
+ }
+ }
+
+ private Instances verifyServiceInstances(LocalDateTime minutesAgo, LocalDateTime now, Service service) throws Exception {
+ InputStream expectedInputStream;
+ Instances instances = queryClient.instances(
+ new InstancesQuery()
+ .serviceId(service.getKey())
+ .start(minutesAgo)
+ .end(now)
+ );
+ expectedInputStream =
+ new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml").getInputStream();
+ final InstancesMatcher instancesMatcher = new Yaml().loadAs(expectedInputStream, InstancesMatcher.class);
+ instancesMatcher.verify(instances);
+ return instances;
+ }
+
+ private Endpoints verifyServiceEndpoints(LocalDateTime minutesAgo, LocalDateTime now, Service service) throws Exception {
+ Endpoints instances = queryClient.endpoints(
+ new EndpointQuery().serviceId(service.getKey())
+ );
+ InputStream expectedInputStream =
+ new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml").getInputStream();
+ final EndpointsMatcher endpointsMatcher = new Yaml().loadAs(expectedInputStream, EndpointsMatcher.class);
+ endpointsMatcher.verify(instances);
+ return instances;
+ }
+
+ private void verifyInstancesMetrics(Instances instances) throws Exception {
+ final String[] instanceMetricsNames = new String[] {
+ SERVICE_INSTANCE_RESP_TIME,
+ SERVICE_INSTANCE_CPM,
+ SERVICE_INSTANCE_SLA
+ };
+ for (Instance instance : instances.getInstances()) {
+ for (String metricsName : instanceMetricsNames) {
+ LOGGER.info("verifying service instance response time: {}", instance);
+ final Metrics instanceRespTime = queryClient.metrics(
+ new MetricsQuery()
+ .step("MINUTE")
+ .metricsName(metricsName)
+ .id(instance.getKey())
+ );
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("gt 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(instanceRespTime);
+ LOGGER.info("{}: {}", metricsName, instanceRespTime);
+ }
+ }
+ }
+
+ private void verifyEndpointsMetrics(Endpoints endpoints) throws Exception {
+ final String[] endpointMetricsNames = {
+ ENDPOINT_P99,
+ ENDPOINT_P95,
+ ENDPOINT_P90,
+ ENDPOINT_P75,
+ ENDPOINT_P50
+ };
+ for (Endpoint endpoint : endpoints.getEndpoints()) {
+ if (!endpoint.getLabel().equals("/e2e/users")) {
+ continue;
+ }
+ for (String metricName : endpointMetricsNames) {
+ LOGGER.info("verifying endpoint {}, metrics: {}", endpoint, metricName);
+ final Metrics metrics = queryClient.metrics(
+ new MetricsQuery()
+ .step("MINUTE")
+ .metricsName(metricName)
+ .id(endpoint.getKey())
+ );
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("gt 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(metrics);
+ LOGGER.info("metrics: {}", metrics);
+ }
+ }
+ }
+
+ private void verifyServiceMetrics(Service service) throws Exception {
+ final String[] serviceMetrics = {
+ SERVICE_P99,
+ SERVICE_P95,
+ SERVICE_P90,
+ SERVICE_P75,
+ SERVICE_P50
+ };
+ for (String metricName : serviceMetrics) {
+ LOGGER.info("verifying service {}, metrics: {}", service, metricName);
+ final Metrics instanceRespTime = queryClient.metrics(
+ new MetricsQuery()
+ .step("MINUTE")
+ .metricsName(metricName)
+ .id(service.getKey())
+ );
+ AtLeastOneOfMetricsMatcher instanceRespTimeMatcher = new AtLeastOneOfMetricsMatcher();
+ MetricsValueMatcher greaterThanZero = new MetricsValueMatcher();
+ greaterThanZero.setValue("gt 0");
+ instanceRespTimeMatcher.setValue(greaterThanZero);
+ instanceRespTimeMatcher.verify(instanceRespTime);
+ LOGGER.info("instanceRespTime: {}", instanceRespTime);
+ }
+ }
+
+ private void verifyTraces(LocalDateTime minutesAgo) throws Exception {
+ final LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
+
+ final List traces = queryClient.traces(
+ new TracesQuery()
+ .start(minutesAgo)
+ .end(now)
+ .orderByDuration()
+ );
+
+ InputStream expectedInputStream =
+ new ClassPathResource("expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml").getInputStream();
+
+ final TracesMatcher tracesMatcher = new Yaml().loadAs(expectedInputStream, TracesMatcher.class);
+ tracesMatcher.verify(traces);
+ }
+}
diff --git a/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a1f5b450af380a8b16eda893abf5d39d6adb0cb1
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.endpoints.yml
@@ -0,0 +1,27 @@
+# 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.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+endpoints:
+ - key: not null
+ label: /e2e/health-check
+ - key: not null
+ label: /e2e/users
diff --git a/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml
new file mode 100644
index 0000000000000000000000000000000000000000..26bb314d2b5d486a486b158349d80cde573e77a6
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.instances.yml
@@ -0,0 +1,34 @@
+# 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.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+instances:
+ - key: 2
+ label: not null
+ attributes:
+ - name: os_name
+ value: not null
+ - name: host_name
+ value: not null
+ - name: process_no
+ value: gt 0
+ - name: ipv4s
+ value: not null
diff --git a/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..07ff835878d8a6f111bce16ec743236184814220
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.services.yml
@@ -0,0 +1,25 @@
+# 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.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+services:
+ - key: 2
+ label: "Your_ApplicationName"
diff --git a/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3a455f5b60c8f29b41989fb8f2b5e5016fb9d249
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.topo.yml
@@ -0,0 +1,46 @@
+# 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.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+nodes:
+ - id: 1
+ name: User
+ type: USER
+ isReal: false
+ - id: 2
+ name: Your_ApplicationName
+ type: Tomcat
+ isReal: true
+ - id: 3
+ name: "localhost:-1"
+ type: H2
+ isReal: false
+calls:
+ - id: 2_3
+ source: 2
+ detectPoints:
+ - CLIENT
+ target: 3
+ - id: 1_2
+ source: 1
+ detectPoints:
+ - SERVER
+ target: 2
diff --git a/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2052aad3acd3776b972a62b321a77064cc278e4e
--- /dev/null
+++ b/test/e2e/e2e-single-service/src/test/resources/expected-data/org.apache.skywalking.e2e.SampleVerificationITCase.traces.yml
@@ -0,0 +1,31 @@
+# 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.
+
+# 1 health-check by docker-maven-plugin
+# 1 drop table if exists, because we have `ddl-auto: create-drop`
+# 1 drop sequence
+# 1 create sequence
+# 1 create table statement
+
+traces:
+ - key: not null
+ endpointNames:
+ - /e2e/users
+ duration: ge 0
+ start: gt 0
+ isError: false
+ traceIds:
+ - not null
diff --git a/test/e2e/pom.xml b/test/e2e/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f914a7361621d18faf0edebce756c8d0f302b4c2
--- /dev/null
+++ b/test/e2e/pom.xml
@@ -0,0 +1,169 @@
+
+
+
+
+
+ 4.0.0
+
+ org.apache.skywalking
+ apache-skywalking-e2e
+ 1.0.0
+
+ SkyWalking End to End Tests
+
+ pom
+
+
+ e2e-base
+ e2e-single-service
+
+
+
+ 1.8
+ ${java.version}
+ ${java.version}
+
+ UTF-8
+
+ 2.1.5.RELEASE
+ 2.9.7
+ 4.12
+ 2.9.7
+ 28.0-jre
+ 1.23
+ 2.8.5
+ 1.4.199
+
+
+ local
+
+ 1.0.0
+
+ 2.22.0
+ 3.8.0
+ 0.30.0
+
+
+
+
+ junit
+ junit
+ ${junit.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.boot.version}
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+
+ verify
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+ ${docker-maven-plugin.version}
+
+ all
+ true
+ default
+ IfNotPresent
+
+
+
+ start
+ pre-integration-test
+
+ start
+
+
+
+ stop
+ post-integration-test
+
+ stop
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${java.version}
+ ${java.version}
+ ${project.build.sourceEncoding}
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
+