summaryrefslogtreecommitdiff
path: root/extras/luacov/src/bin/luacov
blob: 6a24bc676494b92622751c45d6e94a364458a839 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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