summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham.hm@gmail.com>2007-07-16 04:45:13 +0000
committerHisham Muhammad <hisham.hm@gmail.com>2007-07-16 04:45:13 +0000
commit9da6972f4287d68b35b00db5650a69426d4a01c5 (patch)
tree3dc5836a161e6e2c5ed976ec3721ec8307377a71
downloadluacov-9da6972f4287d68b35b00db5650a69426d4a01c5.tar.gz
Initial revision
-rwxr-xr-xMakefile14
-rwxr-xr-xdoc/doc.css223
-rwxr-xr-xdoc/index.html188
-rwxr-xr-xdoc/license.html116
-rwxr-xr-xdoc/luacov.pngbin0 -> 8420 bytes
-rwxr-xr-xsrc/bin/luacov49
-rwxr-xr-xsrc/luacov.lua53
-rwxr-xr-xsrc/luacov/stats.lua56
-rwxr-xr-xsrc/luacov/tick.lua7
9 files changed, 706 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..5f85cfb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+
+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
+
diff --git a/doc/doc.css b/doc/doc.css
new file mode 100755
index 0000000..3770e4e
--- /dev/null
+++ b/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/doc/index.html b/doc/index.html
new file mode 100755
index 0000000..c31261b
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,188 @@
+<!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>
+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/tracker/?group_id=11">Bug Tracker</a></li>
+ <li><a href="http://luaforge.net/scm/?group_id=11">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>
+
+<h2><a name="history"></a>History</h2>
+
+<dl>
+ <dt><strong>0.1</strong> [xx/Jul/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/doc/license.html b/doc/license.html
new file mode 100755
index 0000000..b2c14dd
--- /dev/null
+++ b/doc/license.html
@@ -0,0 +1,116 @@
+<!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/tracker/?group_id=11">Bug Tracker</a></li>
+ <li><a href="http://luaforge.net/scm/?group_id=11">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 and the <a href="http://www.keplerproject.org/">Kepler Project</a>
+keep its copyright. 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>
+ <p><small>$Id: license.html,v 1.1 2007/07/16 04:45:19 hisham Exp $</small></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" -->
+
+</body>
+</html>
diff --git a/doc/luacov.png b/doc/luacov.png
new file mode 100755
index 0000000..1cf37b7
--- /dev/null
+++ b/doc/luacov.png
Binary files differ
diff --git a/src/bin/luacov b/src/bin/luacov
new file mode 100755
index 0000000..517680c
--- /dev/null
+++ b/src/bin/luacov
@@ -0,0 +1,49 @@
+#!/usr/bin/env lua
+
+local luacov = require("luacov.stats")
+
+local data = luacov.load_stats()
+
+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")
+
+local names = {}
+for filename, _ in pairs(data) do
+ table.insert(names, filename)
+end
+
+table.sort(names)
+
+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
+ while true do
+ local line = file:read("*l")
+ if not line then break end
+ if line:match("^%s*%-%-") -- Comment line
+ or line:match("^%s*$") -- Empty line
+ or line:match("^%s*end,?%s*$") -- Single "end"
+ or line:match("^%s*else%s*$") -- Single "else"
+ or line:match("^#!") -- Unix hash-bang magic line
+ then
+ report:write("\t", line, "\n")
+ else
+ local hits = filedata[line_nr]
+ if not hits then hits = 0 end
+ report:write(hits, "\t", line, "\n")
+ end
+ line_nr = line_nr + 1
+ end
+ end
+end
diff --git a/src/luacov.lua b/src/luacov.lua
new file mode 100755
index 0000000..21b8d12
--- /dev/null
+++ b/src/luacov.lua
@@ -0,0 +1,53 @@
+
+module("luacov", package.seeall)
+
+local stats = require("luacov.stats")
+
+data = stats.load_stats()
+
+statsfile = stats.start_stats()
+
+tick = package.loaded["luacov.tick"]
+ctr = 0
+
+local function on_line(_, line_nr)
+ if tick then
+ ctr = ctr + 1
+ if ctr == 100 then
+ ctr = 0
+ stats.save_stats(data, statsfile)
+ end
+ end
+
+ local name = debug.getinfo(2, "S").short_src
+ local file = data[name]
+ if not file then
+ file = {}
+ file.max = 0
+ data[name] = file
+ end
+ if line_nr > file.max then
+ file.max = line_nr
+ end
+ local current = file[line_nr]
+ if not current then
+ file[line_nr] = 1
+ else
+ file[line_nr] = current + 1
+ end
+end
+
+local luacovlock = os.tmpname()
+
+function on_exit()
+ os.remove(luacovlock)
+ stats.save_stats(data, statsfile)
+ stats.stop_stats(statsfile)
+end
+
+if not tick then
+ on_exit_trick = io.open(luacovlock, "w")
+ debug.setmetatable(on_exit_trick, { __gc = on_exit } )
+end
+
+debug.sethook(on_line, "l")
diff --git a/src/luacov/stats.lua b/src/luacov/stats.lua
new file mode 100755
index 0000000..fbf1b6a
--- /dev/null
+++ b/src/luacov/stats.lua
@@ -0,0 +1,56 @@
+
+module("luacov.stats", package.seeall)
+
+local statsfile = "luacov.stats.out"
+
+function load_stats()
+ local data = {}
+ local 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] = {}
+ 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
+ end
+ end
+ end
+ stats:close()
+ return data
+end
+
+function start_stats()
+ return io.open(statsfile, "w")
+end
+
+function stop_stats(stats)
+ stats:close()
+end
+
+function save_stats(data, stats)
+ stats:seek("set")
+ for filename, filedata in pairs(data) do
+ local max = filedata.max
+ stats:write(filedata.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
diff --git a/src/luacov/tick.lua b/src/luacov/tick.lua
new file mode 100755
index 0000000..624cae3
--- /dev/null
+++ b/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")