1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module dextool.plugin.mutate.backend.report.html.page_test_case_similarity;
11 
12 import logger = std.experimental.logger;
13 import std.format : format;
14 import std.datetime : dur, Clock;
15 
16 import arsd.dom : Document, Element, require, Table, RawSource;
17 
18 import dextool.plugin.mutate.backend.database : Database;
19 import dextool.plugin.mutate.backend.report.analyzers : TestCaseSimilarityAnalyse,
20     reportTestCaseSimilarityAnalyse;
21 import dextool.plugin.mutate.backend.report.html.constants;
22 import dextool.plugin.mutate.backend.resource : jsTableOnClick;
23 import dextool.plugin.mutate.backend.report.html.tmpl : tmplBasicPage,
24     tmplDefaultTable, tmplDefaultMatrixTable, dashboardCss;
25 import dextool.plugin.mutate.backend.type : Mutation;
26 import dextool.plugin.mutate.config : ConfigReport;
27 import dextool.plugin.mutate.type : MutationKind;
28 
29 auto makeTestCaseSimilarityAnalyse(ref Database db, ref const ConfigReport conf,
30         const(MutationKind)[] humanReadableKinds, const(Mutation.Kind)[] kinds) @trusted {
31 
32     auto doc = tmplBasicPage.dashboardCss;
33     doc.title(format("Test Case Similarity Analyse %(%s %) %s",
34             humanReadableKinds, Clock.currTime));
35     doc.mainBody.setAttribute("onload", "init()");
36 
37     auto script = doc.root.childElements("head")[0].addChild("script");
38     script.addChild(new RawSource(doc, jsTableOnClick));
39 
40     toHtml(db, doc, reportTestCaseSimilarityAnalyse(db, kinds, 5), doc.mainBody, script);
41 
42     return doc.toPrettyString;
43 }
44 
45 private:
46 
47 void toHtml(ref Database db, Document doc, TestCaseSimilarityAnalyse result,
48         Element root, Element script) {
49     import std.algorithm : sort, map;
50     import std.array : array, appender;
51     import std.conv : to;
52     import std.json : JSONValue;
53     import std.path : buildPath;
54     import dextool.cachetools;
55     import dextool.plugin.mutate.backend.database : spinSql, MutationId;
56     import dextool.plugin.mutate.backend.report.html.page_files : pathToHtmlLink;
57     import dextool.type : Path;
58 
59     root.addChild("p", "This is the similarity between test cases.");
60     {
61         auto p = root.addChild("p");
62         p.addChild("b", "Note");
63         p.appendText(": The analysis is based on the mutants that the test cases kill; thus, it is dependent on the mutation operators that are used when generating the report.");
64     }
65 
66     auto getPath = nullableCache!(MutationId, string, (MutationId id) {
67         auto path = spinSql!(() => db.getPath(id)).get;
68         return format!"%s#%s"(buildPath(Html.fileDir, pathToHtmlLink(path)), id);
69     })(0, 30.dur!"seconds");
70 
71     const test_cases = result.similarities.byKey.array.sort!((a, b) => a < b).array;
72 
73     root.addChild("p", "The intersection column is the mutants that are killed by both the test case in the heading and in the column Test Case.")
74         .appendText(
75                 " The difference column are the mutants that are only killed by the test case in the heading.");
76     root.addChild("p",
77             "The tables are hidden by default, click on the corresponding header to display a table.");
78     with (root.addChild("button", "expand all")) {
79         setAttribute("type", "button");
80         setAttribute("id", "expand_all");
81     }
82     with (root.addChild("button", "collapse all")) {
83         setAttribute("type", "button");
84         setAttribute("id", "collapse_all");
85     }
86     foreach (const tc; test_cases) {
87         // Containers allows for hiding a table by clicking the corresponding header.
88         // Defaults to hiding tables.
89         auto comp_container = root.addChild("div").addClass("comp_container");
90         auto heading = comp_container.addChild("h2").addClass("tbl_header");
91         heading.addChild("i").addClass("right");
92         heading.appendText(" ");
93         heading.appendText(tc.name);
94         auto tbl_container = comp_container.addChild("div").addClass("tbl_container");
95         tbl_container.setAttribute("style", "display: none;");
96         auto tbl = tmplDefaultTable(tbl_container, [
97                 "Test Case", "Similarity", "Difference", "Intersection"
98                 ]);
99         foreach (const d; result.similarities[tc]) {
100             auto r = tbl.appendRow();
101             r.addChild("td", d.testCase.name);
102             r.addChild("td", format("%#.3s", d.similarity));
103             auto difference = r.addChild("td");
104             foreach (const mut; d.difference) {
105                 auto link = difference.addChild("a", mut.to!string);
106                 link.href = getPath(mut).get;
107                 difference.appendText(" ");
108             }
109             auto similarity = r.addChild("td");
110             foreach (const mut; d.intersection) {
111                 auto link = similarity.addChild("a", mut.to!string);
112                 link.href = getPath(mut).get;
113                 similarity.appendText(" ");
114             }
115         }
116     }
117 }