001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.benchmarking.common.runner; 017 018import de.cuioss.benchmarking.common.config.BenchmarkType; 019import de.cuioss.benchmarking.common.config.ReportConfiguration; 020import de.cuioss.benchmarking.common.converter.JmhBenchmarkConverter; 021import de.cuioss.benchmarking.common.model.BenchmarkData; 022import de.cuioss.benchmarking.common.output.OutputDirectoryStructure; 023import de.cuioss.benchmarking.common.report.GitHubPagesGenerator; 024import de.cuioss.benchmarking.common.report.ReportGenerator; 025import de.cuioss.tools.logging.CuiLogger; 026import org.openjdk.jmh.results.RunResult; 027 028import java.io.IOException; 029import java.nio.file.Files; 030import java.nio.file.Path; 031import java.nio.file.StandardCopyOption; 032import java.util.Collection; 033 034import static de.cuioss.benchmarking.common.util.BenchmarkingLogMessages.INFO; 035 036/** 037 * Processes JMH benchmark results to generate all required artifacts during execution. 038 * <p> 039 * This processor handles the complete pipeline from raw JMH results to deployment-ready 040 * artifacts including badges, reports, metrics, and GitHub Pages structure. 041 * <p> 042 * Generated artifacts: 043 * <ul> 044 * <li>Performance badges (shields.io compatible JSON)</li> 045 * <li>Trend analysis badges</li> 046 * <li>Self-contained HTML reports</li> 047 * <li>Structured metrics in JSON format</li> 048 * <li>GitHub Pages deployment structure</li> 049 * </ul> 050 */ 051public class BenchmarkResultProcessor { 052 053 private static final CuiLogger LOGGER = 054 new CuiLogger(BenchmarkResultProcessor.class); 055 056 057 private final BenchmarkType benchmarkType; 058 private final ReportConfiguration reportConfig; 059 060 /** 061 * Creates a processor for the specified benchmark type. 062 * 063 * @param benchmarkType the type of benchmarks being processed 064 */ 065 public BenchmarkResultProcessor(BenchmarkType benchmarkType) { 066 this(benchmarkType, null); 067 } 068 069 /** 070 * Creates a processor with full report configuration including configured benchmark names. 071 * 072 * @param benchmarkType the type of benchmarks being processed 073 * @param reportConfig the report configuration with benchmark names (may be null) 074 */ 075 public BenchmarkResultProcessor(BenchmarkType benchmarkType, ReportConfiguration reportConfig) { 076 this.benchmarkType = benchmarkType; 077 this.reportConfig = reportConfig; 078 } 079 080 /** 081 * Processes benchmark results to generate all artifacts. 082 * 083 * @param results the JMH benchmark results (still needed for some generators) 084 * @param outputDir the output directory for generated artifacts 085 * @throws IOException if file operations fail 086 */ 087 public void processResults(Collection<RunResult> results, String outputDir) throws IOException { 088 LOGGER.info(INFO.PROCESSING_RESULTS, results.size()); 089 LOGGER.info(INFO.BENCHMARK_TYPE_DETECTED, benchmarkType); 090 091 // Create OutputDirectoryStructure for organized file generation 092 Path benchmarkResultsPath = Path.of(outputDir); 093 OutputDirectoryStructure structure = new OutputDirectoryStructure(benchmarkResultsPath); 094 structure.ensureDirectories(); 095 096 // Determine JSON result file path based on benchmark type 097 String jsonFileName = benchmarkType == BenchmarkType.MICRO ? 098 "micro-result.json" : "integration-result.json"; 099 Path jsonFile = benchmarkResultsPath.resolve(jsonFileName); 100 101 // Target location in gh-pages-ready/data directory with unified name 102 Path targetJsonFile = structure.getDataDir().resolve("original-jmh-result.json"); 103 104 // FAIL FAST: JSON file must exist (created by runner in production) 105 // For testing, tests must provide proper JSON files 106 if (!Files.exists(jsonFile)) { 107 throw new IllegalStateException("Benchmark JSON file not found: " + jsonFile + 108 ". The benchmark runner should have created this file."); 109 } 110 111 // Convert JMH JSON to BenchmarkData using converter, passing configured benchmark names 112 JmhBenchmarkConverter converter = reportConfig != null 113 ? new JmhBenchmarkConverter(benchmarkType, 114 reportConfig.throughputBenchmarkName(), reportConfig.latencyBenchmarkName(), 115 reportConfig.projectName()) 116 : new JmhBenchmarkConverter(benchmarkType); 117 BenchmarkData benchmarkData; 118 try { 119 benchmarkData = converter.convert(jsonFile); 120 } catch (IOException e) { 121 throw new IOException("Failed to convert JMH results to BenchmarkData", e); 122 } 123 124 // Copy JMH result to gh-pages-ready/data directory with unified name 125 Files.copy(jsonFile, targetJsonFile, StandardCopyOption.REPLACE_EXISTING); 126 LOGGER.info(INFO.JMH_RESULT_COPIED, targetJsonFile); 127 128 // Generate reports directly to gh-pages-ready structure 129 generateReportsToDeploymentDir(benchmarkData, structure); 130 131 // Generate deployment-specific assets (404.html, robots.txt, sitemap.xml) 132 generateGitHubPagesAssets(structure); 133 134 LOGGER.info(INFO.ARTIFACTS_GENERATED); 135 } 136 137 138 /** 139 * Generates HTML reports directly to the deployment directory. 140 */ 141 private void generateReportsToDeploymentDir(BenchmarkData benchmarkData, OutputDirectoryStructure structure) throws IOException { 142 LOGGER.info(INFO.GENERATING_REPORTS); 143 144 // Generate HTML reports to gh-pages-ready/ using standard API 145 // This will generate files in the deployment directory only 146 ReportGenerator reportGen = new ReportGenerator(); 147 String deploymentPath = structure.getDeploymentDir().toString(); 148 reportGen.generateIndexPage(benchmarkData, benchmarkType, deploymentPath); 149 reportGen.generateTrendsPage(deploymentPath); 150 if (benchmarkType == BenchmarkType.MICRO) { 151 reportGen.generateDetailedPage(deploymentPath); 152 } 153 reportGen.copySupportFiles(deploymentPath); 154 } 155 156 157 /** 158 * Generates GitHub Pages deployment-specific assets. 159 */ 160 private void generateGitHubPagesAssets(OutputDirectoryStructure structure) throws IOException { 161 GitHubPagesGenerator ghGen = new GitHubPagesGenerator(); 162 163 LOGGER.info(INFO.GENERATING_GITHUB_PAGES); 164 ghGen.generateDeploymentAssets(structure); 165 } 166 167}