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}