From e2228e97671a498de60716a64f51a724ebe546bb Mon Sep 17 00:00:00 2001 From: Sergio Neves Barros Date: Sun, 6 Feb 2022 21:31:04 +0000 Subject: [PATCH] initial commit to setup delayed reporting which will send a single request to store the build --- .../angles/AnglesDelayedReporter.java | 80 +++++++++ .../angleshq/angles/AnglesReporter.java | 153 +++++++++++------- .../angleshq/angles/AnglesReporterEmpty.java | 10 ++ .../angles/AnglesReporterInterface.java | 4 + .../angleshq/angles/api/models/Platform.java | 4 +- .../angles/api/models/build/CreateBuild.java | 12 ++ .../angles/api/models/build/Suite.java | 7 +- .../angles/api/models/execution/Action.java | 3 +- .../api/models/execution/CreateExecution.java | 7 + .../api/models/execution/Execution.java | 9 +- .../angles/api/models/execution/Step.java | 3 +- 11 files changed, 229 insertions(+), 63 deletions(-) create mode 100644 angles-java-core/src/main/java/com/github/angleshq/angles/AnglesDelayedReporter.java diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesDelayedReporter.java b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesDelayedReporter.java new file mode 100644 index 0000000..69893c8 --- /dev/null +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesDelayedReporter.java @@ -0,0 +1,80 @@ +package com.github.angleshq.angles; + +import com.github.angleshq.angles.api.exceptions.AnglesServerException; +import com.github.angleshq.angles.api.models.build.Artifact; +import com.github.angleshq.angles.api.models.build.Build; +import com.github.angleshq.angles.api.models.build.CreateBuild; +import com.github.angleshq.angles.api.models.screenshot.Screenshot; +import com.github.angleshq.angles.api.models.screenshot.ScreenshotDetails; +import org.apache.commons.lang3.SerializationUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public class AnglesDelayedReporter extends AnglesReporter implements AnglesReporterInterface { + + protected InheritableThreadLocal currentCreateBuildRequest = new InheritableThreadLocal<>(); + + public AnglesDelayedReporter(String baseUrl) { + super(baseUrl); + } + + public void startBuild(String name, String environmentName, String teamName, String componentName, String phase) { + if (currentCreateBuildRequest.get() != null) { + return; + } + CreateBuild createBuild = createBuild(name, environmentName, teamName, componentName, phase); + currentCreateBuildRequest.set(createBuild); + } + + /** + * This method will save the store build + */ + @Override + public void saveBuild() { + try { + Build build = buildRequests.create(this.currentCreateBuildRequest.get()); + System.out.println(build.getId() + ": " + build.toString()); + } catch (IOException | AnglesServerException exception) { + throw new Error("Unable to save build due to [" + exception.getMessage() + "]"); + } + } + + @Override + public void storeArtifacts(Artifact[] artifacts) { + this.currentCreateBuildRequest.get().setArtifacts(Arrays.asList(artifacts)); + } + + @Override + public void startSuite(String suiteName) { + super.startSuite(suiteName); + this.currentCreateBuildRequest.get().addSuite(this.currentSuite.get()); + } + + @Override + public void startTest(String suiteName, String testName, String feature, List tags) { + super.startTest(suiteName, testName, feature, tags); + } + + /** + * Delayed reporter does not support screenshots. + * @param details + * @return + */ + public Screenshot storeScreenshot(ScreenshotDetails details) { + Screenshot screenshot = new Screenshot(); + screenshot.setId(null); + return screenshot; + } + + @Override + public void saveTest() { + // do nothing. + if (currentExecution.get() != null) { + this.currentSuite.get().getExecutions().add(SerializationUtils.clone(this.currentExecution.get())); + currentExecution.set(null); + } + } + +} diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporter.java b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporter.java index 94c9bef..7cc748f 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporter.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporter.java @@ -5,6 +5,7 @@ import com.github.angleshq.angles.api.models.build.Artifact; import com.github.angleshq.angles.api.models.build.Build; import com.github.angleshq.angles.api.models.build.CreateBuild; +import com.github.angleshq.angles.api.models.build.Suite; import com.github.angleshq.angles.api.models.execution.Action; import com.github.angleshq.angles.api.models.execution.CreateExecution; import com.github.angleshq.angles.api.models.execution.Step; @@ -15,10 +16,7 @@ import com.github.angleshq.angles.api.requests.*; import java.io.IOException; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -26,19 +24,20 @@ public class AnglesReporter implements AnglesReporterInterface { public static final String DEFAULT_ACTION_NAME = "Test Details"; public static final String EMPTY_REPORTER_NAME = "empty"; + private static Map reporterMap = new HashMap<>(); private static boolean enabled = true; - private String baseUrl; - private BuildRequests buildRequests; + private static boolean delayedReporting = true; + + protected String baseUrl; + protected BuildRequests buildRequests; private ExecutionRequests executionRequests; - private EnvironmentRequests environmentRequests; - private TeamRequests teamRequests; private ScreenshotRequests screenshotRequests; - private InheritableThreadLocal currentBuild = new InheritableThreadLocal<>(); - private InheritableThreadLocal currentExecution = new InheritableThreadLocal<>(); - private InheritableThreadLocal currentAction = new InheritableThreadLocal<>(); - private ThreadLocal setUpAction = new InheritableThreadLocal<>(); + protected InheritableThreadLocal currentBuild = new InheritableThreadLocal<>(); + protected InheritableThreadLocal currentExecution = new InheritableThreadLocal<>(); + protected InheritableThreadLocal currentAction = new InheritableThreadLocal<>(); + protected InheritableThreadLocal currentSuite = new InheritableThreadLocal<>(); public static AnglesReporterInterface getInstance(String url) { // if angles is disabled, return the empty reporter @@ -51,29 +50,23 @@ public static AnglesReporterInterface getInstance(String url) { // if not disabled then return the reporter. if (!AnglesReporter.reporterMap.containsKey(url)) { - AnglesReporter.reporterMap.put(url, new AnglesReporter(url)); + if (delayedReporting) { + AnglesReporter.reporterMap.put(url, new AnglesDelayedReporter(url)); + } else { + AnglesReporter.reporterMap.put(url, new AnglesReporter(url)); + } } return AnglesReporter.reporterMap.get(url); } - private AnglesReporter(String url) { + protected AnglesReporter(String url) { this.baseUrl = url; buildRequests = new BuildRequests(baseUrl); executionRequests = new ExecutionRequests(baseUrl); - environmentRequests = new EnvironmentRequests(baseUrl); - teamRequests = new TeamRequests(baseUrl); screenshotRequests = new ScreenshotRequests(baseUrl); } - - public synchronized void startBuild(String name, String environmentName, String teamName, String componentName) { - startBuild(name, environmentName, teamName, componentName, null); - } - - public synchronized void startBuild(String name, String environmentName, String teamName, String componentName, String phase) { - if (currentBuild.get() != null) { - return; - } + protected CreateBuild createBuild(String name, String environmentName, String teamName, String componentName, String phase) { CreateBuild createBuild = new CreateBuild(); createBuild.setName(name); createBuild.setEnvironment(environmentName); @@ -82,7 +75,43 @@ public synchronized void startBuild(String name, String environmentName, String createBuild.setStart(new Date()); if (!isBlank(phase)) createBuild.setPhase(phase); + return createBuild; + } + protected CreateExecution createExecution(String suiteName, String testName, String feature, List tags) { + CreateExecution createExecution = new CreateExecution(); + createExecution.setStart(new Date()); + if (currentBuild.get() != null) + createExecution.setBuild(currentBuild.get().getId()); + createExecution.setTitle(testName); + createExecution.setSuite(suiteName); + createExecution.setFeature(feature); + createExecution.setTags(tags); + return createExecution; + } + + protected Suite createSuite(String suiteName) { + Suite suite = new Suite(); + suite.setName(suiteName); + CreateExecution setupExecution = createExecution("Suite Setup", suiteName, null, null); + this.currentExecution.set(setupExecution); + suite.setSetup(setupExecution); + return suite; + } + + public synchronized void startBuild(String name, String environmentName, String teamName, String componentName) { + startBuild(name, environmentName, teamName, componentName, null); + } + + public void saveBuild() { + // do nothing; + } + + public synchronized void startBuild(String name, String environmentName, String teamName, String componentName, String phase) { + if (currentBuild.get() != null) { + return; + } + CreateBuild createBuild = createBuild(name, environmentName, teamName, componentName, phase); try { currentBuild.set(buildRequests.create(createBuild)); } catch (IOException | AnglesServerException exception) { @@ -91,13 +120,19 @@ public synchronized void startBuild(String name, String environmentName, String } public synchronized void storeArtifacts(Artifact[] artifacts) { - try { - currentBuild.set(buildRequests.artifacts(currentBuild.get().getId(), artifacts)); - } catch (IOException | AnglesServerException exception) { - throw new Error("Unable to store build artifacts due to [" + exception.getMessage() + "]"); + if (currentBuild.get().getArtifacts().size() == 0) { + try { + currentBuild.set(buildRequests.artifacts(currentBuild.get().getId(), artifacts)); + } catch (IOException | AnglesServerException exception) { + throw new Error("Unable to store build artifacts due to [" + exception.getMessage() + "]"); + } } } + public void startSuite(String suiteName) { + this.currentSuite.set(createSuite(suiteName)); + } + public void startTest(String suiteName, String testName) { startTest(suiteName, testName, null, null); } @@ -110,20 +145,11 @@ public void startTest(String suiteName, String testName, String feature) { startTest(suiteName, testName, feature, null); } - public void startTest(String suiteName, String testName, String feature, List tags) { - CreateExecution createExecution = new CreateExecution(); - createExecution.setStart(new Date()); - createExecution.setBuild(currentBuild.get().getId()); - createExecution.setTitle(testName); - createExecution.setSuite(suiteName); - createExecution.setFeature(feature); - createExecution.setTags(tags); - currentExecution.set(createExecution); - if (setUpAction.get() != null) { - // TODO: handle suite setup - // currentExecution.get().addAction(setUpAction.get()); - setUpAction.set(null); + public synchronized void startTest(String suiteName, String testName, String feature, List tags) { + if (this.currentSuite.get() == null) { + startSuite(suiteName); } + currentExecution.set(createExecution(suiteName, testName, feature, tags)); currentAction.set(null); } @@ -146,7 +172,13 @@ public void storePlatformDetails(Platform... platform) { public void startAction(String description) { this.currentAction.set(new Action(description)); - this.currentExecution.get().getActions().add(this.currentAction.get()); + if (currentExecution.get() == null) { + if (this.currentSuite.get() != null) { + this.currentSuite.get().getSetup().addAction(this.currentAction.get()); + } + } else { + this.currentExecution.get().getActions().add(this.currentAction.get()); + } } public void debug(String debug) { @@ -193,7 +225,7 @@ private void addStep(String name, String expected, String actual, String info, S addStep(name, expected, actual, info, status, null); } - private void addStep(String name, String expected, String actual, String info, StepStatus status, String screenshotId) { + protected void addStep(String name, String expected, String actual, String info, StepStatus status, String screenshotId) { Step step = new Step(); step.setTimestamp(new Date()); step.setStatus(status); @@ -204,20 +236,12 @@ private void addStep(String name, String expected, String actual, String info, S if (screenshotId !=null) { step.setScreenshot(screenshotId); } - // scenarios, 1. pre logs, 2. no action - if (currentExecution.get() == null) { - // test hasn't started yet (or maybe it has finished). - if (setUpAction.get() == null) { - this.setUpAction.set(new Action("Test-Setup")); - } - setUpAction.get().addStep(step); - } else { - // test has started - if (currentAction.get() == null) { - startAction(DEFAULT_ACTION_NAME); - } - currentAction.get().addStep(step); + + // test has started + if (currentAction.get() == null) { + startAction(DEFAULT_ACTION_NAME); } + currentAction.get().addStep(step); } public Screenshot storeScreenshot(ScreenshotDetails details) { @@ -272,4 +296,19 @@ public static boolean isEnabled() { return AnglesReporter.enabled; } + /** + * By setting this variable the Angles client will only send a single request to store the build (when saveBuild + * is called). This is useful for e.g. Unit tests who don't want to be delayed by the additional before/after tests + * calls to Angles. + * + * @param delayedReporting + */ + public static void setDelayedReporting(boolean delayedReporting) { + AnglesReporter.delayedReporting = delayedReporting; + } + + public static boolean isDelayedReporting() { + return delayedReporting; + } + } diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterEmpty.java b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterEmpty.java index 82c31f1..aed940b 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterEmpty.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterEmpty.java @@ -22,10 +22,20 @@ public void startBuild(String name, String environmentName, String teamName, Str // do nothing } + @Override + public void saveBuild() { + // do nothing + } + public void storeArtifacts(Artifact[] artifacts) { // do nothing } + @Override + public void startSuite(String suiteName) { + // do nothing + } + public void startTest(String suiteName, String testName) { // do nothing } diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterInterface.java b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterInterface.java index f3ba979..d29b24a 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterInterface.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/AnglesReporterInterface.java @@ -14,8 +14,12 @@ public interface AnglesReporterInterface { void startBuild(String name, String environmentName, String teamName, String componentName, String phaseName); + void saveBuild(); + void storeArtifacts(Artifact[] artifacts); + void startSuite(String suiteName); + void startTest(String suiteName, String testName); void updateTestName(String testName); diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/Platform.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/Platform.java index c8246ed..88b00f4 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/Platform.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/Platform.java @@ -4,8 +4,10 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.io.Serializable; + @Setter @Getter @NoArgsConstructor -public class Platform { +public class Platform implements Serializable { private String platformName; private String platformVersion; diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/CreateBuild.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/CreateBuild.java index c60e3fa..0d01798 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/CreateBuild.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/CreateBuild.java @@ -5,7 +5,9 @@ import lombok.Setter; import java.io.Serializable; +import java.util.ArrayList; import java.util.Date; +import java.util.List; @NoArgsConstructor @Getter @Setter public class CreateBuild implements Serializable { @@ -15,4 +17,14 @@ public class CreateBuild implements Serializable { private String phase; private Date start; private String component; + private List artifacts = new ArrayList<>(); + private List suites = new ArrayList<>(); + + public void addArtifact(Artifact artifact) { + this.artifacts.add(artifact); + } + + public void addSuite(Suite suite) { + this.suites.add(suite); + } } diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/Suite.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/Suite.java index 0c16005..54cfcef 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/Suite.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/build/Suite.java @@ -1,5 +1,6 @@ package com.github.angleshq.angles.api.models.build; +import com.github.angleshq.angles.api.models.execution.CreateExecution; import com.github.angleshq.angles.api.models.execution.Execution; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,9 +12,11 @@ @Getter @Setter @NoArgsConstructor public class Suite { private String name; - private List executions = new ArrayList<>(); + private CreateExecution setup; + private CreateExecution teardown; + private List executions = new ArrayList<>(); - public void addExecution(Execution execution) { + public void addExecution(CreateExecution execution) { this.executions.add(execution); } } diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Action.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Action.java index 48de4fd..90fab59 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Action.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Action.java @@ -3,11 +3,12 @@ import lombok.Getter; import lombok.Setter; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @Getter @Setter -public class Action { +public class Action implements Serializable { private String name; private List steps = new ArrayList(); diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/CreateExecution.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/CreateExecution.java index 5c4fcad..8fa3bc8 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/CreateExecution.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/CreateExecution.java @@ -25,6 +25,13 @@ public class CreateExecution implements Serializable { private List actions = new CopyOnWriteArrayList<>(); private List platforms = new ArrayList<>(); + public CreateExecution(String build, String title, String suite, Date start) { + this.build = build; + this.title = title; + this.suite = suite; + this.start = start; + } + public void addTag(String tag) { this.tags.add(tag); } diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Execution.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Execution.java index e20f9de..3dae9cc 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Execution.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Execution.java @@ -21,7 +21,14 @@ public class Execution extends BaseModel { private List actions = new ArrayList<>(); private List platforms = new ArrayList<>(); - public Execution(String build, String title, String suite, String feature,Date start) { + public Execution(String build, String title, String suite, Date start) { + this.build = build; + this.title = title; + this.suite = suite; + this.start = start; + } + + public Execution(String build, String title, String suite, String feature, Date start) { this.build = build; this.title = title; this.suite = suite; diff --git a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Step.java b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Step.java index b5ad4cb..16d7a44 100644 --- a/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Step.java +++ b/angles-java-core/src/main/java/com/github/angleshq/angles/api/models/execution/Step.java @@ -5,10 +5,11 @@ import lombok.NoArgsConstructor; import lombok.Setter; +import java.io.Serializable; import java.util.Date; @Setter @Getter @NoArgsConstructor -public class Step { +public class Step implements Serializable { private String name; private String expected;