From 9da6972f4287d68b35b00db5650a69426d4a01c5 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Mon, 16 Jul 2007 04:45:13 +0000 Subject: Initial revision --- Makefile | 14 ++++ doc/doc.css | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++ doc/index.html | 188 +++++++++++++++++++++++++++++++++++++++++++ doc/license.html | 116 +++++++++++++++++++++++++++ doc/luacov.png | Bin 0 -> 8420 bytes src/bin/luacov | 49 +++++++++++ src/luacov.lua | 53 ++++++++++++ src/luacov/stats.lua | 56 +++++++++++++ src/luacov/tick.lua | 7 ++ 9 files changed, 706 insertions(+) create mode 100755 Makefile create mode 100755 doc/doc.css create mode 100755 doc/index.html create mode 100755 doc/license.html create mode 100755 doc/luacov.png create mode 100755 src/bin/luacov create mode 100755 src/luacov.lua create mode 100755 src/luacov/stats.lua create mode 100755 src/luacov/tick.lua 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 @@ + + + + LuaCov - Coverage analysis for Lua scripts + + + + + + + +
+ +
+ +
LuaCov
+
Coverage analysis for Lua scripts
+
+ +
+ + + +
+ +

Overview

+ +

+LuaCov is a simple coverage analyzer for Lua +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 uses the same license as Lua. +

+ +

Download

+ +

+LuaCov can be downloaded from its +LuaForge page. +

+ +

+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). +

+ +

History

+ +
+
0.1 [xx/Jul/2007]
+
+
    +
  • Initial release.
  • +
+
+
+ +

Credits

+ +

+LuaCov was designed and implemented by Hisham Muhammad as a tool for +testing LuaRocks. +

+ +

Contact

+ +

+For more information please +contact us. Comments are welcome! +

+ +
+ +
+ +
+

+ Valid XHTML 1.0!

+
+ +
+ + + 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 @@ + + + + LuaCov - Coverage analysis for Lua scripts + + + + + + +
+ +
+ +
LuaCov
+
Coverage analysis for Lua scripts
+
+ +
+ + + +
+ +

License

+ +

+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 Open Source software. +Its licenses are compatible with GPL. +LuaCov is not in the public domain and the Kepler Project +keep its copyright. The legal details are below. +

+ +

+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. +

+ +

+LuaCov is designed and implemented by Hisham Muhammad. +The implementation is not derived from licensed software. +

+ +
+ +

Copyright (c) 2007 Hisham Muhammad.

+ +

+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: +

+ +

+The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +

+ +

+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. +

+ +
+ +
+ +
+

+ Valid XHTML 1.0!

+

$Id: license.html,v 1.1 2007/07/16 04:45:19 hisham Exp $

+
+ +
+ + + diff --git a/doc/luacov.png b/doc/luacov.png new file mode 100755 index 0000000..1cf37b7 Binary files /dev/null and b/doc/luacov.png 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") -- cgit v1.2.1