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_group_similarity;
11 
12 import logger = std.experimental.logger;
13 import std.format : format;
14 import std.datetime : Clock, dur;
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 : TestGroupSimilarity,
20     reportTestGroupsSimilarity;
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 makeTestGroupSimilarityAnalyse(ref Database db, ref const ConfigReport conf,
30         const(MutationKind)[] humanReadableKinds, const(Mutation.Kind)[] kinds) @trusted {
31     auto doc = tmplBasicPage.dashboardCss;
32     auto s = doc.root.childElements("head")[0].addChild("script");
33     s.addChild(new RawSource(doc, jsTableOnClick));
34     doc.title(format("Test Group Similarity Analyse %(%s %) %s",
35             humanReadableKinds, Clock.currTime));
36     doc.mainBody.addChild("p",
37             "This is the similarity between test groups as specified in the dextool mutate configuration file.")
38         .appendText(" The closer to 1.0 the more similare the test groups are in what they verify.");
39     {
40         auto p = doc.mainBody.addChild("p");
41         p.addChild("b", "Note");
42         p.appendText(": The analyse 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.");
43     }
44 
45     toHtml(db, reportTestGroupsSimilarity(db, kinds, conf.testGroups), doc.mainBody);
46 
47     return doc.toPrettyString;
48 }
49 
50 private:
51 
52 void toHtml(ref Database db, TestGroupSimilarity result, Element root) {
53     import std.algorithm : sort, map;
54     import std.array : array;
55     import std.conv : to;
56     import std.path : buildPath;
57     import dextool.cachetools;
58     import dextool.plugin.mutate.backend.database : spinSql, MutationId, MutationStatusId;
59     import dextool.plugin.mutate.backend.report.html.utility : pathToHtmlLink;
60     import dextool.type : Path;
61 
62     auto getPath = nullableCache!(MutationStatusId, string, (MutationStatusId id) {
63         auto path = spinSql!(() => db.mutantApi.getPath(id)).get;
64         auto mutId = spinSql!(() => db.mutantApi.getMutationId(id)).get;
65         return format!"%s#%s"(buildPath("..", Html.fileDir, pathToHtmlLink(path)), mutId.get);
66     })(0, 30.dur!"seconds");
67 
68     const test_groups = result.similarities.byKey.array.sort!((a, b) => a < b).array;
69 
70     root.addChild("p", "The intersection column are the mutants that are killed by both the test group in the heading and in the column Test Group.")
71         .appendText(
72                 " The difference column are the mutants that are only killed by the test group in the heading.");
73     root.addChild("p",
74             "The tables are hidden by default, click on the corresponding header to display a table.");
75     with (root.addChild("button", "expand all")) {
76         setAttribute("type", "button");
77         setAttribute("id", "expand_all");
78     }
79     with (root.addChild("button", "collapse all")) {
80         setAttribute("type", "button");
81         setAttribute("id", "collapse_all");
82     }
83     foreach (const tg; test_groups) {
84         auto comp_container = root.addChild("div").addClass("comp_container");
85         auto heading = comp_container.addChild("h2").addClass("tbl_header");
86         heading.addChild("i").addClass("right");
87         heading.appendText(format(" %s (%s)", tg.description, tg.name));
88         comp_container.addChild("p", tg.userInput);
89         auto tbl_container = comp_container.addChild("div").addClass("tbl_container");
90         tbl_container.setAttribute("style", "display: none;");
91         auto tbl = tmplDefaultTable(tbl_container, [
92                 "Test Group", "Similarity", "Difference", "Intersection"
93                 ]);
94         foreach (const d; result.similarities[tg]) {
95             auto r = tbl.appendRow();
96             r.addChild("td", d.comparedTo.name);
97             r.addChild("td", format("%#.3s", d.similarity));
98             auto difference = r.addChild("td");
99             foreach (const mut; d.difference) {
100                 auto link = difference.addChild("a", mut.to!string);
101                 link.href = getPath(mut).get;
102                 difference.appendText(" ");
103             }
104             auto similarity = r.addChild("td");
105             foreach (const mut; d.intersection) {
106                 auto link = similarity.addChild("a", mut.to!string);
107                 link.href = getPath(mut).get;
108                 similarity.appendText(" ");
109             }
110         }
111     }
112 }