Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add info about test failures #64

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ STATUS_TOKEN=<TOKEN>

The token only needs read access to the repository.

A lot of API calls are made to gather flaky tests statistics. If you would like to
use a different API token for it, you can add it to the .env file:
```
FLAKY_TESTS_TOKEN=<ANOTHER_TOKEN>
```

If not provided, the `STATUS_TOKEN` will be used.

**NOTE** in dev mode, gathering flaky tests is mocked.

## Running the application in dev mode

You can run your application in dev mode that enables live coding using:
Expand Down
43 changes: 43 additions & 0 deletions ddl.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
CREATE SEQUENCE hibernate_sequence
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER TABLE hibernate_sequence OWNER TO quarkus;

SET default_tablespace = '';

SET default_table_access_method = heap;

CREATE TABLE testexecution (
id bigint NOT NULL,
successful boolean NOT NULL,
testname character varying(255),
job_id bigint
);

ALTER TABLE testexecution OWNER TO quarkus;

CREATE TABLE testjob (
id bigint NOT NULL,
completedat timestamp without time zone,
name character varying(255),
sha character varying(255),
url character varying(255)
);

ALTER TABLE testjob OWNER TO quarkus;

ALTER TABLE ONLY testexecution
ADD CONSTRAINT testexecution_pkey PRIMARY KEY (id);

ALTER TABLE ONLY testjob
ADD CONSTRAINT testjob_pkey PRIMARY KEY (id);

ALTER TABLE ONLY testexecution
ADD CONSTRAINT fkptkmwmynles1g3xwdo7iudrun FOREIGN KEY (job_id) REFERENCES testjob(id);

create index on testexecution (testname, id desc, successful);
create index on testexecution using btree (testname);
16 changes: 16 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@
<artifactId>quarkus-prettytime</artifactId>
<version>0.1.1</version>
</dependency>

<!-- database: -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/io/quarkus/status/StatusResource.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package io.quarkus.status;

import java.io.IOException;
import java.util.List;

import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import io.quarkus.qute.CheckedTemplate;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.status.flaky.TestExecution;
import io.quarkus.status.model.Stats;
import io.quarkus.status.model.Status;

Expand All @@ -29,6 +34,8 @@ public class StatusResource {
public static class Templates {
public static native TemplateInstance index(Status status);
public static native TemplateInstance issues(Status status, Stats stats, boolean isBugs);
public static native TemplateInstance tests(Status status);
public static native TemplateInstance testResults(Status status, List<TestExecution> executions);
}

@GET
Expand All @@ -44,6 +51,24 @@ public TemplateInstance bugs() throws IOException {
return Templates.issues(statusService.getStatus(), issuesService.getBugsMonthlyStats(), true);
}

@GET
@Path("tests")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance tests() throws IOException {
return Templates.tests(statusService.getStatus());
}

@GET
@Path("test-details/{testName}")
@Produces(MediaType.TEXT_HTML)
public TemplateInstance testResults(@PathParam("testName") String testName,
@QueryParam("page") @DefaultValue("0") Integer page,
@QueryParam("pageSize") @DefaultValue("40") Integer pageSize) throws IOException {
List<TestExecution> executions = TestExecution.find("testName = ?1 ORDER BY id DESC", testName)
.page(page, pageSize).list();
return Templates.testResults(statusService.getStatus(), executions);
}

@GET
@Path("enhancements")
@Produces(MediaType.TEXT_HTML)
Expand Down
105 changes: 105 additions & 0 deletions src/main/java/io/quarkus/status/flaky/TestDataInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package io.quarkus.status.flaky;

import io.quarkus.arc.profile.UnlessBuildProfile;
import io.quarkus.runtime.StartupEvent;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.transaction.Transactional;

import java.util.Date;
import java.util.Random;

import static io.quarkus.status.flaky.TestStatisticsResource.RESULT_GROUP_SIZE;

@ApplicationScoped
@UnlessBuildProfile("prod")
public class TestDataInitializer {
@Transactional
public void setUp(@Observes StartupEvent anything) {
TestJob testJob = new TestJob();
testJob.name = "JVM tests on Windows";
testJob.url = "https://example.com/test-result";
testJob.completedAt = new Date();
testJob.sha = "093245802938402934902341";
TestJob.persist(testJob);

for (int i = 0; i < 10; i++) {
TestExecution execution = new TestExecution();
execution.job = testJob;
execution.testName = "com.example.restclient.it.SomeTest.alwaysFailingTest";
execution.successful = false;
TestExecution.persist(execution);
}
for (int i = 0; i < 10; i++) {
TestExecution execution = new TestExecution();
execution.job = testJob;
execution.testName = "always.succeeding.test";
execution.successful = true;
TestExecution.persist(execution);
}


for (int i = 0; i < RESULT_GROUP_SIZE; i++) {
TestExecution execution = new TestExecution();
execution.job = testJob;
execution.testName = "half.failing.test";
execution.successful = (i % 2) == 0;
TestExecution.persist(execution);
}
for (int i = 0; i < RESULT_GROUP_SIZE * 2; i++) {
TestExecution execution = new TestExecution();
execution.job = testJob;
execution.testName = "test.failing.in.the.past";
execution.successful = i >= RESULT_GROUP_SIZE;
TestExecution.persist(execution);
}
for (int i = 0; i < RESULT_GROUP_SIZE * 2; i++) {
TestExecution execution = new TestExecution();
execution.job = testJob;
execution.testName = "test.successful.in.the.past";
execution.successful = i <= RESULT_GROUP_SIZE;
TestExecution.persist(execution);
}
}

@Inject
PerfTestInitializer perfTestInitializer;

public void createDataForPerfTest(@Observes StartupEvent anything) {
if (!Boolean.TRUE.toString().equalsIgnoreCase(System.getenv("generate_perf_test_data"))) {
return;
}
Random r = new Random();
for (int i = 0; i < 600; i++) {
String testName = "test.successful.in.the.past" + r.nextInt();
perfTestInitializer.initTestResults(testName);
}
System.out.println("\ndone");
}

@ApplicationScoped
public static class PerfTestInitializer {

public static final int TEST_ROWS_FOR_TEST = 50;

@Transactional
void initTestResults(String testName) {
TestJob testJob = new TestJob();
testJob.name = "JVM tests on Linux";
testJob.url = "https://example.com/test-result/2";
testJob.completedAt = new Date();
testJob.sha = "123132423412313242341";
TestJob.persist(testJob);
Random r = new Random();
for (int i = 0; i < TEST_ROWS_FOR_TEST; i++) {
TestExecution execution = new TestExecution();
execution.job = testJob;
execution.testName = testName;
execution.successful = r.nextDouble() > 0.1;
TestExecution.persist(execution);
}
}
}
}
29 changes: 29 additions & 0 deletions src/main/java/io/quarkus/status/flaky/TestExecution.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.quarkus.status.flaky;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(indexes = {
@Index(columnList = "testName"),
@Index(columnList = "testName, id desc, successful")
})
public class TestExecution extends PanacheEntity {
public String testName;
public boolean successful;
@ManyToOne
public TestJob job;

@Override
public String toString() {
return "TestExecution{" +
"testName='" + testName + '\'' +
", successful=" + successful +
", id=" + id +
'}';
}
}
14 changes: 14 additions & 0 deletions src/main/java/io/quarkus/status/flaky/TestJob.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.status.flaky;

import io.quarkus.hibernate.orm.panache.PanacheEntity;

import javax.persistence.Entity;
import java.util.Date;

@Entity
public class TestJob extends PanacheEntity {
public String url;
public String name;
public Date completedAt;
public String sha;
}
48 changes: 48 additions & 0 deletions src/main/java/io/quarkus/status/flaky/TestReportFeedingTube.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.status.flaky;

import io.quarkus.status.flaky.TestExecution;
import io.quarkus.status.flaky.TestJob;
import io.quarkus.status.flaky.feeding.JobResult;
import io.quarkus.status.flaky.feeding.TestResultDto;
import io.quarkus.status.flaky.feeding.WorkflowResult;

import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("/test-results")
@RolesAllowed("quarkus-bot")
public class TestReportFeedingTube {

@Inject
TestStatisticsResource statisticsResource;

@POST
@Transactional
public Response storeTestResults(WorkflowResult results) {
for (JobResult job : results.getJobs()) {
var testJob = new TestJob();
testJob.url = job.getJobUrl();
testJob.name = job.getJobName();
testJob.completedAt = job.getCompletedAt();
testJob.sha = results.getSha();
TestJob.persist(testJob);

for (TestResultDto test : job.getTests()) {
var testExecution = new TestExecution();
testExecution.testName = test.getName();
testExecution.successful = test.isSuccessful();
testExecution.job = testJob;

TestExecution.persist(testExecution);
}
}

statisticsResource.clearCache();

return Response.ok().build();
}
}
Loading