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.html.constants;
19 import dextool.plugin.mutate.backend.report.html.js;
20 import dextool.plugin.mutate.backend.report.html.tmpl : tmplBasicPage,
21     tmplDefaultTable, tmplDefaultMatrixTable;
22 import dextool.plugin.mutate.backend.report.utility;
23 import dextool.plugin.mutate.backend.type : Mutation;
24 import dextool.plugin.mutate.config : ConfigReport;
25 import dextool.plugin.mutate.type : MutationKind;
26 
27 auto makeTestCaseSimilarityAnalyse(ref Database db, ref const ConfigReport conf,
28         const(MutationKind)[] humanReadableKinds, const(Mutation.Kind)[] kinds) @trusted {
29     import std.datetime : Clock;
30 
31     auto doc = tmplBasicPage;
32     
33     auto s = doc.root.childElements("head")[0].addChild("script");
34     s.addChild(new RawSource(doc, js_similarity));
35     
36     doc.title(format("Test Case Similarity Analyse %(%s %) %s",
37             humanReadableKinds, Clock.currTime));
38     doc.mainBody.setAttribute("onload", "init()");
39     doc.mainBody.addChild("p", "This is the similarity between test cases.")
40         .appendText(" The closer to 1.0 the more similare the test cases are in what they verify.");
41     {
42         auto p = doc.mainBody.addChild("p");
43         p.addChild("b", "Note");
44         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.");
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 : spinSqlQuery, 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 = spinSqlQuery!(() => db.getPath(id));
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", "The tables are hidden by default, click on the corresponding header to display a table.");
83     foreach (const tc; test_cases) {
84         // Containers allows for hiding a table by clicking the corresponding header.
85         // Defaults to hiding tables.
86         auto comp_container = root.addChild("div").addClass("comp_container");
87         comp_container.addChild("h2", tc.name).addClass("tbl_header");
88         auto tbl_container = comp_container.addChild("div").addClass("tbl_container");
89         tbl_container.setAttribute("style", "display: none;");
90         auto tbl = tmplDefaultTable(tbl_container, [
91                 "Test Case", "Similarity", "Difference", "Intersection"
92                 ]);
93         foreach (const d; result.similarities[tc]) {
94             auto r = tbl.appendRow();
95             r.addChild("td", d.testCase.name);
96             r.addChild("td", format("%#.3s", d.similarity));
97             auto difference = r.addChild("td");
98             foreach (const mut; d.difference) {
99                 auto link = difference.addChild("a", mut.to!string);
100                 link.href = getPath(mut);
101                 difference.appendText(" ");
102             }
103             auto similarity = r.addChild("td");
104             foreach (const mut; d.intersection) {
105                 auto link = similarity.addChild("a", mut.to!string);
106                 link.href = getPath(mut);
107                 similarity.appendText(" ");
108             }
109         }
110     }
111 }