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 
15 import arsd.dom : Document, Element, require, Table, RawSource;
16 
17 import dextool.plugin.mutate.backend.database : Database;
18 import dextool.plugin.mutate.backend.report.analyzers : TestCaseSimilarityAnalyse,
19     reportTestCaseSimilarityAnalyse;
20 import dextool.plugin.mutate.backend.report.html.constants;
21 import dextool.plugin.mutate.backend.report.html.js;
22 import dextool.plugin.mutate.backend.report.html.tmpl : tmplBasicPage,
23     tmplDefaultTable, tmplDefaultMatrixTable;
24 import dextool.plugin.mutate.backend.type : Mutation;
25 import dextool.plugin.mutate.config : ConfigReport;
26 import dextool.plugin.mutate.type : MutationKind;
27 
28 auto makeTestCaseSimilarityAnalyse(ref Database db, ref const ConfigReport conf,
29         const(MutationKind)[] humanReadableKinds, const(Mutation.Kind)[] kinds) @trusted {
30     import std.datetime : Clock;
31 
32     auto doc = tmplBasicPage;
33 
34     auto s = doc.root.childElements("head")[0].addChild("script");
35     s.addChild(new RawSource(doc, js_similarity));
36 
37     doc.title(format("Test Case Similarity Analyse %(%s %) %s",
38             humanReadableKinds, Clock.currTime));
39     doc.mainBody.setAttribute("onload", "init()");
40     doc.mainBody.addChild("p", "This is the similarity between test cases.");
41     {
42         auto p = doc.mainBody.addChild("p");
43         p.addChild("b", "Note");
44         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.");
45     }
46 
47     toHtml(db, reportTestCaseSimilarityAnalyse(db, kinds, 5), doc.mainBody);
48 
49     return doc.toPrettyString;
50 }
51 
52 private:
53 
54 void toHtml(ref Database db, TestCaseSimilarityAnalyse result, Element root) {
55     import std.algorithm : sort, map;
56     import std.array : array;
57     import std.conv : to;
58     import std.path : buildPath;
59     import cachetools : CacheLRU;
60     import dextool.cachetools;
61     import dextool.plugin.mutate.backend.database : spinSql, MutationId;
62     import dextool.plugin.mutate.backend.report.html.page_files : pathToHtmlLink;
63     import dextool.type : Path;
64 
65     auto link_cache = new CacheLRU!(MutationId, string);
66     link_cache.ttl = 30; // magic number
67     Path getPath(MutationId id) {
68         return dextool.cachetools.cacheToolsRequire(link_cache, id, {
69             auto path = spinSql!(() => db.getPath(id)).get;
70             return format!"%s#%s"(buildPath(htmlFileDir, pathToHtmlLink(path)), id);
71         }()).Path;
72     }
73 
74     //const distances = result.distances.length;
75     const test_cases = result.similarities.byKey.array.sort!((a, b) => a < b).array;
76 
77     //auto mat = tmplDefaultMatrixTable(root, test_cases.map!(a => a.name.idup).array);
78 
79     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.")
80         .appendText(
81                 " The difference column are the mutants that are only killed by the test case in the heading.");
82     root.addChild("p",
83             "The tables are hidden by default, click on the corresponding header to display a table.");
84     with (root.addChild("button", "expand all")) {
85         setAttribute("type", "button");
86         setAttribute("id", "expand_all");
87     }
88     with (root.addChild("button", "collapse all")) {
89         setAttribute("type", "button");
90         setAttribute("id", "collapse_all");
91     }
92     foreach (const tc; test_cases) {
93         // Containers allows for hiding a table by clicking the corresponding header.
94         // Defaults to hiding tables.
95         auto comp_container = root.addChild("div").addClass("comp_container");
96         auto heading = comp_container.addChild("h2").addClass("tbl_header");
97         heading.addChild("i").addClass("right");
98         heading.appendText(" ");
99         heading.appendText(tc.name);
100         auto tbl_container = comp_container.addChild("div").addClass("tbl_container");
101         tbl_container.setAttribute("style", "display: none;");
102         auto tbl = tmplDefaultTable(tbl_container, [
103                 "Test Case", "Similarity", "Difference", "Intersection"
104                 ]);
105         foreach (const d; result.similarities[tc]) {
106             auto r = tbl.appendRow();
107             r.addChild("td", d.testCase.name);
108             r.addChild("td", format("%#.3s", d.similarity));
109             auto difference = r.addChild("td");
110             foreach (const mut; d.difference) {
111                 auto link = difference.addChild("a", mut.to!string);
112                 link.href = getPath(mut);
113                 difference.appendText(" ");
114             }
115             auto similarity = r.addChild("td");
116             foreach (const mut; d.intersection) {
117                 auto link = similarity.addChild("a", mut.to!string);
118                 link.href = getPath(mut);
119                 similarity.appendText(" ");
120             }
121         }
122     }
123 }