This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.8 KiB
227 lines
7.8 KiB
/* |
|
Copyright 2012-2015, Yahoo Inc. |
|
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. |
|
*/ |
|
"use strict"; |
|
|
|
var InsertionText = require('./insertion-text'), |
|
lt = '\u0001', |
|
gt = '\u0002', |
|
RE_LT = /</g, |
|
RE_GT = />/g, |
|
RE_AMP = /&/g, |
|
RE_lt = /\u0001/g, |
|
RE_gt = /\u0002/g; |
|
|
|
function title(str) { |
|
return ' title="' + str + '" '; |
|
} |
|
|
|
function customEscape(text) { |
|
text = String(text); |
|
return text.replace(RE_AMP, '&') |
|
.replace(RE_LT, '<') |
|
.replace(RE_GT, '>') |
|
.replace(RE_lt, '<') |
|
.replace(RE_gt, '>'); |
|
} |
|
|
|
function annotateLines(fileCoverage, structuredText) { |
|
var lineStats = fileCoverage.getLineCoverage(); |
|
if (!lineStats) { |
|
return; |
|
} |
|
Object.keys(lineStats).forEach(function (lineNumber) { |
|
var count = lineStats[lineNumber]; |
|
if (structuredText[lineNumber]) { |
|
structuredText[lineNumber].covered = count > 0 ? 'yes' : 'no'; |
|
structuredText[lineNumber].hits = count; |
|
} |
|
}); |
|
} |
|
|
|
function annotateStatements(fileCoverage, structuredText) { |
|
var statementStats = fileCoverage.s, |
|
statementMeta = fileCoverage.statementMap; |
|
Object.keys(statementStats).forEach(function (stName) { |
|
var count = statementStats[stName], |
|
meta = statementMeta[stName], |
|
type = count > 0 ? 'yes' : 'no', |
|
startCol = meta.start.column, |
|
endCol = meta.end.column + 1, |
|
startLine = meta.start.line, |
|
endLine = meta.end.line, |
|
openSpan = lt + 'span class="' + (meta.skip ? 'cstat-skip' : 'cstat-no') + '"' + title('statement not covered') + gt, |
|
closeSpan = lt + '/span' + gt, |
|
text; |
|
|
|
if (type === 'no' && structuredText[startLine]) { |
|
if (endLine !== startLine) { |
|
endCol = structuredText[startLine].text.originalLength(); |
|
} |
|
text = structuredText[startLine].text; |
|
text.wrap(startCol, |
|
openSpan, |
|
startCol < endCol ? endCol : text.originalLength(), |
|
closeSpan); |
|
} |
|
}); |
|
} |
|
|
|
function annotateFunctions(fileCoverage, structuredText) { |
|
|
|
var fnStats = fileCoverage.f, |
|
fnMeta = fileCoverage.fnMap; |
|
if (!fnStats) { |
|
return; |
|
} |
|
Object.keys(fnStats).forEach(function (fName) { |
|
var count = fnStats[fName], |
|
meta = fnMeta[fName], |
|
type = count > 0 ? 'yes' : 'no', |
|
startCol = meta.decl.start.column, |
|
endCol = meta.decl.end.column + 1, |
|
startLine = meta.decl.start.line, |
|
endLine = meta.decl.end.line, |
|
openSpan = lt + 'span class="' + (meta.skip ? 'fstat-skip' : 'fstat-no') + '"' + title('function not covered') + gt, |
|
closeSpan = lt + '/span' + gt, |
|
text; |
|
|
|
if (type === 'no' && structuredText[startLine]) { |
|
if (endLine !== startLine) { |
|
endCol = structuredText[startLine].text.originalLength(); |
|
} |
|
text = structuredText[startLine].text; |
|
text.wrap(startCol, |
|
openSpan, |
|
startCol < endCol ? endCol : text.originalLength(), |
|
closeSpan); |
|
} |
|
}); |
|
} |
|
|
|
function annotateBranches(fileCoverage, structuredText) { |
|
var branchStats = fileCoverage.b, |
|
branchMeta = fileCoverage.branchMap; |
|
if (!branchStats) { |
|
return; |
|
} |
|
|
|
Object.keys(branchStats).forEach(function (branchName) { |
|
var branchArray = branchStats[branchName], |
|
sumCount = branchArray.reduce(function (p, n) { |
|
return p + n; |
|
}, 0), |
|
metaArray = branchMeta[branchName].locations, |
|
i, |
|
count, |
|
meta, |
|
type, |
|
startCol, |
|
endCol, |
|
startLine, |
|
endLine, |
|
openSpan, |
|
closeSpan, |
|
text; |
|
|
|
// only highlight if partial branches are missing or if there is a |
|
// single uncovered branch. |
|
if (sumCount > 0 || (sumCount === 0 && branchArray.length === 1)) { |
|
for (i = 0; i < branchArray.length && i < metaArray.length; i += 1) { |
|
count = branchArray[i]; |
|
meta = metaArray[i]; |
|
type = count > 0 ? 'yes' : 'no'; |
|
startCol = meta.start.column; |
|
endCol = meta.end.column + 1; |
|
startLine = meta.start.line; |
|
endLine = meta.end.line; |
|
openSpan = lt + 'span class="branch-' + i + ' ' + |
|
(meta.skip ? 'cbranch-skip' : 'cbranch-no') + '"' |
|
+ title('branch not covered') + gt; |
|
closeSpan = lt + '/span' + gt; |
|
|
|
if (count === 0 && structuredText[startLine]) { //skip branches taken |
|
if (endLine !== startLine) { |
|
endCol = structuredText[startLine].text.originalLength(); |
|
} |
|
text = structuredText[startLine].text; |
|
if (branchMeta[branchName].type === 'if') { |
|
// 'if' is a special case |
|
// since the else branch might not be visible, being non-existent |
|
text.insertAt(startCol, lt + 'span class="' + |
|
(meta.skip ? 'skip-if-branch' : 'missing-if-branch') + '"' + |
|
title((i === 0 ? 'if' : 'else') + ' path not taken') + gt + |
|
(i === 0 ? 'I' : 'E') + lt + '/span' + gt, true, false); |
|
} else { |
|
text.wrap(startCol, |
|
openSpan, |
|
startCol < endCol ? endCol : text.originalLength(), |
|
closeSpan); |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
} |
|
|
|
function annotateSourceCode(fileCoverage, sourceStore) { |
|
var codeArray, |
|
lineCoverageArray; |
|
try { |
|
var sourceText = sourceStore.getSource(fileCoverage.path), |
|
code = sourceText.split(/(?:\r?\n)|\r/), |
|
count = 0, |
|
structured = code.map(function (str) { |
|
count += 1; |
|
return { |
|
line: count, |
|
covered: 'neutral', |
|
hits: 0, |
|
text: new InsertionText(str, true) |
|
}; |
|
}); |
|
structured.unshift({line: 0, covered: null, text: new InsertionText("")}); |
|
annotateLines(fileCoverage, structured); |
|
//note: order is important, since statements typically result in spanning the whole line and doing branches late |
|
//causes mismatched tags |
|
annotateBranches(fileCoverage, structured); |
|
annotateFunctions(fileCoverage, structured); |
|
annotateStatements(fileCoverage, structured); |
|
structured.shift(); |
|
|
|
codeArray = structured.map(function (item) { |
|
return customEscape(item.text.toString()) || ' '; |
|
}); |
|
|
|
lineCoverageArray = structured.map(function (item) { |
|
return { |
|
covered: item.covered, |
|
hits: item.hits > 0 ? item.hits + 'x' : ' ' |
|
}; |
|
}); |
|
|
|
return { |
|
annotatedCode: codeArray, |
|
lineCoverage: lineCoverageArray, |
|
maxLines: structured.length |
|
}; |
|
} catch (ex) { |
|
codeArray = [ ex.message ]; |
|
lineCoverageArray = [ { covered: 'no', hits: 0 } ]; |
|
String(ex.stack || '').split(/\r?\n/).forEach(function (line) { |
|
codeArray.push(line); |
|
lineCoverageArray.push({ covered: 'no', hits: 0 }); |
|
}); |
|
return { |
|
annotatedCode: codeArray, |
|
lineCoverage: lineCoverageArray, |
|
maxLines: codeArray.length |
|
}; |
|
} |
|
} |
|
|
|
module.exports = { |
|
annotateSourceCode: annotateSourceCode |
|
}; |
|
|
|
|