diff --git a/common/src/main/java/org/wso2/testgrid/common/TestGridConstants.java b/common/src/main/java/org/wso2/testgrid/common/TestGridConstants.java index 5a5f8c787..96ffdb5b8 100644 --- a/common/src/main/java/org/wso2/testgrid/common/TestGridConstants.java +++ b/common/src/main/java/org/wso2/testgrid/common/TestGridConstants.java @@ -71,6 +71,7 @@ public class TestGridConstants { public static final String TEST_PLANS_URI = "test-plans"; public static final String HTML_LINE_SEPARATOR = "
"; public static final String TESTGRID_EMAIL_REPORT_NAME = "EmailReport.html"; + public static final String TESTGRID_SUMMARIZED_EMAIL_REPORT_NAME = "SummarizedEmailReport.html"; public static final String SHELL_SUFFIX = ".sh"; public static final String PRE_STRING = "pre-scenario-steps"; diff --git a/common/src/main/java/org/wso2/testgrid/common/util/FileUtil.java b/common/src/main/java/org/wso2/testgrid/common/util/FileUtil.java index 748b899b6..c21046894 100644 --- a/common/src/main/java/org/wso2/testgrid/common/util/FileUtil.java +++ b/common/src/main/java/org/wso2/testgrid/common/util/FileUtil.java @@ -19,15 +19,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.wso2.testgrid.common.TestPlan; import org.wso2.testgrid.common.exception.TestGridException; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; @@ -38,12 +42,18 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import static org.wso2.testgrid.common.TestGridConstants.PRODUCT_TEST_PLANS_DIR; + /** * Utility class for handling file operations. * @@ -162,4 +172,83 @@ public static void compress(String sourceDir, String destination) throws IOExcep } } + /** + * Writes a given string to a given file to persistent media. + * + * @param filePath absolute path of the file to be written + * @param string string to be written + * @throws TestGridException thrown on {@link FileNotFoundException} or {@link UnsupportedEncodingException} + */ + public static void writeToFile(String filePath, String string) throws TestGridException { + createFileIfNotExists(filePath); // Create file if not exists + try (PrintWriter writer = new PrintWriter(filePath, StandardCharsets.UTF_8.name())) { + writer.write(string); + } catch (FileNotFoundException e) { + throw new TestGridException(String.format(Locale.ENGLISH, "File %s not found", filePath), e); + } catch (UnsupportedEncodingException e) { + throw new TestGridException( + String.format(Locale.ENGLISH, "Unsupported encoding %s", StandardCharsets.UTF_8.name()), e); + } + } + + /** + * Creates a file with the given name. + * + * @param filePath absolute path of the file + * @throws TestGridException thrown when IO exception on creating a file + */ + private static void createFileIfNotExists(String filePath) throws TestGridException { + File file = new File(filePath); + if (!file.exists()) { + // Create directories if not exists + Path parent = Paths.get(filePath).getParent(); + + if (parent != null) { + boolean status = new File(parent.toAbsolutePath().toString()).mkdirs(); + + if (status) { + // Touch file + try { + new FileOutputStream(file).close(); + } catch (IOException e) { + throw new TestGridException(String.format(Locale.ENGLISH, + "IO Exception occurred when creating file %s", file), e); + } + } + } + } + } + + /** + * Get test plan ids by reading testgrid yaml files contains in the testgrid home. + * + * @param workspace path of the current workspace + * @throws TestGridException thrown when IO exception on reading testgrid yaml files. + */ + public static List getTestPlanIdByReadingTGYaml(String workspace) throws TestGridException { + List testPlanIds = new ArrayList<>(); + Path source = Paths.get(workspace, PRODUCT_TEST_PLANS_DIR); + if (!Files.exists(source)) { + logger.error("Test-plans dir does not exist: " + source); + return Collections.emptyList(); + } + try (Stream stream = Files.list(source).filter(Files::isRegularFile)) { + List paths = stream.sorted().collect(Collectors.toList()); + for (Path path : paths) { + if (!path.toFile().exists()) { + throw new IOException( + "Test Plan File doesn't exist. File path is " + path.toAbsolutePath().toString()); + } + logger.info("A test plan file found at " + path.toAbsolutePath().toString()); + TestPlan testPlanYaml = org.wso2.testgrid.common.util.FileUtil + .readYamlFile(path.toAbsolutePath().toString(), TestPlan.class); + testPlanIds.add(testPlanYaml.getId()); + } + return testPlanIds; + } catch (IOException e) { + throw new TestGridException("Error occurred while reading the test-plan yamls in workspace " + workspace, + e); + } + + } } diff --git a/core/src/main/java/org/wso2/testgrid/core/command/GenerateEmailCommand.java b/core/src/main/java/org/wso2/testgrid/core/command/GenerateEmailCommand.java index eb7d0aef2..ed7a9167f 100644 --- a/core/src/main/java/org/wso2/testgrid/core/command/GenerateEmailCommand.java +++ b/core/src/main/java/org/wso2/testgrid/core/command/GenerateEmailCommand.java @@ -56,8 +56,14 @@ public void execute() throws CommandExecutionException { Product product = getProduct(productName); try { TestReportEngine testReportEngine = new TestReportEngine(); - final Optional path = testReportEngine.generateEmailReport(product, workspace); - path.ifPresent(p -> logger.info("Written the email report body contents to: " + p)); + // Generating the summary report + final Optional summarizedReportPath = testReportEngine.generateSummarizedEmailReport(product, + workspace); + summarizedReportPath.ifPresent(p -> logger.info("Written the summarized email " + + "report body contents to: " + p)); + + final Optional reportPath = testReportEngine.generateEmailReport(product, workspace); + reportPath.ifPresent(p -> logger.info("Written the email report body contents to: " + p)); } catch (ReportingException e) { throw new CommandExecutionException(StringUtil .concatStrings("Error occurred when generating email report for {" + diff --git a/dao/src/main/java/org/wso2/testgrid/dao/dto/TestCaseFailureResultDTO.java b/dao/src/main/java/org/wso2/testgrid/dao/dto/TestCaseFailureResultDTO.java new file mode 100644 index 000000000..0e223b1ef --- /dev/null +++ b/dao/src/main/java/org/wso2/testgrid/dao/dto/TestCaseFailureResultDTO.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.dao.dto; + +/** + * Defines a model object of test case failure results. + * + * @since 1.0.0 + */ +public class TestCaseFailureResultDTO { + + private String name; + private String failureMessage; + private String infraParameters; + + public TestCaseFailureResultDTO(String testname, String failureMessage, String infraParameters) { + this.name = testname; + this.failureMessage = failureMessage; + this.infraParameters = infraParameters; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFailureMessage() { + return failureMessage; + } + + public void setFailureMessage(String failureMessage) { + this.failureMessage = failureMessage; + } + + public String getInfraParameters() { + return infraParameters; + } + + public void setInfraParameters(String infraParameters) { + this.infraParameters = infraParameters; + } +} diff --git a/dao/src/main/java/org/wso2/testgrid/dao/repository/TestPlanRepository.java b/dao/src/main/java/org/wso2/testgrid/dao/repository/TestPlanRepository.java index 7e8866400..214abe420 100644 --- a/dao/src/main/java/org/wso2/testgrid/dao/repository/TestPlanRepository.java +++ b/dao/src/main/java/org/wso2/testgrid/dao/repository/TestPlanRepository.java @@ -24,9 +24,14 @@ import org.wso2.testgrid.dao.EntityManagerHelper; import org.wso2.testgrid.dao.SortOrder; import org.wso2.testgrid.dao.TestGridDAOException; +import org.wso2.testgrid.dao.dto.TestCaseFailureResultDTO; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.sql.Timestamp; +import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; @@ -290,4 +295,120 @@ public List getTestPlanOlderThan(String duration, String timeUnit) { .getResultList(); return EntityManagerHelper.refreshResultList(entityManager, resultList); } + + /** + * This method returns test failure summary for given test plan ids. (i.e for a given build job). + * + * @param testPlanIds test plan ids + * @return a List of {@link TestCaseFailureResultDTO} representing the test case failure results for a given build + */ + public List getTestFailureSummaryByTPId(List testPlanIds) + throws TestGridDAOException { + StringBuilder sql = new StringBuilder("select failed_tc.test_name as name, failed_tc.failure_message as " + + "failureMessage, tp.infra_parameters as infraParametrs from test_plan tp join (select tc" + + ".test_name, tc.failure_message, ts.TESTPLAN_id from test_case tc inner join test_scenario ts on " + + "ts.id=tc.TESTSCENARIO_id and tc.status = 'FAIL' and ts.TESTPLAN_id in ("); + for (int i = 0; i < testPlanIds.size() - 1; i++) { + sql.append("?, "); + } + sql.append("?)) failed_tc on tp.id = failed_tc.TESTPLAN_id;"); + Query query = entityManager.createNativeQuery(sql.toString()); + int index = 1; + for (String s : testPlanIds) { + query.setParameter(index++, s); + } + return getResultList(query, TestCaseFailureResultDTO.class); + } + + /** + * This method returns the test execution summary for given test plan ids(i.e for a given build job). + * + * @param testPlanIds test plan ids of a specific build job + * @return a List of {@link String} representing statuses of given test plans + */ + public List getTestPlanStatuses(List testPlanIds) { + StringBuilder sql = new StringBuilder("select status from test_plan where id in ("); + for (int i = 0; i < testPlanIds.size() - 1; i++) { + sql.append("?, "); + } + sql.append("?);"); + Query query = entityManager.createNativeQuery(sql.toString()); + int index = 1; + for (String s : testPlanIds) { + query.setParameter(index++, s); + } + @SuppressWarnings("unchecked") + List statuses = (List) query.getResultList(); + return statuses; + } + + /** + * This method returns the history of test execution summary for given product. + * + * @param productId id of the product + * @param from starting point of the considering time range + * @param to end point of the considering time range + * @return a List of {@link TestPlan} representing executed test plans of a given product for a given time range + */ + public List getTestExecutionHistory(String productId, String from, String to) { + String sql = "select tp.* from test_plan tp inner join (Select distinct infra_parameters from test_plan where " + + "DEPLOYMENTPATTERN_id in (select id from deployment_pattern where PRODUCT_id=?)) as rn on " + + "tp.infra_parameters=rn.infra_parameters and tp.DEPLOYMENTPATTERN_id " + + "in (select id from deployment_pattern where PRODUCT_id=?) and modified_timestamp between ? and ?;"; + + @SuppressWarnings("unchecked") + List resultList = (List) entityManager.createNativeQuery(sql, TestPlan.class) + .setParameter(1, productId) + .setParameter(2, productId) + .setParameter(3, from) + .setParameter(4, to) + .getResultList(); + return EntityManagerHelper.refreshResultList(entityManager, resultList); + } + + /** + * This method is responsible to map list of objects to a given class. + * + * @param type Mapping class + * @param records lst of objects that are mapping to instance of the given class + * @return a List of mapped objects + */ + public static List mapObject(Class type, List records) throws TestGridDAOException { + List result = new LinkedList<>(); + for (Object[] record : records) { + List> tupleTypes = new ArrayList<>(); + for (Object field : record) { + //if a filed contains null value assign empty string. If null values in the either infra_combination + // column or test case name column or test case description column, null value could be passed to here. + if (field == null) { + field = ""; + } + tupleTypes.add(field.getClass()); + } + Constructor ctor; + try { + ctor = type.getConstructor(tupleTypes.toArray(new Class[record.length])); + result.add(ctor.newInstance(record)); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | + InstantiationException e) { + throw new TestGridDAOException("Error occured while mapping object to TestCaseFailureResultDTO object", + e); + } + + } + return result; + } + + /** + * This method is responsible to execute the native query and return mapped oject to a given class. + * + * @param query native query + * @param type class of the mapping object + * @return a List of mapped objects + */ + private static List getResultList(Query query, Class type) throws TestGridDAOException { + @SuppressWarnings("unchecked") + List records = query.getResultList(); + return mapObject(type, records); + } } diff --git a/dao/src/main/java/org/wso2/testgrid/dao/uow/TestCaseUOW.java b/dao/src/main/java/org/wso2/testgrid/dao/uow/TestCaseUOW.java index 281d91c88..09d6cfc15 100644 --- a/dao/src/main/java/org/wso2/testgrid/dao/uow/TestCaseUOW.java +++ b/dao/src/main/java/org/wso2/testgrid/dao/uow/TestCaseUOW.java @@ -79,7 +79,7 @@ public Optional getTestCaseById(String id) throws TestGridDAOException @SuppressWarnings("unchecked") public boolean isExistsFailedTests(TestScenario testScenario) throws TestGridDAOException { List resultObject = testCaseRepository.executeTypedQuery("SELECT * FROM test_case " - + "WHERE TESTSCENARIO_id = '" + testScenario.getId() + "' AND is_success = FALSE;"); + + "WHERE TESTSCENARIO_id = '" + testScenario.getId() + "' AND status = FAIL;"); return resultObject.isEmpty(); } diff --git a/dao/src/main/java/org/wso2/testgrid/dao/uow/TestPlanUOW.java b/dao/src/main/java/org/wso2/testgrid/dao/uow/TestPlanUOW.java index 4d093382d..97f19e865 100644 --- a/dao/src/main/java/org/wso2/testgrid/dao/uow/TestPlanUOW.java +++ b/dao/src/main/java/org/wso2/testgrid/dao/uow/TestPlanUOW.java @@ -22,6 +22,7 @@ import org.wso2.testgrid.common.TestPlan; import org.wso2.testgrid.dao.EntityManagerHelper; import org.wso2.testgrid.dao.TestGridDAOException; +import org.wso2.testgrid.dao.dto.TestCaseFailureResultDTO; import org.wso2.testgrid.dao.repository.TestPlanRepository; import java.sql.Timestamp; @@ -180,4 +181,32 @@ public List getTestPlanHistory(TestPlan testPlan) { public List getTestPlansOlderThan(String duration, String timeUnit) { return testPlanRepository.getTestPlanOlderThan(duration, timeUnit); } + + /** + * Returns the test plan statuses for given test plan ids. + * + * @return a List of Test Plan statuses. + */ + public List getTestExecutionSummary(List tpIds) { + return testPlanRepository.getTestPlanStatuses(tpIds); + } + + /** + * Returns the representation of failed test cases with test case name, description and infra combination for given + * test plan ids. (I.e for a given build job) + * + * @return a List of TestCaseFailureResultDTO which represent test cases failure for given test plan ids. + */ + public List getTestFailureSummary(List tpIds) throws TestGridDAOException { + return testPlanRepository.getTestFailureSummaryByTPId(tpIds); + } + + /** + * Returns the representation of test execution history for q given product in a given time range + * + * @return a List of TestPlan which represent executed test plans in the given time range for a given product. + */ + public List getTestExecutionHistory(String productId, String from, String to) { + return testPlanRepository.getTestExecutionHistory(productId, from, to); + } } diff --git a/reporting/pom.xml b/reporting/pom.xml index fe3377a33..520659ee3 100755 --- a/reporting/pom.xml +++ b/reporting/pom.xml @@ -100,5 +100,9 @@ org.jacoco.agent runtime + + com.google.code.gson + gson + \ No newline at end of file diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/ChartGenerator.java b/reporting/src/main/java/org/wso2/testgrid/reporting/ChartGenerator.java new file mode 100644 index 000000000..4f537fdc4 --- /dev/null +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/ChartGenerator.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.reporting; + +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.embed.swing.JFXPanel; +import javafx.embed.swing.SwingFXUtils; +import javafx.scene.Scene; +import javafx.scene.chart.CategoryAxis; +import javafx.scene.chart.Chart; +import javafx.scene.chart.NumberAxis; +import javafx.scene.chart.PieChart; +import javafx.scene.chart.StackedBarChart; +import javafx.scene.chart.XYChart; +import javafx.scene.image.WritableImage; +import javafx.stage.Stage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.wso2.testgrid.reporting.model.email.BuildExecutionSummary; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Map; +import javax.imageio.ImageIO; + +/** + * This class is responsible for generating the necessary charts for the email report. + * + * @since 1.0.0 + */ +public class ChartGenerator { + + private static final Logger logger = LoggerFactory.getLogger(ChartGenerator.class); + private static final String summaryChartFileName = "summary.png"; + private static final String historyChartFileName = "history.png"; + private String chartGenLocation; + + public ChartGenerator(String chartGenLocation) { + new JFXPanel(); + this.chartGenLocation = chartGenLocation; + } + + /** + * Generates a pie chart with the summary test results of the current build. + * + * @param passedCount passed test count + * @param failedCount failed test count + * @param skippedCount skipped test count + * @throws IOException if the chart cannot be written into a file + */ + public void generateSummaryChart(int passedCount, int failedCount, int skippedCount) { + + ObservableList pieChartData = + FXCollections.observableArrayList( + new PieChart.Data("Failed", failedCount), + new PieChart.Data("Skipped", skippedCount), + new PieChart.Data("Passed", passedCount)); + final PieChart chart = new PieChart(pieChartData); + chart.setAnimated(false); + chart.setTitle("Build Failure Summary by Test Plans"); + genChart(chart, 500, 500, summaryChartFileName); + } + + /** + * Generates the history chart with the summary of test executions. + * + * @param dataSet input data-set for the chart + */ + public void generateResultHistoryChart(Map dataSet) { + + final CategoryAxis xAxis = new CategoryAxis(); + final NumberAxis yAxis = new NumberAxis(); + final StackedBarChart stackedBarChart = new StackedBarChart<>(xAxis, yAxis); + // This represents the series of values e.g: Failed, skipped, Passed + final XYChart.Series[] seriesSet = new XYChart.Series[]{new XYChart.Series<>(), + new XYChart.Series<>(), new XYChart.Series<>()}; + + xAxis.setCategories(FXCollections.observableArrayList(dataSet.keySet())); + // Disabling animation + xAxis.setAnimated(false); + yAxis.setAnimated(false); + stackedBarChart.setAnimated(false); + // Set Axis Names + xAxis.setLabel("Build Timestamp"); + yAxis.setLabel("Number of Infra Combinations"); + + // Setting series names + seriesSet[0].setName("Build Failed Combinations"); + seriesSet[1].setName("Build Passed Combinations"); + seriesSet[2].setName("Infra Failed Combinations"); + // Setting space between the bars + stackedBarChart.setCategoryGap(50); + //Setting the title of the bar chart. + stackedBarChart.setTitle("History of test execution summary"); + + dataSet.forEach((key, summary) -> { + seriesSet[0].getData().add(new XYChart.Data<>(key, summary.getFailedTestPlans())); + seriesSet[1].getData().add(new XYChart.Data<>(key, summary.getPassedTestPlans())); + seriesSet[2].getData().add(new XYChart.Data<>(key, summary.getSkippedTestPlans())); + }); + + // Adding the series to the chart + for (XYChart.Series series : seriesSet) { + stackedBarChart.getData().add(series); + } + genChart(stackedBarChart, 800, 800, historyChartFileName); + } + + /** + * Returns the chart generation location. + * + * @return chart generation directory + */ + public String getChartGenLocation() { + return chartGenLocation; + } + + /** + * Generates the chart and writes to an image. + * + * @param chart to be rendered + * @param width with of the chart in pixels + * @param height height of the chart in pixels + * @param fileName of the written image + */ + private void genChart(Chart chart, int width, int height, String fileName) { + Platform.runLater(() -> { + Stage stage = new Stage(); + Scene scene = new Scene(chart, width, height); + stage.setScene(scene); + WritableImage img = new WritableImage(width, height); + scene.snapshot(img); + writeImage(img, fileName); + }); + } + + /** + * Writes the {@link WritableImage} to a file. + * + * @param image {@link WritableImage} which is written the given file + * @param fileName file name of the image to be written + */ + private void writeImage(WritableImage image, String fileName) { + File file = new File(Paths.get(chartGenLocation, fileName).toString()); + try { + ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file); + } catch (IOException e) { + logger.error("Error occurred while writing the chart image", e); + } + } +} diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/GraphDataProvider.java b/reporting/src/main/java/org/wso2/testgrid/reporting/GraphDataProvider.java new file mode 100644 index 000000000..9de0ac3b0 --- /dev/null +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/GraphDataProvider.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.reporting; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.wso2.testgrid.common.Status; +import org.wso2.testgrid.common.TestPlan; +import org.wso2.testgrid.common.exception.TestGridException; +import org.wso2.testgrid.common.util.FileUtil; +import org.wso2.testgrid.common.util.StringUtil; +import org.wso2.testgrid.dao.TestGridDAOException; +import org.wso2.testgrid.dao.dto.TestCaseFailureResultDTO; +import org.wso2.testgrid.dao.uow.TestPlanUOW; +import org.wso2.testgrid.reporting.model.email.BuildExecutionSummary; +import org.wso2.testgrid.reporting.model.email.BuildFailureSummary; +import org.wso2.testgrid.reporting.model.email.InfraCombination; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * This class is responsible for providing required data in order to generate email graphs. + * + * @since 1.0.0 + */ +public class GraphDataProvider { + private TestPlanUOW testPlanUOW; + private static final int MAXIMUM_TIME_RANGE = 30; + private static final int TEST_EXECUTION_HISTORY_RANGE = 7; + private static final String OPERATING_SYSTEM = "OS"; + private static final String JDK = "JDK"; + private static final String DATABASE_ENGINE = "DBEngine"; + private static final String DATABASE_ENGINE_VERSION = "DBEngineVersion"; + + public GraphDataProvider() { + this.testPlanUOW = new TestPlanUOW(); + } + + /** + * Provides the test failure summary for a given build job. Test Plan ids of the build job is retrieved by reading + * testgrid yaml files which contains in the current workspace. + * + * @param workspace current workspace + * @throws ReportingException thrown when getting test plan ids by reading test plan yaml files + */ + public List getTestFailureSummary(String workspace) throws ReportingException { + List testFailureSummary; + try { + testFailureSummary = testPlanUOW + .getTestFailureSummary(FileUtil.getTestPlanIdByReadingTGYaml(workspace)); + if (testFailureSummary.isEmpty()) { + return Collections.emptyList(); + } + return processTestFailureSummary(testFailureSummary); + } catch (TestGridException e) { + throw new ReportingException("Error occurred while reading yaml files in the TG home", e); + } catch (TestGridDAOException e) { + throw new ReportingException("Error occurred while getting test failure summary from the database", e); + } + } + + /** + * Process the test failure summary which is retrieved by reading database and construct the list of + * {@link BuildFailureSummary} to draw test failure summary chart. + * + * @param testFailureSummary list of {@link TestCaseFailureResultDTO} which are retrieved by quering the databse. + */ + private List processTestFailureSummary(List testFailureSummary) { + TreeMap testFailureSummaryMap = new TreeMap<>(); + for (TestCaseFailureResultDTO testFailure : testFailureSummary) { + BuildFailureSummary buildFailureSummaryData = new BuildFailureSummary(); + Gson gson = new GsonBuilder().create(); + InfraCombination infraCombination = new InfraCombination(); + String testName = testFailure.getName(); + JsonElement jelem = gson.fromJson(testFailure.getInfraParameters(), JsonElement.class); + JsonObject jobj = jelem.getAsJsonObject(); + infraCombination.setOs(StringUtil.concatStrings(jobj.get(OPERATING_SYSTEM).getAsString(), " - ", + jobj.get("OSVersion").getAsString())); + infraCombination.setJdk(jobj.get(JDK).getAsString()); + infraCombination.setDbEngine(StringUtil.concatStrings(jobj.get(DATABASE_ENGINE).getAsString(), " - ", + jobj.get(DATABASE_ENGINE_VERSION).getAsString())); + if (testFailureSummaryMap.containsKey(testName)) { + testFailureSummaryMap.get(testName).getInfraCombinations().add(infraCombination); + } else { + List infraCombinations = new ArrayList<>(); + buildFailureSummaryData.setTestCaseName(testName); + buildFailureSummaryData.setTestCaseDescription(testFailure.getFailureMessage()); + infraCombinations.add(infraCombination); + buildFailureSummaryData.setInfraCombinations(infraCombinations); + testFailureSummaryMap.put(testName, buildFailureSummaryData); + } + } + return new ArrayList<>(testFailureSummaryMap.values()); + } + + /** + * Provide test execution summary for a given build job.. + * + * @param workspace current workspace + * @throws ReportingException thrown when error on getting test plan ids by reading testgrid yaml files located + * in the current workspace. + */ + public BuildExecutionSummary getTestExecutionSummary(String workspace) throws ReportingException { + int passedTestPlans = 0; + int failedTestPlans = 0; + int skippedTestPlans = 0; + List testExecutionSummary; + try { + testExecutionSummary = testPlanUOW + .getTestExecutionSummary(FileUtil.getTestPlanIdByReadingTGYaml(workspace)); + BuildExecutionSummary testExecutionSummaryData = new BuildExecutionSummary(); + if (testExecutionSummary.isEmpty()) { + throw new ReportingException("Couldn't find test plan status for given test plan ids"); + } + + for (String status : testExecutionSummary) { + if (Status.SUCCESS.toString().equals(status)) { + passedTestPlans++; + } else if (Status.FAIL.toString().equals(status)) { + failedTestPlans++; + } else { + skippedTestPlans++; + } + } + testExecutionSummaryData.setPassedTestPlans(passedTestPlans); + testExecutionSummaryData.setFailedTestPlans(failedTestPlans); + testExecutionSummaryData.setSkippedTestPlans(skippedTestPlans); + return testExecutionSummaryData; + } catch (TestGridException e) { + throw new ReportingException("Error occurred while reading yaml files in the TG home", e); + } + } + + /** + * Provide history of the test execution summary for a given build job.. + * + * in the current workspace. + */ + public Map getTestExecutionHistory(String productId) { + + Map buildExecutionSummariesHistory = new TreeMap<>(); + + LocalTime midnight = LocalTime.MIDNIGHT; + LocalDate timeZone = LocalDate.now(ZoneId.of("UTC")); + LocalDateTime todayMidnight = LocalDateTime.of(timeZone, midnight); + + for (int i = 0; i < MAXIMUM_TIME_RANGE; i++) { + if (TEST_EXECUTION_HISTORY_RANGE == buildExecutionSummariesHistory.size()) { + break; + } + String from = todayMidnight.minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + String to = todayMidnight.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + TreeMap testExecutionHistory = new TreeMap<>(); + BuildExecutionSummary buildExecutionSummary = new BuildExecutionSummary(); + List filteredTestPlanHistory; + + int passedTestPlans = 0; + int failedTestPlans = 0; + int skippedTestPlans = 0; + + List testPlanHistory = testPlanUOW.getTestExecutionHistory(productId, from, to); + + if (testPlanHistory.isEmpty()) { + todayMidnight = todayMidnight.minusDays(1); + continue; + } + + for (TestPlan testplan : testPlanHistory) { + String key = testplan.getInfraParameters(); + if (testExecutionHistory.containsKey(key)) { + if (testplan.getTestRunNumber() > testExecutionHistory.get(key).getTestRunNumber()) { + testExecutionHistory.replace(key, testplan); + } + } else { + testExecutionHistory.put(testplan.getInfraParameters(), testplan); + } + } + + filteredTestPlanHistory = new ArrayList<>(testExecutionHistory.values()); + for (TestPlan testplan : filteredTestPlanHistory) { + if (Status.SUCCESS.equals(testplan.getStatus())) { + passedTestPlans++; + } else if (Status.FAIL.equals(testplan.getStatus())) { + failedTestPlans++; + } else { + skippedTestPlans++; + } + } + buildExecutionSummary.setPassedTestPlans(passedTestPlans); + buildExecutionSummary.setFailedTestPlans(failedTestPlans); + buildExecutionSummary.setSkippedTestPlans(skippedTestPlans); + buildExecutionSummariesHistory.put(to, buildExecutionSummary); + todayMidnight = todayMidnight.minusDays(1); + } + return buildExecutionSummariesHistory; + } +} diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/TestReportEngine.java b/reporting/src/main/java/org/wso2/testgrid/reporting/TestReportEngine.java index 97b2b25db..0a158d5c0 100755 --- a/reporting/src/main/java/org/wso2/testgrid/reporting/TestReportEngine.java +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/TestReportEngine.java @@ -25,6 +25,8 @@ import org.wso2.testgrid.common.TestCase; import org.wso2.testgrid.common.TestPlan; import org.wso2.testgrid.common.TestScenario; +import org.wso2.testgrid.common.exception.TestGridException; +import org.wso2.testgrid.common.util.FileUtil; import org.wso2.testgrid.common.util.StringUtil; import org.wso2.testgrid.common.util.TestGridUtil; import org.wso2.testgrid.dao.TestGridDAOException; @@ -34,11 +36,14 @@ import org.wso2.testgrid.reporting.model.PerAxisSummary; import org.wso2.testgrid.reporting.model.Report; import org.wso2.testgrid.reporting.model.ReportElement; +import org.wso2.testgrid.reporting.model.email.BuildExecutionSummary; +import org.wso2.testgrid.reporting.model.email.BuildFailureSummary; +import org.wso2.testgrid.reporting.model.email.InfraCombination; +import org.wso2.testgrid.reporting.model.email.TestCaseResultSection; import org.wso2.testgrid.reporting.model.performance.PerformanceReport; import org.wso2.testgrid.reporting.renderer.Renderable; import org.wso2.testgrid.reporting.renderer.RenderableFactory; import org.wso2.testgrid.reporting.summary.InfrastructureBuildStatus; -import org.wso2.testgrid.reporting.util.FileUtil; import java.io.IOException; import java.nio.file.Files; @@ -59,10 +64,11 @@ import java.util.stream.Stream; import static org.wso2.testgrid.common.TestGridConstants.TESTGRID_EMAIL_REPORT_NAME; +import static org.wso2.testgrid.common.TestGridConstants.TESTGRID_SUMMARIZED_EMAIL_REPORT_NAME; +import static org.wso2.testgrid.common.util.FileUtil.writeToFile; import static org.wso2.testgrid.reporting.AxisColumn.DEPLOYMENT; import static org.wso2.testgrid.reporting.AxisColumn.INFRASTRUCTURE; import static org.wso2.testgrid.reporting.AxisColumn.SCENARIO; -import static org.wso2.testgrid.reporting.util.FileUtil.writeToFile; /** * This class is responsible for generating the test reports. @@ -76,8 +82,10 @@ public class TestReportEngine { private static final String REPORT_MUSTACHE = "report.mustache"; private static final String PERFORMANCE_REPORT_MUSTACHE = "performance_report.mustache"; private static final String EMAIL_REPORT_MUSTACHE = "email_report.mustache"; + private static final String SUMMARIZED_EMAIL_REPORT_MUSTACHE = "summarized_email_report.mustache"; private static final String REPORT_TEMPLATE_KEY = "parsedReport"; private static final String PER_TEST_PLAN_TEMPLATE_KEY = "perTestPlan"; + private static final String PER_TEST_CASE_TEMPLATE_KEY = "perTestCase"; private static final String PRODUCT_STATUS_TEMPLATE_KEY = "productTestStatus"; private static final String PRODUCT_NAME_TEMPLATE_KEY = "productName"; private static final String GIT_BUILD_DETAILS_TEMPLATE_KEY = "gitBuildDetails"; @@ -107,7 +115,8 @@ public TestReportEngine() { * @param groupBy columns to group by * @throws ReportingException thrown when error on generating test report */ - public void generateReport(Product product, boolean showSuccess, String groupBy) throws ReportingException { + public void generateReport(Product product, boolean showSuccess, String groupBy) + throws ReportingException { AxisColumn uniqueAxisColumn = getGroupByColumn(groupBy); // Construct report elements @@ -167,7 +176,6 @@ public void generatePerformanceReport(PerformanceReport report, List generateEmailReport(Product product, String workspace) thr .getSummaryTable(testPlans); postProcessSummaryTable(testCaseInfraSummaryMap); logger.info("Generated summary table info: " + testCaseInfraSummaryMap); - Renderable renderer = RenderableFactory.getRenderable(EMAIL_REPORT_MUSTACHE); Map report = new HashMap<>(); Map perSummariesMap = new HashMap<>(); @@ -765,7 +776,6 @@ public Optional generateEmailReport(Product product, String workspace) thr report.put(GIT_BUILD_DETAILS_TEMPLATE_KEY, emailReportProcessor.getGitBuildDetails(product, testPlans)); report.put(PRODUCT_STATUS_TEMPLATE_KEY, emailReportProcessor.getProductStatus(product).toString()); report.put(PER_TEST_PLAN_TEMPLATE_KEY, emailReportProcessor.generatePerTestPlanSection(product, testPlans)); - report.put("testCaseInfraSummaryTable", testCaseInfraSummaryMap.entrySet()); perSummariesMap.put(REPORT_TEMPLATE_KEY, report); String htmlString = renderer.render(EMAIL_REPORT_MUSTACHE, perSummariesMap); @@ -773,7 +783,7 @@ public Optional generateEmailReport(Product product, String workspace) thr String relativeFilePath = TestGridUtil.deriveTestGridLogFilePath(product.getName(), TESTGRID_EMAIL_REPORT_NAME); String testGridHome = TestGridUtil.getTestGridHomePath(); Path reportPath = Paths.get(testGridHome, relativeFilePath); - writeToFile(reportPath.toString(), htmlString); + writeHTMLToFile(reportPath, htmlString); return Optional.of(reportPath); } @@ -813,4 +823,121 @@ private List getTestPlans(String workspace, Path testPlansDir) throws } return testPlans; } + + /** + * Generate a summarized HTML report which is used as the content of the email to be sent out to + * relevant parties interested in TestGrid test job. + * + * @param product product needing the report + * @param workspace workspace containing the test-plan yamls + * @return {@link Path} of the created report + */ + public Optional generateSummarizedEmailReport(Product product, String workspace) throws ReportingException { + + List testPlans = getTestPlansInWorkspace(workspace); + //start email generation + if (!emailReportProcessor.hasFailedTests(testPlans)) { + logger.info("Latest build of '" + product.getName() + "' does not contain failed tests. " + + "Hence skipping email-report generation.."); + return Optional.empty(); + } + GraphDataProvider graphDataProvider = new GraphDataProvider(); + List failureSummary = graphDataProvider.getTestFailureSummary(workspace); + + Map results = new HashMap<>(); + List resultList = new ArrayList<>(); + + for (BuildFailureSummary buildFailureSummary : failureSummary) { + TestCaseResultSection resultSection = new TestCaseResultSection(); + resultSection.setTestCaseName(buildFailureSummary.getTestCaseName()); + resultSection.setTestCaseDescription(buildFailureSummary.getTestCaseDescription()); + List combinations = buildFailureSummary.getInfraCombinations(); + resultSection.setTestCaseExecutedOS(combinations.get(0).getOs()); + resultSection.setTestCaseExecutedJDK(combinations.get(0).getJdk()); + resultSection.setTestCaseExecutedDB(combinations.get(0).getDbEngine()); + + if (combinations.size() > 1) { + resultSection.setInfraCombinations(combinations.subList(1, combinations.size())); + resultSection.setRowSpan(combinations.size()); + } + resultList.add(resultSection); + } + + Renderable renderer = RenderableFactory.getRenderable(EMAIL_REPORT_MUSTACHE); + + results.put(PRODUCT_NAME_TEMPLATE_KEY, product.getName()); + results.put(GIT_BUILD_DETAILS_TEMPLATE_KEY, emailReportProcessor.getGitBuildDetails(product, testPlans)); + results.put(PRODUCT_STATUS_TEMPLATE_KEY, emailReportProcessor.getProductStatus(product).toString()); + results.put(PER_TEST_CASE_TEMPLATE_KEY, resultList); + String htmlString = renderer.render(SUMMARIZED_EMAIL_REPORT_MUSTACHE, results); + + // Write to HTML file + String relativeFilePath = TestGridUtil + .deriveTestGridLogFilePath(product.getName(), TESTGRID_SUMMARIZED_EMAIL_REPORT_NAME); + String testGridHome = TestGridUtil.getTestGridHomePath(); + Path reportPath = Paths.get(testGridHome, relativeFilePath); + writeHTMLToFile(reportPath, htmlString); + Path reportParentPath = reportPath.getParent(); + + // Generating the charts required for the email + if (reportParentPath == null) { + throw new ReportingException( + "Couldn't find the parent of the report path: " + reportPath.toAbsolutePath().toString()); + } + generateSummarizedCharts(workspace, reportParentPath.toString(), product.getId()); + return Optional.of(reportPath); + } + + /** + * Returns a list of {@link TestPlan} for a given workspace. + * + * @param workspace build workspace + * @return a list of {@link TestPlan} in the given workspace + * @throws ReportingException if {@link TestGridDAOException} or {@link TestGridException} occurs + */ + private List getTestPlansInWorkspace(String workspace) throws ReportingException { + List testPlans = new ArrayList<>(); + try { + List testPlanIds = FileUtil.getTestPlanIdByReadingTGYaml(workspace); + for (String testPlanId : testPlanIds) { + Optional testPlanById = testPlanUOW.getTestPlanById(testPlanId); + if (testPlanById.isPresent()) { + logger.info("Derived test plan dir in email phase : " + TestGridUtil + .deriveTestPlanDirName(testPlanById.get())); + testPlans.add(testPlanById.get()); + } else { + logger.error(String.format( + "Inconsistent state: The test plan yaml with id '%s' has no entry in the database. " + + "Ignoring the test plan...", testPlanId)); + } + } + } catch (TestGridDAOException e) { + throw new ReportingException("Error occurred while getting the test plan from database ", e); + } catch (TestGridException e) { + throw new ReportingException( + "Error occurred while getting the test plan id by reading test grid yaml files ", e); + } + return testPlans; + } + + /** + * Generate summarized charts for the summary email. + * @param workspace curent workspace of the build + * @param chartGenLocation location where charts should be generated + * @param id product id + */ + private void generateSummarizedCharts(String workspace, String chartGenLocation, String id) + throws ReportingException { + GraphDataProvider dataProvider = new GraphDataProvider(); + logger.info(StringUtil.concatStrings("Generating Charts with workspace : ", workspace, " at ", + chartGenLocation)); + BuildExecutionSummary summary = dataProvider.getTestExecutionSummary(workspace); + ChartGenerator chartGenerator = new ChartGenerator(chartGenLocation); + + // Generating the charts + chartGenerator.generateSummaryChart(summary.getPassedTestPlans(), summary.getFailedTestPlans(), summary + .getSkippedTestPlans()); + // Generate history chart + chartGenerator.generateResultHistoryChart(dataProvider.getTestExecutionHistory(id)); + } } diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/BuildExecutionSummary.java b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/BuildExecutionSummary.java new file mode 100644 index 000000000..4f0e7d947 --- /dev/null +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/BuildExecutionSummary.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.reporting.model.email; + +/** + * This defines the build execution summary for a build. + * + * @since 1.0.0 + */ +public class BuildExecutionSummary { + private int failedTestPlans; + private int passedTestPlans; + private int skippedTestPlans; + + public int getFailedTestPlans() { + return failedTestPlans; + } + + public void setFailedTestPlans(int failedTestPlans) { + this.failedTestPlans = failedTestPlans; + } + + public int getPassedTestPlans() { + return passedTestPlans; + } + + public void setPassedTestPlans(int passedTestPlans) { + this.passedTestPlans = passedTestPlans; + } + + public int getSkippedTestPlans() { + return skippedTestPlans; + } + + public void setSkippedTestPlans(int skippedTestPlans) { + this.skippedTestPlans = skippedTestPlans; + } +} diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/BuildFailureSummary.java b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/BuildFailureSummary.java new file mode 100644 index 000000000..8cb93f89a --- /dev/null +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/BuildFailureSummary.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.reporting.model.email; + +import java.util.ArrayList; +import java.util.List; + +/** + * This defines the build failure summary for a specifc test case. + * + * @since 1.0.0 + */ +public class BuildFailureSummary { + private String testCaseName; + private String testCaseDescription; + private List infraCombinations = new ArrayList<>(); + + public String getTestCaseName() { + return testCaseName; + } + + public void setTestCaseName(String testCaseName) { + this.testCaseName = testCaseName; + } + + public String getTestCaseDescription() { + return testCaseDescription; + } + + public void setTestCaseDescription(String testCaseDescription) { + this.testCaseDescription = testCaseDescription; + } + + public List getInfraCombinations() { + return infraCombinations; + } + + public void setInfraCombinations(List infraCombinations) { + this.infraCombinations = infraCombinations; + } +} diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/InfraCombination.java b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/InfraCombination.java new file mode 100644 index 000000000..f5df491b0 --- /dev/null +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/InfraCombination.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.reporting.model.email; + +/** + * This defines s single infra combination. + * + * @since 1.0.0 + */ +public class InfraCombination { + private String os; + private String jdk; + private String dbEngine; + + /** + * Returns name of the operating system. + * + * @return OS name + OS version + */ + public String getOs() { + return os; + } + + /** + * Sets name of the operating system. + * + * @param os OS name + OS version + */ + public void setOs(String os) { + this.os = os; + } + + /** + * Returns jdk name. + * + * @return JDK name + */ + public String getJdk() { + return jdk; + } + + /** + * Sets jdk name. + * + * @param jdk JDK name + */ + public void setJdk(String jdk) { + this.jdk = jdk; + } + + /** + * Returns database engine name. + * + * @return DB engine name + DB engine version + */ + public String getDbEngine() { + return dbEngine; + } + + /** + * Sets database engine name. + * + * @param dbEngine DB engine name + DB engine version + */ + public void setDbEngine(String dbEngine) { + this.dbEngine = dbEngine; + } + +} diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/TestCaseResultSection.java b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/TestCaseResultSection.java new file mode 100644 index 000000000..45d438b16 --- /dev/null +++ b/reporting/src/main/java/org/wso2/testgrid/reporting/model/email/TestCaseResultSection.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.wso2.testgrid.reporting.model.email; + +import java.util.List; + +/** + * Model class representing the test-case result section in summarized email-report. + */ +public class TestCaseResultSection { + + private String testCaseName; + private String testCaseDescription; + private String testCaseExecutedOS; + private String testCaseExecutedJDK; + private String testCaseExecutedDB; + int rowSpan = -1; + + List infraCombinations; + + public String getTestCaseName() { + return testCaseName; + } + + public void setTestCaseName(String testCaseName) { + this.testCaseName = testCaseName; + } + + public String getTestCaseDescription() { + return testCaseDescription; + } + + public void setTestCaseDescription(String testCaseDescription) { + this.testCaseDescription = testCaseDescription; + } + + public String getTestCaseExecutedOS() { + return testCaseExecutedOS; + } + + public void setTestCaseExecutedOS(String testCaseExecutedOS) { + this.testCaseExecutedOS = testCaseExecutedOS; + } + + public String getTestCaseExecutedJDK() { + return testCaseExecutedJDK; + } + + public void setTestCaseExecutedJDK(String testCaseExecutedJDK) { + this.testCaseExecutedJDK = testCaseExecutedJDK; + } + + public String getTestCaseExecutedDB() { + return testCaseExecutedDB; + } + + public void setTestCaseExecutedDB(String testCaseExecutedDB) { + this.testCaseExecutedDB = testCaseExecutedDB; + } + + public List getInfraCombinations() { + return infraCombinations; + } + + public void setInfraCombinations(List infraCombinations) { + this.infraCombinations = infraCombinations; + } + + public int getRowSpan() { + return rowSpan; + } + + public void setRowSpan(int rowSpan) { + this.rowSpan = rowSpan; + } +} diff --git a/reporting/src/main/java/org/wso2/testgrid/reporting/util/FileUtil.java b/reporting/src/main/java/org/wso2/testgrid/reporting/util/FileUtil.java deleted file mode 100755 index 32b40cff7..000000000 --- a/reporting/src/main/java/org/wso2/testgrid/reporting/util/FileUtil.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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.wso2.testgrid.reporting.util; - -import org.wso2.testgrid.reporting.ReportingException; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Locale; - -/** - * This class is responsible for handling file operations. - * - * @since 1.0.0 - */ -public class FileUtil { - - /** - * Writes a given string to a given file to persistent media. - * - * @param filePath absolute path of the file to be written - * @param string string to be written - * @throws ReportingException thrown on {@link FileNotFoundException} or {@link UnsupportedEncodingException} - */ - public static void writeToFile(String filePath, String string) throws ReportingException { - createFileIfNotExists(filePath); // Create file if not exists - try (PrintWriter writer = new PrintWriter(filePath, StandardCharsets.UTF_8.name())) { - writer.write(string); - writer.close(); - } catch (FileNotFoundException e) { - throw new ReportingException(String.format(Locale.ENGLISH, "File %s not found", filePath), e); - } catch (UnsupportedEncodingException e) { - throw new ReportingException( - String.format(Locale.ENGLISH, "Unsupported encoding %s", StandardCharsets.UTF_8.name()), e); - } - } - - /** - * Creates a file with the given name. - * - * @param filePath absolute path of the file - * @throws ReportingException thrown when IO exception on creating a file - */ - private static void createFileIfNotExists(String filePath) throws ReportingException { - File file = new File(filePath); - if (!file.exists()) { - // Create directories if not exists - Path parent = Paths.get(filePath).getParent(); - - if (parent != null) { - boolean status = new File(parent.toAbsolutePath().toString()).mkdirs(); - - if (status) { - // Touch file - try { - new FileOutputStream(file).close(); - } catch (IOException e) { - throw new ReportingException(String.format(Locale.ENGLISH, - "IO Exception occurred when creating file %s", file), e); - } - } - } - } - } -} diff --git a/reporting/src/main/resources/templates/summarized_email_report.mustache b/reporting/src/main/resources/templates/summarized_email_report.mustache new file mode 100644 index 000000000..86d3ed2a9 --- /dev/null +++ b/reporting/src/main/resources/templates/summarized_email_report.mustache @@ -0,0 +1,192 @@ + + + + + + WSO2 Test Grid - Test Execution Summary + + + + + + + + + +
WSO2

TestGrid : Test Execution Results

+
+
+ + + + +
+ + + + +
+ {{productName}} integration tests FAILED!
+
+
+
+
{{{gitBuildDetails}}}
+
+ Test Result Summary +

+ + + + + +
+ + + +
+
+ Testcase Failure Summary Table +

+ + + + + + + + + + + + {{#perTestCase}} + + + + + + + + {{#infraCombinations}} + + + + + + {{/infraCombinations}} + {{/perTestCase}} + +
Failed Test CaseFailure MessageOSJDKDB
{{testCaseName}}{{testCaseDescription}}{{testCaseExecutedOS}}{{testCaseExecutedJDK}}{{testCaseExecutedDB}}
{{os}}{{jdk}}{{dbEngine}}
+ Tested by WSO2 TestGrid. +

+ + diff --git a/reporting/src/test/java/org/wso2/testgrid/reporting/TestReportEngineTest.java b/reporting/src/test/java/org/wso2/testgrid/reporting/TestReportEngineTest.java index 9d48d07a7..7ae81ab93 100644 --- a/reporting/src/test/java/org/wso2/testgrid/reporting/TestReportEngineTest.java +++ b/reporting/src/test/java/org/wso2/testgrid/reporting/TestReportEngineTest.java @@ -47,7 +47,6 @@ public void generateEmailReport(String testNum) throws Exception { logger.info("---- Running " + testNum); List testPlans = getTestPlansFor(testNum); - when(testPlanUOW.getLatestTestPlans(Matchers.any(Product.class))) .thenReturn(testPlans); when(testPlanUOW.getTestPlanById("abc")).thenReturn(Optional.of(testPlans.get(0))); @@ -67,5 +66,4 @@ public void generateEmailReport(String testNum) throws Exception { Assert.assertTrue(path.isPresent(), "Email report generation has failed. File path is empty."); logger.info("email report file: " + path.get()); } - }