• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

future-architect / uroborosql / #743

28 Jun 2024 04:17PM UTC coverage: 90.988% (+0.5%) from 90.484%
#743

push

web-flow
add paramIfNotEmpty method (#318)

* v0.x to master merge

* fix nanoseconds diff (java8 to java11)

* eclipse cleanup and  optimize import

* eclipse format

* optimize Imports

* remove unused annotation

* library version up

* migrate v0.x to master
- update java version 8 to 11
- update junit4 to junit5
- library version update

* fix test failed

* remove unused annotation

* fixed bug

* use var

* fix java8 coding style

* Refactoring TransactionContextManager

* Refactoring SqlAgent

* 途中コミット

* fix testcase

* fix review

* fix javadoc comments

* merge v0.x PR

* cleanup code

* cleanup test code

* change build status badge

* - agent.query, update, proc, batch にそれぞれSupplierを引数にとるメソッドを追加
- SqlQuery.paramとSqlEntityUpdate.setにFunctionを受け取るメソッドを追加

* testcaseの整形

* - SqlFluent と ExtractionCondition の分離
- Arrays.asList -> List.of への変更
- テストの整形

* - v0.x系の不具合対応の追いつき
- ログ出力の整理

* - SqlKindの整理(ENTITY_XXXの追加)
- REPL_LOGの追加
- Deprecatedメソッドの削除(SqlAgent)
- SqlAgent, ExecutionContextでsetterをfluent APIに変更

* DB接続URLの修正

* add and fix testcases.

* add event testcases.

* fix typo

* add paramIfNotEmpty method and StringUtils rename ObjectUtils

* fix review comments.

* remove unused import

1695 of 1958 new or added lines in 97 files covered. (86.57%)

26 existing lines in 10 files now uncovered.

8249 of 9066 relevant lines covered (90.99%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.75
/src/main/java/jp/co/future/uroborosql/coverage/reports/html/HtmlReportCoverageHandler.java
1
/**
2
 * Copyright (c) 2017-present, Future Corporation
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
package jp.co.future.uroborosql.coverage.reports.html;
8

9
import java.io.BufferedWriter;
10
import java.io.IOException;
11
import java.nio.charset.StandardCharsets;
12
import java.nio.file.Files;
13
import java.nio.file.Path;
14
import java.nio.file.Paths;
15
import java.nio.file.StandardCopyOption;
16
import java.time.ZonedDateTime;
17
import java.time.format.DateTimeFormatter;
18
import java.util.Collection;
19
import java.util.Comparator;
20
import java.util.Map;
21
import java.util.concurrent.ConcurrentHashMap;
22
import java.util.stream.Collectors;
23
import java.util.stream.Stream;
24

25
import org.slf4j.Logger;
26
import org.slf4j.LoggerFactory;
27

28
import jp.co.future.uroborosql.SqlAgent;
29
import jp.co.future.uroborosql.coverage.CoverageData;
30
import jp.co.future.uroborosql.coverage.CoverageHandler;
31
import jp.co.future.uroborosql.utils.ObjectUtils;
32

33
/**
34
 * カバレッジレポート出力ハンドラ<br>
35
 * htmlのカバレッジレポートを出力する
36
 *
37
 * <pre>
38
 * system property "uroborosql.sql.coverage" に "jp.co.future.uroborosql.coverage.reports.html.HtmlReportCoverageHandler" を指定することで
39
 * 本機能を利用することができます。
40
 * </pre>
41
 *
42
 * @author ota
43
 */
44
public class HtmlReportCoverageHandler implements CoverageHandler {
45
        /** カバレッジロガー. */
46
        private static final Logger COVERAGE_LOG = LoggerFactory.getLogger("jp.co.future.uroborosql.sql.coverage");
1✔
47

48
        /** カバレッジ情報. */
49
        private final Map<String, Map<String, SqlCoverageReport>> coverages = new ConcurrentHashMap<>();
1✔
50

51
        /** レポートディレクトリパス. */
52
        private final Path reportDirPath;
53

54
        /**
55
         * コンストラクタ<br>
56
         *
57
         * <pre>
58
         * system property "uroborosql.sql.coverage.dir" が指定された場合、指定されたPATHに レポートを出力します。
59
         * 指定の無い場合、デフォルトで "./target/coverage/sql" に レポートを出力します。
60
         * </pre>
61
         */
62
        public HtmlReportCoverageHandler() {
1✔
63
                var s = System.getProperty(SqlAgent.KEY_SQL_COVERAGE + ".dir");
1✔
64
                if (ObjectUtils.isNotEmpty(s)) {
1✔
65
                        this.reportDirPath = Paths.get(s);
1✔
66
                } else {
67
                        this.reportDirPath = Paths.get("target", "coverage", "sql");
×
68
                }
69
        }
1✔
70

71
        /**
72
         * コンストラクタ
73
         *
74
         * @param reportDirPath レポートファイルPATH
75
         */
76
        public HtmlReportCoverageHandler(final Path reportDirPath) {
×
77
                this.reportDirPath = reportDirPath;
×
78
        }
×
79

80
        @Override
81
        public synchronized void accept(final CoverageData coverageData) {
82
                if (ObjectUtils.isEmpty(coverageData.getSqlName())) {
1✔
83
                        // SQL名の設定されていないSQLは集約しない
84
                        return;
×
85
                }
86
                var map = coverages.computeIfAbsent(coverageData.getSqlName(), k -> new ConcurrentHashMap<>());
1✔
87
                var sqlCoverage = map.computeIfAbsent(coverageData.getMd5(),
1✔
88
                                k -> new SqlCoverageReport(coverageData.getSqlName(),
1✔
89
                                                coverageData.getSql(),
1✔
90
                                                coverageData.getMd5(),
1✔
91
                                                this.reportDirPath,
92
                                                map.size()));
1✔
93

94
                sqlCoverage.accept(coverageData.getPassRoute());
1✔
95
        }
1✔
96

97
        @Override
98
        public synchronized void onSqlAgentClose() {
99
                coverages.values().stream()
1✔
100
                                .map(Map::values)
1✔
101
                                .flatMap(Collection::stream)
1✔
102
                                .forEach(SqlCoverageReport::writeHtml);
1✔
103
                writeHtml();
1✔
104
        }
1✔
105

106
        private void writeHtml() {
107
                try {
108
                        Files.createDirectories(this.reportDirPath);
1✔
109
                        // copy resources
110
                        var packagePath = this.getClass().getPackage().getName().replace(".", "/");
1✔
111
                        Stream.of("style.css", "jquery-3.2.0.min.js", "stupidtable.min.js", "highlight.pack.js", "sqlcov.js",
1✔
112
                                        "icon.png").forEach(filename -> {
1✔
113
                                                try (var src = this.getClass().getClassLoader()
1✔
114
                                                                .getResourceAsStream(packagePath + "/" + filename)) {
1✔
115
                                                        Files.copy(src, Paths.get(this.reportDirPath + "/" + filename),
1✔
116
                                                                        StandardCopyOption.REPLACE_EXISTING);
NEW
117
                                                } catch (IOException ex) {
×
NEW
118
                                                        COVERAGE_LOG.error(ex.getMessage(), ex);
×
119
                                                }
1✔
120
                                        });
1✔
121
                        // write report
122
                        try (var writer = Files.newBufferedWriter(this.reportDirPath.resolve("index.html"),
1✔
123
                                        StandardCharsets.UTF_8)) {
124
                                writePrefix(writer);
1✔
125

126
                                writeHeaderSection(writer);
1✔
127

128
                                writeTablePrefix(writer);
1✔
129
                                writeTable(writer);
1✔
130
                                writeTableSuffix(writer);
1✔
131

132
                                writeSuffix(writer);
1✔
133
                        }
NEW
134
                } catch (IOException ex) {
×
NEW
135
                        COVERAGE_LOG.error(ex.getMessage(), ex);
×
136
                }
1✔
137
        }
1✔
138

139
        private void writeTable(final BufferedWriter writer) throws IOException {
140
                var list = coverages.values().stream()
1✔
141
                                .map(Map::values)
1✔
142
                                .flatMap(Collection::stream)
1✔
143
                                .sorted(Comparator.comparing(SqlCoverageReport::getName))
1✔
144
                                .collect(Collectors.toList());
1✔
145
                for (var sqlCoverageReport : list) {
1✔
146
                        var htmlName = sqlCoverageReport.getName();
1✔
147
                        var linkName = sqlCoverageReport.getName();
1✔
148
                        var lineCount = sqlCoverageReport.getLineValidSize();
1✔
149
                        var lineCovered = sqlCoverageReport.getLineCoveredSize();
1✔
150

151
                        var branchesCount = sqlCoverageReport.getBranchValidSize();
1✔
152
                        var branchesCovered = sqlCoverageReport.getBranchCoveredSize();
1✔
153

154
                        var lineCoveredPer = CoverageHandler.percent(lineCovered, lineCount);
1✔
155

156
                        writer.append("<tr>");
1✔
157
                        writer.newLine();
1✔
158
                        writer.append("   <td class=\"file\" ><a href=\"").append(linkName).append(".html\" >").append(htmlName)
1✔
159
                                        .append("</a></td>");
1✔
160
                        writer.newLine();
1✔
161
                        writer.append(
1✔
162
                                        "   <td class=\"pic\" ><div class=\"chart\"><div class=\"cover-fill\" style=\"width: ")
163
                                        .append(String.valueOf(lineCoveredPer))
1✔
164
                                        .append("%;\"></div><div class=\"cover-empty\" style=\"width:")
1✔
165
                                        .append(String.valueOf(100 - lineCoveredPer)).append("%;\"></div></div></td>");
1✔
166
                        writer.newLine();
1✔
167
                        writer.append("   <td class=\"lines\">").append(String.valueOf(lineCoveredPer))
1✔
168
                                        .append("%</td>");
1✔
169
                        writer.newLine();
1✔
170
                        writer.append("   <td class=\"lines-raw\">").append(String.valueOf(lineCovered)).append("/")
1✔
171
                                        .append(String.valueOf(lineCount)).append("</td>");
1✔
172
                        writer.newLine();
1✔
173
                        writer.append("   <td class=\"branches\">")
1✔
174
                                        .append(CoverageHandler.percentStr(branchesCovered, branchesCount))
1✔
175
                                        .append("%</td>");
1✔
176
                        writer.newLine();
1✔
177
                        writer.append("   <td class=\"branches-raw\">").append(String.valueOf(branchesCovered)).append("/")
1✔
178
                                        .append(String.valueOf(branchesCount)).append("</td>");
1✔
179
                        writer.newLine();
1✔
180
                        writer.append("</tr>");
1✔
181
                        writer.newLine();
1✔
182
                }
1✔
183
        }
1✔
184

185
        private void writePrefix(final BufferedWriter writer) throws IOException {
186
                writer.append("<!DOCTYPE html>");
1✔
187
                writer.newLine();
1✔
188
                writer.append("<html lang=\"en\">");
1✔
189
                writer.newLine();
1✔
190
                writer.append("<head>");
1✔
191
                writer.newLine();
1✔
192
                writer.append("    <meta charset=\"utf-8\" />");
1✔
193
                writer.newLine();
1✔
194
                writer.append("    <title>uroboroSQL code coverage report</title>");
1✔
195
                writer.newLine();
1✔
196
                writer.append("    <link rel=\"stylesheet\" href=\"style.css\">");
1✔
197
                writer.newLine();
1✔
198
                writer.append("    <script src=\"jquery-3.2.0.min.js\"></script>");
1✔
199
                writer.newLine();
1✔
200
                writer.append("    <script src=\"stupidtable.min.js\"></script>");
1✔
201
                writer.newLine();
1✔
202
                writer.append("    <script>$(function(){ $(\"table.coverage-summary\").stupidtable(); });</script>");
1✔
203
                writer.newLine();
1✔
204
                writer.append("</head>");
1✔
205
                writer.newLine();
1✔
206
                writer.append("<body>");
1✔
207
                writer.newLine();
1✔
208
        }
1✔
209

210
        private void writeHeaderSection(final BufferedWriter writer) throws IOException {
211
                var lineCount = coverages.values().stream()
1✔
212
                                .map(Map::values)
1✔
213
                                .flatMap(Collection::stream)
1✔
214
                                .mapToInt(SqlCoverageReport::getLineValidSize)
1✔
215
                                .sum();
1✔
216
                var lineCovered = coverages.values().stream()
1✔
217
                                .map(Map::values)
1✔
218
                                .flatMap(Collection::stream)
1✔
219
                                .mapToInt(SqlCoverageReport::getLineCoveredSize)
1✔
220
                                .sum();
1✔
221

222
                var branchesCount = coverages.values().stream()
1✔
223
                                .map(Map::values)
1✔
224
                                .flatMap(Collection::stream)
1✔
225
                                .mapToInt(SqlCoverageReport::getBranchValidSize)
1✔
226
                                .sum();
1✔
227
                var branchesCovered = coverages.values().stream()
1✔
228
                                .map(Map::values)
1✔
229
                                .flatMap(Collection::stream)
1✔
230
                                .mapToInt(SqlCoverageReport::getBranchCoveredSize)
1✔
231
                                .sum();
1✔
232

233
                writer.append("<div class=\"global-header\">");
1✔
234
                writer.newLine();
1✔
235
                writer.append("    <img class=\"icon\" src=\"icon.png\" />");
1✔
236
                writer.newLine();
1✔
237
                writer.append("    <span class=\"title\">uroboroSQL coverage</span>");
1✔
238
                writer.newLine();
1✔
239
                writer.append("</div>");
1✔
240
                writer.newLine();
1✔
241
                writer.append("<h1>Code coverage report Summary</h1>");
1✔
242
                writer.newLine();
1✔
243
                writer.append("<div class=\"header\">");
1✔
244
                writer.newLine();
1✔
245
                writer.append("    <div class=\"summary\">");
1✔
246
                writer.newLine();
1✔
247
                writer.append("      <strong>").append(CoverageHandler.percentStr(lineCovered, lineCount))
1✔
248
                                .append("% </strong>");
1✔
249
                writer.newLine();
1✔
250
                writer.append("      <span>Lines</span>");
1✔
251
                writer.newLine();
1✔
252
                writer.append("      <span class=\"fraction\">").append(String.valueOf(lineCovered)).append("/")
1✔
253
                                .append(String.valueOf(lineCount)).append("</span>");
1✔
254
                writer.newLine();
1✔
255
                writer.append("    </div>");
1✔
256
                writer.newLine();
1✔
257
                writer.append("    <div class=\"summary\">");
1✔
258
                writer.newLine();
1✔
259
                writer.append("      <strong>").append(CoverageHandler.percentStr(branchesCovered, branchesCount))
1✔
260
                                .append("% </strong>");
1✔
261
                writer.newLine();
1✔
262
                writer.append("      <span>Branches</span>");
1✔
263
                writer.newLine();
1✔
264
                writer.append("      <span class=\"fraction\">").append(String.valueOf(branchesCovered)).append("/")
1✔
265
                                .append(String.valueOf(branchesCount)).append("</span>");
1✔
266
                writer.newLine();
1✔
267
                writer.append("    </div>");
1✔
268
                writer.newLine();
1✔
269
                writer.append("</div>");
1✔
270
                writer.newLine();
1✔
271
        }
1✔
272

273
        private void writeTablePrefix(final BufferedWriter writer) throws IOException {
274
                writer.append("<div class=\"inner\">");
1✔
275
                writer.newLine();
1✔
276
                writer.append("<table class=\"coverage-summary\">");
1✔
277
                writer.newLine();
1✔
278
                writer.append("<thead>");
1✔
279
                writer.newLine();
1✔
280
                writer.append("<tr>");
1✔
281
                writer.newLine();
1✔
282
                writer.append("   <th data-sort=\"string\" class=\"file sorting-asc\" >File</th>");
1✔
283
                writer.newLine();
1✔
284
                writer.append("   <th class=\"pic\" ></th>");
1✔
285
                writer.newLine();
1✔
286
                writer.append("   <th data-sort=\"int\" class=\"lines\" >Lines</th>");
1✔
287
                writer.newLine();
1✔
288
                writer.append("   <th class=\"lines-raw\"></th>");
1✔
289
                writer.newLine();
1✔
290
                writer.append("   <th data-sort=\"int\" class=\"branches\" >Branches</th>");
1✔
291
                writer.newLine();
1✔
292
                writer.append("   <th class=\"branches-raw\"></th>");
1✔
293
                writer.newLine();
1✔
294
                writer.append("</tr>");
1✔
295
                writer.newLine();
1✔
296
                writer.append("</thead>");
1✔
297
                writer.newLine();
1✔
298
                writer.append("<tbody>");
1✔
299
                writer.newLine();
1✔
300

301
        }
1✔
302

303
        private void writeTableSuffix(final BufferedWriter writer) throws IOException {
304
                writer.append("</tbody>");
1✔
305
                writer.newLine();
1✔
306
                writer.append("</table>");
1✔
307
                writer.newLine();
1✔
308
                writer.append("</div>");
1✔
309
                writer.newLine();
1✔
310
                writer.append("<div class=\"footer\">code coverage report generated by ")
1✔
311
                                .append("<a href=\"https://github.com/future-architect/uroborosql\" target=\"_blank\">uroboroSQL</a> at ")
1✔
312
                                .append(ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME))
1✔
313
                                .append(".</div>");
1✔
314
                writer.newLine();
1✔
315
        }
1✔
316

317
        private void writeSuffix(final BufferedWriter writer) throws IOException {
318
                writer.append("</body>");
1✔
319
        }
1✔
320
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc