1 /*
2                                     __
3                                    / _|
4   __ _ _   _ _ __ ___  _ __ __ _  | |_ ___  ___ ___
5  / _` | | | | '__/ _ \| '__/ _` | |  _/ _ \/ __/ __|
6 | (_| | |_| | | | (_) | | | (_| | | || (_) \__ \__ \
7  \__,_|\__,_|_|  \___/|_|  \__,_| |_| \___/|___/___/
8 
9 Copyright (C) 2018-2020 Aurora Free Open Source Software.
10 Copyright (C) 2018-2020 Luís Ferreira <luis@aurorafoss.org>
11 
12 This file is part of the Aurora Free Open Source Software. This
13 organization promote free and open source software that you can
14 redistribute and/or modify under the terms of the GNU Lesser General
15 Public License Version 3 as published by the Free Software Foundation or
16 (at your option) any later version approved by the Aurora Free Open Source
17 Software Organization. The license is available in the package root path
18 as 'LICENSE' file. Please review the following information to ensure the
19 GNU Lesser General Public License version 3 requirements will be met:
20 https://www.gnu.org/licenses/lgpl.html .
21 
22 Alternatively, this file may be used under the terms of the GNU General
23 Public License version 3 or later as published by the Free Software
24 Foundation. Please review the following information to ensure the GNU
25 General Public License requirements will be met:
26 http://www.gnu.org/licenses/gpl-3.0.html.
27 
28 NOTE: All products, services or anything associated to trademarks and
29 service marks used or referenced on this file are the property of their
30 respective companies/owners or its subsidiaries. Other names and brands
31 may be claimed as the property of others.
32 
33 For more info about intellectual property visit: aurorafoss.org or
34 directly send an email to: contact (at) aurorafoss.org .
35 */
36 
37 module liblstparse.tests.parser;
38 
39 import aurorafw.unit.assertion;
40 
41 import core.exception;
42 
43 import liblstparse.parser;
44 
45 import std.array;
46 import std.exception;
47 import std.file;
48 import std.typecons;
49 import std.ascii;
50 
51 @safe @("No coverage")
52 unittest {
53 	LSTFile file = LSTFile(DirEntry("tests/res/nocov.lst"));
54 	assertEquals("nocov.d", file.filename);
55 	assertEquals(0, file.totalCoverage);
56 	assertEquals("@safe unittest {", file[3].content);
57 	assertTrue(file.linesCovered.empty);
58 	// check a non coverable file
59 	assertEquals(
60 		LSTFile.Line(Nullable!(uint).init, "module tests.res.nocov;"),
61 		file.lines[0]);
62 }
63 
64 @safe @("Zero coverage")
65 unittest {
66 	LSTFile file = LSTFile(DirEntry("tests/res/zerocov.lst"));
67 	assertEquals("zerocov.d", file.filename);
68 	assertEquals(0, file.totalCoverage);
69 	assertFalse(file.linesCovered.empty);
70 	assertEquals(0, file.linesCovered[4]);
71 }
72 
73 @safe @("Convertion failures")
74 unittest {
75 	auto overflow_file = `
76        |@safe int foo(int t) {
77 81723948712938471923874918273498273941872394|        return t * 2;
78        |}
79 covfile.d is 100% covered`[1 .. $];
80 	auto overflow2_file = `
81        |@safe int foo(int t) {
82       1|        return t * 2;
83        |}
84 covfile.d is 819723498712398471293% covered`[1 .. $];
85 	auto failconv_file = `
86        |@safe int foo(int t) {
87 notaninteger|        return t * 2;
88        |}
89 covfile.d is 100% covered`[1 .. $];
90 	auto failconv2_file = `
91        |@safe int foo(int t) {
92       1|        return t * 2;
93        |}
94 covfile.d is ups% covered`[1 .. $];
95 
96 	import std.conv : ConvException, ConvOverflowException;
97 	expectThrows!ConvOverflowException(LSTFile(overflow_file));
98 	expectThrows!ConvOverflowException(LSTFile(overflow2_file));
99 	expectThrows!ConvException(LSTFile(failconv_file));
100 	expectThrows!ConvException(LSTFile(failconv2_file));
101 }
102 
103 @safe @("Parsing failures")
104 unittest {
105 	// minimum valid lst files
106 	LSTFile("|\na.d has no code");
107 	LSTFile("1|int main(){return 0;}\nb.d is 100% covered");
108 
109 	LSTFileParseException ex;
110 
111 	// invalid files
112 	ex = expectThrows!LSTFileParseException(LSTFile("hey, I like turtles"));
113 	assertEquals("Minimum number of lines is 2. Probably not parsing .lst file", ex.msg);
114 	ex = expectThrows!LSTFileParseException(LSTFile("hey, I like turtles\n."));
115 	assertEquals("'|' separator not found. Probably not parsing .lst file", ex.msg);
116 	ex = expectThrows!LSTFileParseException(LSTFile("|\n 100"));
117 	assertEquals("The last line is not well formatted: missing '% covered'", ex.msg);
118 	ex = expectThrows!LSTFileParseException(
119 		LSTFile("1|int main(){return 0;}\nb.d iss 100% covered"));
120 	assertEquals("The last line is not well formatted: missing ' is '", ex.msg);
121 }
122 
123 @safe @("Merge coverage files")
124 unittest {
125 	// NOTE: This files doesn't represent real coverage
126 	// but should be totally valid LST files.
127 	auto covfile1 = `
128        |@safe int foo(int t) {
129       1|        return t * 2;
130        |}
131 covfile.d is 100% covered`[1 .. $];
132 	auto covfile2 = `
133        |@safe int foo(int t) {
134       2|        return t * 2;
135        |}
136 covfile.d is 0% covered`[1 .. $];
137 	auto covfile3 = `
138        |@safe int foo(int t) {
139        |        return t * 2;
140        |}
141 covfile.d is 0% covered`[1 .. $];
142 	auto diffcovfile1 = `
143        |@safe int foo(int t) {
144       0|        return t * 2;
145        |}
146 diffcovfile.d is 0% covered`[1 .. $];
147 	auto diffcovfile2 = `
148        |@safe int foobar(int t) {
149       0|        return t * 2;
150        |}
151 diffcovfile.d is 0% covered`[1 .. $];
152 	auto diffcovfile3 = `
153        |@safe int foobar(int t) {
154       0|        return t * 2;
155        |
156        |}
157 diffcovfile.d is 0% covered`[1 .. $];
158 
159 	LSTFile file1 = LSTFile(covfile1);
160 	LSTFile file2 = LSTFile(covfile2);
161 	LSTFile file3 = LSTFile(covfile3);
162 	LSTFile diff_file1 = LSTFile(diffcovfile1);
163 	LSTFile diff_file2 = LSTFile(diffcovfile2);
164 	LSTFile diff_file3 = LSTFile(diffcovfile3);
165 
166 	// successfully merge a file
167 	LSTFile merged = file1.merge(file2);
168 	assertEquals("covfile.d", merged.filename);
169 	assertEquals(100, merged.totalCoverage);
170 	assertFalse(merged.linesCovered.empty);
171 	assertEquals(3, merged.linesCovered[1]);
172 
173 	// merge coverable with non coverable lines
174 	merged = file2.merge(file3);
175 	assertEquals(2, merged.linesCovered[1]);
176 
177 	// cover usage of divide by 0 check
178 	merged = file3.merge(file3);
179 	assertEquals(0, merged.totalCoverage);
180 
181 
182 	LSTFileMergeException ex;
183 
184 	// different filename
185 	ex = expectThrows!LSTFileMergeException(LSTFile.merge(file1, diff_file1));
186 	assertEquals("should merge with the same file", ex.msg);
187 	// line length mismatch, not the same content
188 	ex = expectThrows!LSTFileMergeException(LSTFile.merge(diff_file1, diff_file3));
189 	assertEquals("lines length mismatch", ex.msg);
190 	// file content mismatch, not the same content
191 	ex = expectThrows!LSTFileMergeException(LSTFile.merge(diff_file1, diff_file2));
192 	assertEquals("content mismatch at line 1", ex.msg);
193 }
194 
195 @system @("Range violation")
196 unittest {
197 	LSTFile file = LSTFile.fromFilePath("tests/res/zerocov.lst");
198 	assertNotThrown!RangeError(file.linesCovered[4]);
199 	assertThrown!RangeError(file.linesCovered[3]);
200 }
201 
202 @safe @("File doesn't exist")
203 unittest {
204 	expectThrows!FileException(LSTFile(DirEntry("tests/res/file_not_found.lst")));
205 }
206 
207 @safe @("File with coverage")
208 unittest {
209 	import std.file : readText;
210 	auto txt = readText("tests/res/tocov.lst");
211 	LSTFile file = LSTFile(txt);
212 	assertEquals("tocov.d", file.filename);
213 	assertEquals(100, file.totalCoverage);
214 	assertFalse(file.linesCovered.empty);
215 	assertEquals(1, file.linesCovered[4]);
216 	assertEquals(file.linesCovered[4], file[4].coverage);
217 	assertEquals(1, file.linesCovered[9]);
218 	assertEquals(file.linesCovered[9], file[9].coverage);
219 
220 	// check a coverable file
221 	assertEquals(
222 		LSTFile.Line(nullable!uint(1), "        return t * 2;"),
223 		file.lines[4]
224 	);
225 }
226 
227 @safe @("Generate LST")
228 unittest
229 {
230 	auto covfile1 = (`
231        |@safe int foo(int t) {
232       1|        return t * 2;
233        |}
234 covfile.d is 100% covered` ~ newline)[1 .. $];
235 	auto covfile2 = (`
236        |@safe int foo(int t) {
237 0000000|        return t * 2;
238        |}
239 covfile.d is 0% covered` ~ newline)[1 .. $];
240 	auto covfile3 = (`
241        |@safe int foo(int t) {
242        |        return t * 2;
243        |}
244 covfile.d has no code` ~ newline)[1 .. $];
245 
246 	auto file1 = LSTFile(covfile1);
247 	auto file2 = LSTFile(covfile2);
248 	auto file3 = LSTFile(covfile3);
249 	auto emptyFile = LSTFile("");
250 
251 	// should generate the exact same file
252 	assertEquals(covfile1, file1.generateLST);
253 	assertEquals(covfile2, file2.generateLST);
254 	assertEquals(covfile3, file3.generateLST);
255 	assertEquals("", emptyFile.generateLST);
256 }