diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | extras/luacov/Makefile | 26 | ||||
-rw-r--r-- | extras/luacov/README.md | 79 | ||||
-rw-r--r-- | extras/luacov/doc/doc.css | 223 | ||||
-rw-r--r-- | extras/luacov/doc/index.html | 200 | ||||
-rw-r--r-- | extras/luacov/doc/license.html | 113 | ||||
-rw-r--r-- | extras/luacov/doc/luacov.png | bin | 0 -> 8420 bytes | |||
-rw-r--r-- | extras/luacov/luacov-0.2-1.rockspec | 28 | ||||
-rwxr-xr-x | extras/luacov/src/bin/luacov | 142 | ||||
-rw-r--r-- | extras/luacov/src/luacov.lua | 95 | ||||
-rw-r--r-- | extras/luacov/src/luacov/stats.lua | 73 | ||||
-rw-r--r-- | extras/luacov/src/luacov/tick.lua | 7 | ||||
-rw-r--r-- | test/test-clod.lua | 51 |
14 files changed, 1083 insertions, 0 deletions
@@ -1 +1,3 @@ *~ +luacov.report.out +luacov.stats.out diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e5be3ad --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +all: test + +MODULES := clod +LUA_VER := 5.1 + +INST_BASE := /usr/local +INST_ROOT := $(DESTDIR)$(INST_BASE)/share/lua/$(LUA_VER) + +MOD_FILES := $(patsubst %,%.lua,$(subst .,/,$(MODULES))) + +install: + for MOD in $(sort $(MOD_FILES)); do \ + cp lib/$${MOD} $(INST_ROOT)/$${MOD}; \ + done + +LUA := LUA_PATH="$(shell pwd)/lib/?.lua;$(shell pwd)/extras/luacov/src/?.lua;;" lua$(LUA_VER) + +clean: + $(RM) luacov.report.out luacov.stats.out + +distclean: clean + find . -name "*~" -delete + +.PHONY: example +example: + $(LUA) example/simple-config-reader.lua + $(LUA) example/change-setting.lua + $(LUA) example/config-passthru.lua + +.PHONY: test +test: + @$(RM) luacov.stats.out + @ERR=0; \ + for MOD in $(sort $(MODULES)); do \ + echo -n "$${MOD}: "; \ + $(LUA) test/test-$${MOD}.lua; \ + test "x$$?" = "x0" || ERR=1; \ + done; \ + $(LUA) extras/luacov/src/bin/luacov -X luacov. -X test. $(MODULES); \ + exit $$ERR + +.PHONY: interactive +interactive: + $(LUA) -e'clod=require"clod"' -i diff --git a/extras/luacov/Makefile b/extras/luacov/Makefile new file mode 100644 index 0000000..1518bbf --- /dev/null +++ b/extras/luacov/Makefile @@ -0,0 +1,26 @@ + +PACKAGE=luacov +VERSION=0.3 +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +LUADIR=$(PREFIX)/share/lua/5.1/ + +install: + mkdir -p $(BINDIR) + cp src/bin/luacov $(BINDIR) + mkdir -p $(LUADIR) + cp src/luacov.lua $(LUADIR) + mkdir -p $(LUADIR)/luacov + cp src/luacov/stats.lua $(LUADIR)/luacov + cp src/luacov/tick.lua $(LUADIR)/luacov + +dist: + rm -f dist.files + rm -rf $(PACKAGE)-$(VERSION) + rm -f $(PACKAGE)-$(VERSION).tar.gz + find * | grep -v CVS > dist.files + mkdir -p $(PACKAGE)-$(VERSION) + cpio -p $(PACKAGE)-$(VERSION) < dist.files + tar czvf $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION) + rm -f dist.files + rm -rf $(PACKAGE)-$(VERSION) diff --git a/extras/luacov/README.md b/extras/luacov/README.md new file mode 100644 index 0000000..efc31c5 --- /dev/null +++ b/extras/luacov/README.md @@ -0,0 +1,79 @@ +## Overview + +LuaCov is a simple coverage analyzer for [Lua](http://www.lua.org) +scripts. When a Lua script is run with the `luacov` module loaded, it +generates a stats file with the number of executions of each line of the +script and its loaded modules. The `luacov` command-line script then +processes this file generating a report file which allows one to visualize +which code paths were not traversed, which is useful for verifying the +effectiveness of a test suite. + +LuaCov is free software and, like Lua, is released under the +[MIT License](http://www.lua.org/license.html). + +## Download and Installation + +LuaCov can be downloaded from its +[LuaForge page](http://luaforge.net/projects/luacov/files). + +It can also be installed using Luarocks: + + luarocks install luacov + +LuaCov is written in pure Lua and has no external dependencies. + +## Instructions + +Using LuaCov consists of two steps: running your script to collect +coverage data, and then running `luacov` on the collected data to +generate a report. + +To collect coverage data, your script needs to load the `luacov` +Lua module. This can be done from the command-line, without modifying +your script, like this: + + lua -lluacov test.lua + +Alternatively, you can add `require("luacov")` to the first line +of your script. + +Once the script is run, a file called `lcov.stats.out` is generated. +If the file already exists, statistics are _added_ to it. This is useful, +for example, for making a series of runs with different input parameters in +a test suite. To start the accounting from scratch, just delete the stats file. + +To generate a report, just run the `luacov` command-line script. +It expects to find a file named `lcov.stats.out` in the current +directory, and outputs a file named `lcov.report.out`. + +This is an example output of the report file: + + ============================================================ + ../test.lua + ============================================================ + + -- Which branch will run? + 1 if 10 > 100 then + 0 print("I don't think this line will execute.") + 0 else + 1 print("Hello, LuaCov!") + 1 end + +Note that to generate this report, `luacov` reads the source files. +Therefore, it expects to find them in the same location they were when +the `luacov` module ran (the stats file stores the filenames, but +not the sources themselves). + +LuaCov saves its stats upon normal program termination. If your program +is a daemon -- in other words, if it does not terminate normally -- you +can use the `luacov.tick` module, which periodically saves the +stats file. For example, to run (on Unix systems) LuaCov on +[Xavante](http://www.keplerproject.org/xavante), +just modify the first line of `xavante_start.lua` so it reads: + + #!/usr/bin/env lua -lluacov.tick + +## Credits + +LuaCov was designed and implemented by Hisham Muhammad as a tool for +testing [Luarocks](http://luarocks.luaforge.net). diff --git a/extras/luacov/doc/doc.css b/extras/luacov/doc/doc.css new file mode 100644 index 0000000..3770e4e --- /dev/null +++ b/extras/luacov/doc/doc.css @@ -0,0 +1,223 @@ +body {
+ margin-left: 1em;
+ margin-right: 1em;
+ font-family: arial, helvetica, geneva, sans-serif;
+ background-color:#ffffff; margin:0px;
+}
+
+code {
+ font-family: "Andale Mono", monospace;
+}
+
+tt {
+ font-family: "Andale Mono", monospace;
+}
+
+body, td, th { font-size: 11pt; }
+
+h1, h2, h3, h4 { margin-left: 0em; }
+
+textarea, pre, tt { font-size:10pt; }
+body, td, th { color:#000000; }
+small { font-size:0.85em; }
+h1 { font-size:1.5em; }
+h2 { font-size:1.25em; }
+h3 { font-size:1.15em; }
+h4 { font-size:1.06em; }
+
+a:link { font-weight:bold; color: #004080; text-decoration: none; }
+a:visited { font-weight:bold; color: #006699; text-decoration: none; }
+a:link:hover { text-decoration:underline; }
+hr { color:#cccccc }
+img { border-width: 0px; }
+
+
+h3 { padding-top: 1em; }
+
+p { margin-left: 1em; }
+
+p.name {
+ font-family: "Andale Mono", monospace;
+ padding-top: 1em;
+ margin-left: 0em;
+}
+
+blockquote { margin-left: 3em; }
+
+.example {
+ background-color: rgb(245, 245, 245);
+ border-top-width: 1px;
+ border-right-width: 1px;
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ border-top-style: solid;
+ border-right-style: solid;
+ border-bottom-style: solid;
+ border-left-style: solid;
+ border-top-color: silver;
+ border-right-color: silver;
+ border-bottom-color: silver;
+ border-left-color: silver;
+ padding: 1em;
+ margin-left: 1em;
+ margin-right: 1em;
+ font-family: "Andale Mono", monospace;
+ font-size: smaller;
+}
+
+
+hr {
+ margin-left: 0em;
+ background: #00007f;
+ border: 0px;
+ height: 1px;
+}
+
+ul { list-style-type: disc; }
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+table.index ul { padding-top: 0em; margin-top: 0em; }
+
+table {
+ border: 1px solid black;
+ border-collapse: collapse;
+ margin-left: auto;
+ margin-right: auto;
+}
+th {
+ border: 1px solid black;
+ padding: 0.5em;
+}
+td {
+ border: 1px solid black;
+ padding: 0.5em;
+}
+div.header, div.footer { margin-left: 0em; }
+
+#container
+{
+ margin-left: 1em;
+ margin-right: 1em;
+ background-color: #f0f0f0;
+}
+
+#product
+{
+ text-align: center;
+ border-bottom: 1px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#product big {
+ font-size: 2em;
+}
+
+#product_logo
+{
+}
+
+#product_name
+{
+}
+
+#product_description
+{
+}
+
+#main
+{
+ background-color: #f0f0f0;
+ border-left: 2px solid #cccccc;
+}
+
+#navigation
+{
+ float: left;
+ width: 12em;
+ margin: 0;
+ vertical-align: top;
+ background-color: #f0f0f0;
+ overflow:visible;
+}
+
+#navigation h1 {
+ background-color:#e7e7e7;
+ font-size:1.1em;
+ color:#000000;
+ text-align:left;
+ margin:0px;
+ padding:0.2em;
+ border-top:1px solid #dddddd;
+ border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ padding: 0;
+ margin: 1px;
+}
+
+#navigation li
+{
+ text-indent: -1em;
+ margin: 0em 0em 0em 0.5em;
+ display: block;
+ padding: 3px 0px 0px 12px;
+}
+
+#navigation li li a
+{
+ padding: 0px 3px 0px -1em;
+}
+
+#content
+{
+ margin-left: 12em;
+ padding: 1em;
+ border-left: 2px solid #cccccc;
+ border-right: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#about
+{
+ clear: both;
+ margin: 0;
+ padding: 5px;
+ border-top: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+@media print {
+ body {
+ font: 10pt "Times New Roman", "TimeNR", Times, serif;
+ }
+ a { font-weight:bold; color: #004080; text-decoration: underline; }
+
+ #main { background-color: #ffffff; border-left: 0px; }
+ #container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; }
+
+ #content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; }
+
+ #navigation { display: none;
+ }
+
+ #product_logo
+ {
+ display: none;
+ }
+
+ #about img
+ {
+ display: none;
+ }
+
+ .example {
+ font-family: "Andale Mono", monospace;
+ font-size: 8pt;
+ page-break-inside: avoid;
+ }
+}
diff --git a/extras/luacov/doc/index.html b/extras/luacov/doc/index.html new file mode 100644 index 0000000..ed451a2 --- /dev/null +++ b/extras/luacov/doc/index.html @@ -0,0 +1,200 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<head> + <title>LuaCov - Coverage analysis for Lua scripts</title> + <link rel="stylesheet" href="doc.css" type="text/css" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<style type="text/css"> +pre { + font-family: "Andale Mono", monospace; +} +div.Example { + font-family: "Andale Mono", monospace; + background-color: #f0f0f0; + margin-left: 1em; + margin-right: 1em; + margin-top: 0.5em; + margin-bottom 0.5em; + padding: 0.5em; +} +</style> +</head> + +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"><a href="http://luacov.luaforge.net"> + <img alt="LuaCov logo" src="luacov.png" /> + </a></div> + <div id="product_name"><big><strong>LuaCov</strong></big></div> + <div id="product_description">Coverage analysis for Lua scripts</div> +</div> <!-- id="product" --> + +<div id="main"> + +<div id="navigation"> + +<h1>LuaCov</h1> + +<ul> + <li><strong>Home</strong> + <ul> + <li> <a href="index.html#over">Overview</a></li> + <li> <a href="index.html#download">Download</a></li> + <li> <a href="index.html#instructions">Instructions</a></li> + <li> <a href="index.html#history">History</a></li> + <li> <a href="index.html#credits">Credits</a></li> + <li> <a href="index.html#contact">Contact</a></li> + </ul> + </li> + <li><a href="http://luaforge.net/projects/luacov/">Project</a> + <ul> + <li><a href="http://luaforge.net/scm/?group_id=290">CVS</a></li> + </ul> + </li> + <li><a href="license.html">License</a></li> +</ul> + +</div> <!-- id="navigation" --> + +<div id="content"> + +<h2><a name="over"></a>Overview</h2> + +<p> +LuaCov is a simple coverage analyzer for <a href="http://www.lua.org">Lua</a> +scripts. When a Lua script is run with the <tt>luacov</tt> module loaded, it +generates a stats file with the number of executions of each line of the +script and its loaded modules. The <tt>luacov</tt> command-line script then +processes this file generating a report file which allows one to visualize +which code paths were not traversed, which is useful for verifying the +effectiveness of a test suite. +</p> + +<p> +LuaCov is free software and uses the same <a href="license.html">license</a> as Lua. +</p> + +<h2><a name="download"></a>Download</h2> + +<p> +LuaCov can be downloaded from its +<a href="http://luaforge.net/projects/luacov/files/">LuaForge page</a>. +</p> + +<p> +LuaCov is written in pure Lua and has no external dependencies. +</p> + +<h2><a name="instructions"></a>Instructions</h2> + +<p> +Using LuaCov consists of two steps: running your script to collect +coverage data, and then running <tt>luacov</tt> on the collected data to +generate a report. +</p> + +<p> +To collect coverage data, your script needs to load the <tt>luacov</tt> +Lua module. This can be done from the command-line, without modifying +your script, like this: +</p> + +<div class="Example"> +lua -lluacov test.lua +</div> + +<p> +(Alternatively, you can add <tt>require("luacov")</tt> to the first line +of your script.) +</p> + +<p> +Once the script is run, a file called <tt>lcov.stats.out</tt> is generated. +If the file already exists, statistics are <i>added</i> to it. This is useful, +for example, for making a series of runs with different input parameters in +a test suite. To start the accounting from scratch, just delete the stats file. +</p> + +<p> +To generate a report, just run the <tt>luacov</tt> command-line script. +It expects to find a file named <tt>lcov.stats.out</tt> in the current +directory, and outputs a file named <tt>lcov.report.out</tt>. +</p> + +<p>This is an example output of the report file:</p> + +<div class="Example"><pre> +============================================================ +../test.lua +============================================================ + + -- Which branch will run? +1 if 10 > 100 then +0 print("I don't think this line will execute.") +0 else +1 print("Hello, LuaCov!") +1 end +</pre></div> + +<p> +Note that to generate this report, <tt>luacov</tt> reads the source files. +Therefore, it expects to find them in the same location they were when +the <tt>luacov</tt> module ran (the stats file stores the filenames, but +not the sources themselves). +</p> + +<p> +LuaCov saves its stats upon normal program termination. If your program +is a daemon -- in other words, if it does not terminate normally -- you +can use the <tt>luacov.tick</tt> module, which periodically saves the +stats file. For example, to run (on Unix systems) LuaCov on +<a href="http://www.keplerproject.org/xavante">Xavante</a>, +just modify the first line of <tt>xavante_start.lua</tt> so it reads: +</p> + +<div class="Example"> +#!/usr/bin/env lua -lluacov.tick +</div> + +<h2><a name="history"></a>History</h2> + +<dl> + <dt><strong>0.1</strong> [July 16, 2007]</dt> + <dd> + <ul> + <li>Initial release.</li> + </ul> + </dd> +</dl> + +<h2><a name="credits"></a>Credits</h2> + +<p> +LuaCov was designed and implemented by Hisham Muhammad as a tool for +testing <a href="http://luarocks.luaforge.net">LuaRocks</a>. +</p> + +<h2><a name="contact"></a>Contact</h2> + +<p> +For more information please +<a href="mailto:hisham-NO-SPAM-THANKS@gobolinux.org">contact</a> us. Comments are welcome! +</p> + +</div> <!-- id="content" --> + +</div> <!-- id="main" --> + +<div id="about"> + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p> +</div> <!-- id="about" --> + +</div> <!-- id="container" --> + +</body> +</html> diff --git a/extras/luacov/doc/license.html b/extras/luacov/doc/license.html new file mode 100644 index 0000000..afbb9f6 --- /dev/null +++ b/extras/luacov/doc/license.html @@ -0,0 +1,113 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<head> + <title>LuaCov - Coverage analysis for Lua scripts</title> + <link rel="stylesheet" href="doc.css" type="text/css" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +</head> + +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"><a href="http://luacov.luaforge.net"> + <img alt="LuaCov logo" src="luacov.png" /> + </a></div> + <div id="product_name"><big><b>LuaCov</b></big></div> + <div id="product_description">Coverage analysis for Lua scripts</div> +</div> <!-- id="product" --> + +<div id="main"> + +<div id="navigation"> + +<h1>LuaCov</h1> + +<ul> + <li><a href="index.html">Home</a> + <ul> + <li> <a href="index.html#over">Overview</a></li> + <li> <a href="index.html#download">Download</a></li> + <li> <a href="index.html#dependencies">Instructions</a></li> + <li> <a href="index.html#history">History</a></li> + <li> <a href="index.html#credits">Credits</a></li> + <li> <a href="index.html#contact">Contact</a></li> + </ul> + </li> + <li><a href="http://luaforge.net/projects/luacov/">Project</a> + <ul> + <li><a href="http://luaforge.net/scm/?group_id=290">CVS</a></li> + </ul> + </li> + <li><strong>License</strong></li> +</ul> + +</div> <!-- id="navigation" --> + +<div id="content"> + +<h2>License</h2> + +<p> +LuaCov is free software: it can be used for both academic and commercial purposes +at absolutely no cost. There are no royalties or GNU-like "copyleft" restrictions. +LuaCov qualifies as <a href="http://www.opensource.org/docs/definition.html">Open Source</a> software. +Its licenses are compatible with <a href="http://www.gnu.org/licenses/gpl.html">GPL</a>. +LuaCov is not in the public domain. The legal details are below. +</p> + +<p> +The spirit of the license is that you are free to use LuaCov for any purpose +at no cost without having to ask us. The only requirement is that if you do use +LuaCov, then you should give us credit by including the appropriate copyright notice +somewhere in your product or its documentation. +</p> + +<p> +LuaCov is designed and implemented by Hisham Muhammad. +The implementation is not derived from licensed software. +</p> + +<hr/> + +<p>Copyright (c) 2007 Hisham Muhammad.</p> + +<p> +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +</p> + +<p> +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +</p> + +<p> +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +</p> + +</div> <!-- id="content" --> + +</div> <!-- id="main" --> + +<div id="about"> + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p> +</div> <!-- id="about" --> + +</div> <!-- id="container" --> + +</body> +</html> diff --git a/extras/luacov/doc/luacov.png b/extras/luacov/doc/luacov.png Binary files differnew file mode 100644 index 0000000..1cf37b7 --- /dev/null +++ b/extras/luacov/doc/luacov.png diff --git a/extras/luacov/luacov-0.2-1.rockspec b/extras/luacov/luacov-0.2-1.rockspec new file mode 100644 index 0000000..37eff94 --- /dev/null +++ b/extras/luacov/luacov-0.2-1.rockspec @@ -0,0 +1,28 @@ +package = "LuaCov" +version = "0.2-1" +source = { + url = "http://luaforge.net/frs/download.php/4053/luacov-0.2.tar.gz" +} +description = { + summary = "Coverage analysis tool for Lua scripts", + detailed = [[ + LuaCov is a simple coverage analysis tool for Lua scripts. + When a Lua script is run with the luacov module, it + generates a stats file. The luacov command-line script then + processes this file generating a report indicating which code + paths were not traversed, which is useful for verifying the + effectiveness of a test suite. + ]], + license = "MIT/X11", + homepage = "http://luacov.luaforge.net/" +} +dependencies = { + "lua >= 5.0", +} +build = { + type = "make", + variables = { + LUADIR = "$(LUADIR)", + BINDIR = "$(BINDIR)" + } +} diff --git a/extras/luacov/src/bin/luacov b/extras/luacov/src/bin/luacov new file mode 100755 index 0000000..6a24bc6 --- /dev/null +++ b/extras/luacov/src/bin/luacov @@ -0,0 +1,142 @@ +#!/usr/bin/env lua + +local luacov = require("luacov.stats") + +local data, most_hits = luacov.load() + +if not data then + print("Could not load stats file "..luacov.statsfile..".") + print("Run your Lua program with -lluacov and then rerun luacov.") + os.exit(1) +end + +local report = io.open("luacov.report.out", "w") + +-- only report on files specified on the command line +local patterns = {} +local exclude_patterns = {} +local exclude_next = false +for i = 1, #arg do + if arg[i] == "-X" then + exclude_next = true + else + if exclude_next then + exclude_patterns[#exclude_patterns+1] = arg[i] + exclude_next = false + else + patterns[#patterns+1] = arg[i] + end + end +end + +local names = {} +for filename, _ in pairs(data) do + if not patterns[1] then + table.insert(names, filename) + else + local path = filename:gsub("/", "."):gsub("%.lua$", "") + local include = false + for _, p in ipairs(patterns) do + if path:match(p) then + include = true + break + end + end + if include then + for _, p in ipairs(exclude_patterns) do + if path:match(p) then + include = false + break + end + end + end + if include then + table.insert(names, filename) + end + end +end + +table.sort(names) + +local most_hits_length = ("%d"):format(most_hits):len() +local empty_format = (" "):rep(most_hits_length+1) +local false_negative_format = ("!%% %dd"):format(most_hits_length) +local zero_format = ("*"):rep(most_hits_length).."0" +local count_format = ("%% %dd"):format(most_hits_length+1) + +local exclusions = +{ + { false, "^#!" }, -- Unix hash-bang magic line + { true, "" }, -- Empty line + { true, "}" }, -- Just a close-brace + { true, "end,?" }, -- Single "end" + { true, "else" }, -- Single "else" + { true, "repeat" }, -- Single "repeat" + { true, "do" }, -- Single "do" + { true, "local%s+[%w_,%s]+" }, -- "local var1, ..., varN" + { true, "local%s+[%w_,%s]+%s*=" }, -- "local var1, ..., varN =" + { true, "local%s+function%s*%([%w_,%.%s]*%)" }, -- "local function(arg1, ..., argN)" + { true, "local%s+function%s+[%w_]*%s*%([%w_,%.%s]*%)" }, -- "local function f (arg1, ..., argN)" +} + +local function excluded(line) + for _, e in ipairs(exclusions) do + if e[1] then + if line:match("^%s*"..e[2].."%s*$") or line:match("^%s*"..e[2].."%s*%-%-") then return true end + else + if line:match(e[2]) then return true end + end + end + return false +end + +for _, filename in ipairs(names) do + local filedata = data[filename] + local file = io.open(filename, "r") + if file then + report:write("\n") + report:write("==============================================================================\n") + report:write(filename, "\n") + report:write("==============================================================================\n") + local line_nr = 1 + block_comment, equals = false, "" + while true do + local line = file:read("*l") + if not line then break end + local true_line = line + + local new_block_comment = false + if not block_comment then + local l, equals = line:match("^(.*)%-%-%[(=*)%[") + if l then + line = l + new_block_comment = true + end + else + local l = line:match("%]"..equals.."%](.*)$") + if l then + line = l + block_comment = false + end + end + + local hits = filedata[line_nr] or 0 + if block_comment or excluded(line) then + if hits > 0 then + report:write(false_negative_format:format(hits)) + else + report:write(empty_format) + end + else + if hits == 0 then + report:write(zero_format) + else + report:write(count_format:format(hits)) + end + end + report:write("\t", true_line, "\n") + if new_block_comment then block_comment = true end + line_nr = line_nr + 1 + end + end +end diff --git a/extras/luacov/src/luacov.lua b/extras/luacov/src/luacov.lua new file mode 100644 index 0000000..a44333a --- /dev/null +++ b/extras/luacov/src/luacov.lua @@ -0,0 +1,95 @@ + +local M = {} + +local stats = require("luacov.stats") +local data = stats.load() +local statsfile = stats.start() +M.statsfile = statsfile + +local tick = package.loaded["luacov.tick"] +local ctr = 0 +local luacovlock = os.tmpname() + +local booting = true +local skip = {} +M.skip = skip + +local function on_line(_, line_nr) + if tick then + ctr = ctr + 1 + if ctr == 100 then + ctr = 0 + stats.save(data, statsfile) + end + end + + -- get name of processed file; ignore Lua code loaded from raw strings + local name = debug.getinfo(2, "S").source + if not name:match("^@") then + return + end + name = name:sub(2) + + -- skip 'luacov.lua' in coverage report + if booting then + skip[name] = true + booting = false + end + + if skip[name] then + return + end + + local file = data[name] + if not file then + file = {max=0} + data[name] = file + end + if line_nr > file.max then + file.max = line_nr + end + file[line_nr] = (file[line_nr] or 0) + 1 +end + +local function on_exit() + os.remove(luacovlock) + stats.save(data, statsfile) + stats.stop(statsfile) +end + +local function init() + if not tick then + M.on_exit_trick = io.open(luacovlock, "w") + debug.setmetatable(M.on_exit_trick, { __gc = on_exit } ) + end + + debug.sethook(on_line, "l") + + local rawcoroutinecreate = coroutine.create + coroutine.create = function(...) + local co = rawcoroutinecreate(...) + debug.sethook(co, on_line, "l") + return co + end + coroutine.wrap = function(...) + local co = rawcoroutinecreate(...) + debug.sethook(co, on_line, "l") + return function() + local r = { coroutine.resume(co) } + if not r[1] then + error(r[2]) + end + return unpack(r, 2) + end + end + + local rawexit = os.exit + os.exit = function(...) + on_exit() + rawexit(...) + end +end + +init() + +return M
diff --git a/extras/luacov/src/luacov/stats.lua b/extras/luacov/src/luacov/stats.lua new file mode 100644 index 0000000..5390c75 --- /dev/null +++ b/extras/luacov/src/luacov/stats.lua @@ -0,0 +1,73 @@ + +local M = {} + +local statsfile = "luacov.stats.out" +local stats + +function M.load() + local data, most_hits = {}, 0 + stats = io.open(statsfile, "r") + if not stats then + return data + end + while true do + local nlines = stats:read("*n") + if not nlines then + break + end + local skip = stats:read(1) + if skip ~= ":" then + break + end + local filename = stats:read("*l") + if not filename then + break + end + data[filename] = { + max=nlines + } + for i = 1, nlines do + local hits = stats:read("*n") + if not hits then + break + end + local skip = stats:read(1) + if skip ~= " " then + break + end + if hits > 0 then + data[filename][i] = hits + most_hits = math.max(most_hits, hits) + end + end + end + stats:close() + return data, most_hits +end + +function M.start() + return io.open(statsfile, "w") +end + +function M.stop(stats) + stats:close() +end + +function M.save(data, stats) + stats:seek("set") + for filename, filedata in pairs(data) do + local max = filedata.max + stats:write(max, ":", filename, "\n") + for i = 1, max do + local hits = filedata[i] + if not hits then + hits = 0 + end + stats:write(hits, " ") + end + stats:write("\n") + end + stats:flush() +end + +return M diff --git a/extras/luacov/src/luacov/tick.lua b/extras/luacov/src/luacov/tick.lua new file mode 100644 index 0000000..624cae3 --- /dev/null +++ b/extras/luacov/src/luacov/tick.lua @@ -0,0 +1,7 @@ + +--- Load luacov using this if you want it to periodically +-- save the stats file. This is useful if your script is +-- a daemon (ie, does not properly terminate.) +module("luacov.tick", package.seeall) + +require("luacov") diff --git a/test/test-clod.lua b/test/test-clod.lua new file mode 100644 index 0000000..b453dba --- /dev/null +++ b/test/test-clod.lua @@ -0,0 +1,51 @@ +-- test/test-clod.lua +-- +-- Configuration languge organised (by) dots. +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> +-- +-- For Licence terms, see COPYING +-- + +-- Step one, start coverage + +local luacov = require 'luacov' + +local clod = require 'clod' + +local testnames = {} + +local real_assert = assert +local total_asserts = 0 +local function assert(...) + local retval = real_assert(...) + total_asserts = total_asserts + 1 + return retval +end + +local function add_test(suite, name, value) + rawset(suite, name, value) + testnames[#testnames+1] = name +end + +local suite = setmetatable({}, {__newindex = add_test}) + +function suite.test_parse_empty() + local conf = assert(clod.parse("")) +end + +local count_ok = 0 +for _, testname in ipairs(testnames) do +-- print("Run: " .. testname) + local ok, err = xpcall(suite[testname], debug.traceback) + if not ok then + print(err) + print() + else + count_ok = count_ok + 1 + end +end + +print(tostring(count_ok) .. "/" .. tostring(#testnames) .. " [" .. tostring(total_asserts) .. "] OK") + +os.exit(count_ok == #testnames and 0 or 1) |