Skip to content

Instantly share code, notes, and snippets.

@aliaspooryorik
Created December 16, 2013 16:17
Show Gist options
  • Save aliaspooryorik/7989693 to your computer and use it in GitHub Desktop.
Save aliaspooryorik/7989693 to your computer and use it in GitHub Desktop.
Code Coverage Analyzer
<cfsetting enablecfoutputonly="true" requesttimeout="500">
<!--- path to folder where your application is --->
<cfset applicationroot = expandpath("../../../../../../")>
<!--- path to folder where your tests are --->
<cfset testsfolder = expandpath("../../")>
<!--- folders to exclude --->
<cfset exclude = "dev">
<!--- let's search the application first to see what methods are being called --->
<cfdirectory action="list" directory="#applicationroot#" filter="*.cfm|*.cfc" recurse="true" name="scripts" type="file">
<cfset methodcalls = {}>
<cfloop query="scripts">
<cfif refindnocase("(\\|/)(#listchangedelims(exclude, "|")#)(\\|/)?", scripts.directory) EQ 0>
<cfset methodcalls = findCalledFunctions(scripts.directory & '/' & scripts.name, methodcalls)>
</cfif>
</cfloop>
<!--- now let's see what we have tests for... --->
<cfdirectory action="list" directory="#testsfolder#" filter="*Test.cfc" recurse="true" name="tests" type="file">
<cfset testedmethods = {}>
<cfloop query="tests">
<cfset testedmethods = findTestedFunctions(tests.directory & '/' & tests.name, testedmethods)>
</cfloop>
<cfset green = 0>
<cfset red = 0>
<!--- finally report on called functions with functions we have tests for --->
<cfoutput>
<style>
* {font-family:Courier; font-size:11px;}
</style><p>#scripts.recordcount# files found in folder '#applicationroot#'.</p>
<p>#tests.recordcount# Test cases found in folder '#testsfolder#'.</p>
<cfloop collection="#methodcalls#" item="methodname">
<cfif structkeyexists(testedmethods, methodname)>
<span style="color:green">#methodname#</span><br>
<cfset green++>
<cfelse>
<span style="color:red">#methodname#<br>
<cfloop array="#methodcalls[methodname]#" index="usage">
&nbsp;&nbsp;#usage.script#<br>
&nbsp;&nbsp;&nbsp;&nbsp;#htmleditformat(usage.caller)#<br>
</cfloop>
<cfset red++>
</span>
</cfif>
</cfloop>
<p>Found #structcount(methodcalls)# methods called of which it appears that there are #green# with tests and #red# without tests.</p>
<p>Estimated code coverage is #round((green/(green+red))*100)#%.</p>
</cfoutput>
<cfscript>
function findCalledFunctions(required filepath, result={}){
local.filecontent = fileread(arguments.filepath);
// strip out JavaScript
local.filecontent = rereplacenocase(local.filecontent, "<script.+</script>", "", "all");
// strip out cfml comments
local.filecontent = rereplacenocase(local.filecontent, "<!---.+--->", "", "all");
// strip out cfml comments
local.filecontent = rereplacenocase(local.filecontent, "//.+(\n|\r)", "", "all");
local.matches = rematchnocase("\S+\.\w+\([^\)]+\)", local.filecontent);
for (local.match in local.matches){
local.methodname = rereplace(rematchnocase("\.\w+\(", local.match)[1], "\W+", "", "all");
if (!structkeyexists(arguments.result, local.methodname)){
arguments.result[local.methodname] = [{caller=local.match, script=arguments.filepath}];
}
else{
arrayappend(arguments.result[local.methodname], {caller=local.match, script=arguments.filepath});
}
}
return arguments.result;
}
function findTestedFunctions(required filepath, result={}){
local.filecontent = fileread(arguments.filepath);
local.matches = rematchnocase("\.\w+\([^\)]+\)", local.filecontent);
for (local.match in local.matches){
local.methodname = rereplace(rematchnocase("\.\w+\(", local.match)[1], "\W+", "", "all");
if (!structkeyexists(arguments.result, local.methodname)){
arguments.result[local.methodname] = [local.match];
}
else{
arrayappend(arguments.result[local.methodname], local.match);
}
}
return arguments.result;
}
</cfscript>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment