Execute groovy script with shell - shell

I am trying to execute a groovy script on a unix environnement but i have an issue, I don't know how to write a shell script that could execute the script under :
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.search.FlexibleSearchService;
import de.hybris.platform.servicelayer.search.SearchResult;
import com.galerieslafayette.pcm.model.model.product.RmsProductModel ;
final FlexibleSearchService flexibleSearchService = spring.getBean("flexibleSearchService");
final ModelService modelService = spring.getBean("modelService");
new File("/tmp/productsToUpdate.csv").splitEachLine(";") {fields ->
criteria = fields[1]
if (fields[2]?.trim()) {
criteria += "</p><p>" + fields[2]
}
if (fields[3]?.trim()) {
criteria += "</p><p>" + fields[3]
}
criteria = "<p>" + criteria + "</p>"
updateProduct(fields[0], criteria)
}
def updateProduct(ugProduct, criteria) {
SearchResult<RmsProductModel> result = flexibleSearchService.search("select {pk} from {RmsProduct} where {ug}=?ugCode", ["ugCode":ugProduct]);
if(result.getResult().get(0) != null){
RmsProductModel rmsProduct = result.getResult().get(0);
String temp = rmsProduct.getProduct().getDescription(Locale.FRANCE);
rmsProduct.getProduct().setDescription(temp + criteria, Locale.FRANCE)
modelService.save(rmsProduct.getProduct());
}
}
I tried to find on the web how to do so, but i only find subject on how to run shell in a groovy and not the opposite.
I am a beginner on unix and don't know the command line or if i need a specifique addition on my environnement or if vanilla shell could do it

Related

Parse value from Groovy into Shell step Jenkins

I have a Groovy script in my Jenkins build step that calculates the build duration and puts the value into a string that I would like to execute in a shell script.
I've tried doing it through groovy multiple ways but still no luck. Running the exact string on the Jenkins Slave works fine so would like to pass that string into a shell script step and run it after. How would I go about doing that?
I thought about setting an environment variable but currently only have found ways to retrieve them.
import hudson.model.*
import java.math.*
def apiKey = "secret"
def buildId = System.getenv("BUILD_ID")
def buildNo = System.getenv("BUILD_NUMBER")
def jobName = System.getenv("JOB_NAME")
jobName = jobName.replaceAll("\\.","-")
def nodeName = System.getenv("NODE_NAME")
def (startDate, startTime) = buildId.tokenize("_")
def (YY, MM, DD) = startDate.tokenize("-")
def (hh, mm, ss) = startTime.tokenize("-")
MathContext mc = new MathContext(200);
Date startDateTime = new GregorianCalendar(YY.toInteger(), MM.toInteger() - 1, DD.toInteger(), hh.toInteger(), mm.toInteger(),
ss.toInteger()).time
Date end = new Date()
long diffMillis = end.getTime() - startDateTime.getTime()
long buildDurationInSeconds = (diffMillis / 1000);
String metric = String.format("%s.jenkins.%s.%s.%s.duration %s",
apiKey, nodeName, jobName, buildNo, buildDurationInSeconds)
def cmd = 'echo "+metric+" | nc carbon.hostedgraphite.com 2003'
After this step I would invoke an "Execute Shell" step in jenkins passing in the value of "cmd". If someone has an example of both passing the value and then calling it in the shell script that would be a real help
def cmd = "ls -a"
new File("${build.workspace}/mycmd.sh").setText("#!/bin/sh\n${cmd}\n")
and as next step do Execute Shell ./mycmd.sh
Try this
def metric="WHAT_YOU_WANT_TO_PASS"
sh "echo $metric | nc carbon.hostedgraphite.com 2003"

How can I overwrite data in csv file using jmeter

I am using Jmeter for API automation, I am writing result pass or fail in CSV file.
I am unable to overwrite old result data, Every time I run the test cases it's appending with old result data.
I use Beanshell Post processor for writing in CSV file.
import java.io.file;
import org.apache.jmeter.services.FileServer;
ActualResponseCode = prev.getResponseCode();
if (vars.get("ExpectedResponse").equals(vars.get("ActualResponse")))
{
if(vars.get("ExpectedResponseCode").equals(prev.getResponseCode()))
{
prev.setSuccessful(true);
Result = "Pass";
ErrorMessage = "No Error";
}
else
{
Result = "Fail";
ErrorMessage = "ResponseCode not matching";
}
}
else
{
prev.setSuccessful(false);
Result = "Fail";
ErrorMessage = "ResponseData is not matching";
}
f = new FileOutputStream("C://Users//a622821//Desktop//apache-jmeter-3.2//API_AUTOMATION//TestResult_Post.csv", true);
p = new PrintStream(f);
p.println(vars.get("TestCase") + "," + vars.get("API_Endpoint") + "," + vars.get("ExpectedResponseCode") + "," + ActualResponseCode + "," + Result + "," + ErrorMessage);
p.close();
f.close();
Basically when you write to file you can overrid file by using boolean false as second parameter to FileWriter constructor
File file = ....
new FileWriter(file, false);
If you have several calls for beanshell I suggest create Beanshell sampler which will be called in start of the test and will override the file (create empty file):
import java.io.file;
f = new FileOutputStream("C://Users//a622821//Desktop//apache-jmeter-3.2//API_AUTOMATION//TestResult_Post.csv", false);
p = new PrintStream(f);
p.close();
f.close();
If you must use same beanshell you need a variable flag, so add variable firstTime with value true
In Beanshell use it to set the flag by firstTime variable:
import java.io.file;
import org.apache.jmeter.services.FileServer;
ActualResponseCode = prev.getResponseCode();
if (vars.get("ExpectedResponse").equals(vars.get("ActualResponse")))
{
if(vars.get("ExpectedResponseCode").equals(prev.getResponseCode()))
{
prev.setSuccessful(true);
Result = "Pass";
ErrorMessage = "No Error";
}
else
{
Result = "Fail";
ErrorMessage = "ResponseCode not matching";
}
}
else
{
prev.setSuccessful(false);
Result = "Fail";
ErrorMessage = "ResponseData is not matching";
}
firstTime = vars.get("firstTime");
flag = true;
if ("true".equals(firstTime)) {
flag = false;
vars.put("firstTime", "false");
}
f = new FileOutputStream("C://Users//a622821//Desktop//apache-jmeter-3.2//API_AUTOMATION//TestResult_Post.csv", flag);
p = new PrintStream(f);
p.println(vars.get("TestCase") + "," + vars.get("API_Endpoint") + "," + vars.get("ExpectedResponseCode") + "," + ActualResponseCode + "," + Result + "," + ErrorMessage);
p.close();
f.close();
Test Plan variable:
Don't use scripting to write anything to files, JMeter is able to store literally anything into its .jtl results file so I would recommend rather configuring it to store what you need there rather than trying to create an extra results file in such a weird manner as:
Beanshell is not the best scripting option, if you need to go for scripting consider using JSR223 Test Elements and Groovy language instead
If you are writing something into a file make sure you put aforementioned JSR223 Elements under the Critical Section Controller
So I would suggest switching to either Response Assertion or JSR223 Assertion instead of the PostProcessor. If required you can tell JMeter to store assertion failure message by adding the next line to user.properties file (located in the "bin" folder of JMeter installation, however I think it defaults to true in any case)
jmeter.save.saveservice.assertion_results_failure_message=true
Check out Scripting JMeter Assertions in Groovy - A Tutorial for more details.

Getting below error while running ruby script

I am running a test suite in Soap UI where I am trying to call one ruby script from groovy script. The step is getting executed successfully but still the script is not able to move on to the next step as it gives this error after running.
Have searched in google about this error, but found no proper resolution. Moreover the error itself is not very explanatory.
Will appreciate any kind of help.
Below is the groovy script which is calling "ap-v4-batch_DEV_QA.rb" ruby script.
This ruby script opens a browser and performs the task successfully and closes the browser. We expect the step to be marked as Passed so that it can move on to the next step, but it gives the error mentioned at the bottom.
Groovy Script:
String script = "webdriver/v4/ap-v4-batch_DEV_QA.rb";
String argv0 = com.eviware.soapui.SoapUI.globalProperties.getPropertyValue("GLOB_DefaultIP");
String argv1 = "com.wupay.batch.process.tasks.PaymentFileParsingTask_RunOnce";
String argv2 = "";
String argv3 = "";
String argv4 = "";
/* Nothing needs to be modified below */
String commandLine = "ruby " + com.eviware.soapui.SoapUI.globalProperties.getPropertyValue("GLOB_ScriptLocation") + "/" + script + " " + argv0 + " " + argv1 + " " + argv2 + " " + argv3 + " " + argv4;
log.info("Running command line: " + commandLine);
java.lang.Runtime runtime = java.lang.Runtime.getRuntime();
java.lang.Process p = runtime.exec(commandLine);
def propertyStep = testRunner.testCase.getTestStepByName("Properties");
java.io.BufferedReader stdInput =
new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
java.io.BufferedReader stdError =
new java.io.BufferedReader(new java.io.InputStreamReader(p.getErrorStream()));
String s = null;
String e = null;
StringBuffer eb = new StringBuffer();
while ((e = stdError.readLine()) != null) {
eb.append(e);
log.error("Ruby: " + e);
}
while ((s = stdInput.readLine()) != null) {
log.info("Ruby: " + s);
if(s.startsWith("#prop")) {
String[] propSplit = s.split(":", 3);
testRunner.testCase.setPropertyValue(propSplit[1], propSplit[2]);
}
}
p.waitFor();
log.info("Ruby: exit value " + p.exitValue());
if(eb.length() > 0) {
throw new Exception(eb.toString());
}
Error:
java.lang.Exception: C:/Ruby23/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:133:in require':require "watir-webdriver"is deprecated. Please, userequire "watir". java.lang.Exception: C:/Ruby23/lib/ruby/site_ruby/2.3.0/rubygems/core_ext/kernel_require.rb:133:inrequire': require "watir-webdriver" is deprecated. Please, use require "watir". error at line: 57
I have finally resolved the issue.
The issue was that ruby script was not accepting require "watir-webdriver".
I installed watir and replaced require "watir-webdriver" with require "watir".
now I am not getting the above mentioned error.
Thanks anyways!
Regards,
Faraz

Gradle CustomTask not considering InputFiles for dependencies

I'm trying to write a custom Gradle task that takes a specific set of input files, which may be in different directories, an output file and call a javaexec with the inputs.
I currently have it where it will skip the #TaskAction if it sees that the #OutputFile exists but it pays no consideration to the InputFiles list and if they are newer than the output file.
Here are the relevant parts of the buildfile:
class JavaCPPTask extends DefaultTask {
def className = []
String outputDir = ""
String localClasspath = "bin/classes"
String runIfExists = ""
String javacpp = "libs/javacpp.jar"
String outputName = ""
//#InputDirectory
//File inputFile = new File("bin/classes/com/package/ve")
#InputFiles
List<File> getInputFiles() {
ArrayList<File> inputFiles = new ArrayList<File>()
for ( int i = 0; i < className.size(); i++ ) {
def inputFileStr = localClasspath + '/' + className.get(i).replace('.','/') + '.class'
println("InputFileStr: " + inputFileStr )
File inputFile = new File(inputFileStr)
println("Input file exists? " + inputFile.exists() )
inputFiles.add(inputFile)
}
return inputFiles
}
#OutputFile
File getTargetFile() {
if ( outputName.length() == 0 ) {
def nameWithPackage = className.get(0)
def name = nameWithPackage.substring(nameWithPackage.lastIndexOf(".")+1)
outputName = 'jni' + name;
}
File outputFile = new File( outputDir + outputName + '.cpp');
println("Output Name is: " + outputFile )
println("Output file exists? " + outputFile.exists() )
return outputFile
}
JavaCPPTask() {
}
#TaskAction
def javacpp() {
def haveFile = new File(runIfExists)
if ( runIfExists.length() > 0 && ! haveFile.exists() ) {
println("Skipping class " + className + " makefile does not exist: " + runIfExists );
}
else {
def useOutputName = []
if ( outputName.length() > 0 ) {
useOutputName = [ '-o', outputName ]
}
def optionArray = [ '-nocompile', '-classpath', localClasspath, '-d', outputDir ]
def allOptions = [javacpp] + useOutputName + optionArray + className.findAll()
project.javaexec { main="-jar"; args allOptions }
}
}
}
def codecOpus = ['com.package.ve.CodecOpus']
task javaCPP_CodecOpus(type:JavaCPPTask) {
className = codecOpus
outputDir = 'jni/VECodecOpus/'
runIfExists = outputDir + 'Android.mk'
}
And the output, which may or may not help. The output file has already been created by a previous run, but the .class file has been updated so I'd expect that the target would run again.
> c:\java\gradle-2.2.1\bin\gradle -b build
JavaCPP.gradle javaCPP_CodecOpus
Incremental java compilation is an incubating feature.
InputFileStr: bin/classes/com/package/ve/CodecOpus.class
Input file exists? true
:javaCPP_CodecOpus
InputFileStr: bin/classes/com/package/ve/CodecOpus.class
Input file exists? true
Output Name is: jni\VECodecOpus\jniCodecOpus.cpp
Output file exists? true
Output Name is: jni\VECodecOpus\jniCodecOpus.cpp
Output file exists? true
Output Name is: jni\VECodecOpus\jniCodecOpus.cpp
Output file exists? true
Output Name is: jni\VECodecOpus\jniCodecOpus.cpp
Output file exists? true
InputFileStr: bin/classes/com/package/ve/CodecOpus.class
Input file exists? true
:javaCPP_CodecOpus UP-TO-DATE
BUILD SUCCESSFUL
What am I doing wrong?
Turns out after debugging through the Gradle source, the script is working. I had been forcing an update and a recompile on the .java file which created a new .class file, but Gradle was smart enough to know that the contents of the class file had not changed. Once I changed the source in the .java file and recompiled, Gradle correctly identified the source as changed and rebuilt the target.
Guess I've been using Make for far too long.
There's still and issue with the script since i was forced to run a clean build before I started down this rabbit hole, but the out of date input file wasn't it.

Visual Studio code metrics misreporting lines of code

The code metrics analyser in Visual Studio, as well as the code metrics power tool, report the number of lines of code in the TestMethod method of the following code as 8.
At the most, I would expect it to report lines of code as 3.
[TestClass]
public class UnitTest1
{
private void Test(out string str)
{
str = null;
}
[TestMethod]
public void TestMethod()
{
var mock = new Mock<UnitTest1>();
string str;
mock.Verify(m => m.Test(out str));
}
}
Can anyone explain why this is the case?
Further info
After a little more digging I've found that removing the out parameter from the Test method and updating the test code causes LOC to be reported as 2, which I believe is correct. The addition of out causes the jump, so it's not because of braces or attributes.
Decompiling the DLL with dotPeek reveals a fair amount of additional code generated because of the out parameter which could be considered 8 LOC, but removing the parameter and decompiling also reveals generated code, which could be considered 5 LOC, so it's not simply a matter of VS counting compiler generated code (which I don't believe it should do anyway).
There are several common definitions of 'Lines Of Code' (LOC). Each tries to bring some sense to what I think of as an almost meaningless metric. For example google of effective lines of code (eLOC).
I think that VS is including the attribute as part of the method declaration and is trying to give eLOC by counting statements and even braces. One possiblity is that 'm => m.Test(out str)' is being counted as a statement.
Consider this:
if (a > 1 &&
b > 2)
{
var result;
result = GetAValue();
return result;
}
and this:
if (a> 1 && b >2)
return GetAValue();
One definition of LOC is to count the lines that have any code. This may even include braces. In such an extreme simplistic definition the count varies hugely on coding style.
eLOC tries to reduce or eliminate the influence of code style. For example, as may the case here, a declaration may be counted as a 'line'. Not justifying it, just explaining.
Consider this:
int varA = 0;
varA = GetAValue();
and this:
var varA = GetAValue();
Two lines or one?
It all comes down to what is the intent. If it is to measure how tall a monitor you need then perhaps use a simple LOC. If the intent is to measure complexity then perhaps counting code statements is better such as eLOC.
If you want to measure complexity then use a complexity metric like cyclomatic complexity. Don't worry about how VS is measuring LOC as, i think, it is a useless metric anyway.
With the tool NDepend we get a # Lines of Code (LoC) of 2 for TestMethod(). (Disclaimer I am one of the developers of this tool). I wrote an article about How do you count your number of Lines Of Code (LOC) ? that is shedding light on what is logical LoC, and how all .NET LoC counting tooling rely on the PDB sequence points technology.
My guess concerning this LoC value of 8 provided by VS metric, is that it includes the LoC of the method generated by the lambda expression + it includes the PDB sequences points related to open/ending braces (which NDepend doesn't). Also lot of gymnastic is done by the compiler to do what is called capturing the local variable str, but this shouldn't impact the #LoC that is inferred from the PDB sequence points.
Btw, I wrote 2 others related LoC articles:
Why is it useful to count the number of Lines Of Code (LOC) ?
Mythical man month : 10 lines per developer day
I was wondering about the Visual Studio line counting and why what I was seeing wasn't what was being reported. So I wrote a small C# console program to count pure lines of code and write the results to a CSV file (see below).
Open a new solution, copy and paste it into the Program.cs file, build the executable, and then you're ready to go. It's a .Net 3.5 application. Copy it into the topmost directory of your code base. Open a command window and run the executable. You get two prompts, first for name of the program/subsystem, and for any extra file types you want to analyze. It then writes the results to a CSV file in the current directory. Nice simple thing for your purposes or to hand to management.
Anyhoo, here it is, FWIW, and YMMV:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace CodeMetricsConsole
{
class Program
{
// Concept here is that the program has a list of file extensions to do line counts on; it
// gets any extra extensions at startup from the user. Then it gets a list of files based on
// each extension in the current directory and all subdirectories. Then it walks through
// each file line by line and will display counts for that file and for that file extension.
// It writes that information to a CSV file in the current directory. It uses regular expressions
// on each line of each file to figure out what it's looking at, and how to count it (i.e. is it
// a line of code, a single or multi line comment, a multi-line string, or a whitespace line).
//
static void Main(string[] args)
{
try
{
Console.WriteLine(); // spacing
// prompt user for subsystem or application name
String userInput_subSystemName;
Console.Write("Enter the name of this application or subsystem (required): ");
userInput_subSystemName = Console.ReadLine();
if (userInput_subSystemName.Length == 0)
{
Console.WriteLine("Application or subsystem name required, exiting.");
return;
}
Console.WriteLine(); // spacing
// prompt user for additional types
String userInput_additionalFileTypes;
Console.WriteLine("Default extensions are asax, css, cs, js, aspx, ascx, master, txt, jsp, java, php, bas");
Console.WriteLine("Enter a comma-separated list of additional file extensions (if any) you wish to analyze");
Console.Write(" --> ");
userInput_additionalFileTypes = Console.ReadLine();
// tell user processing is starting
Console.WriteLine();
Console.WriteLine("Getting LOC counts...");
Console.WriteLine();
// the default file types to analyze - hashset to avoid duplicates if the user supplies extensions
HashSet allowedExtensions = new HashSet { "asax", "css", "cs", "js", "aspx", "ascx", "master", "txt", "jsp", "java", "php", "bas" };
// Add user-supplied types to allowedExtensions if any
String[] additionalFileTypes;
String[] separator = { "," };
if (userInput_additionalFileTypes.Length > 0)
{
// split string into array of additional file types
additionalFileTypes = userInput_additionalFileTypes.Split(separator, StringSplitOptions.RemoveEmptyEntries);
// walk through user-provided file types and append to default file types
foreach (String ext in additionalFileTypes)
{
try
{
allowedExtensions.Add(ext.Trim()); // remove spaces
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e.Message);
}
}
}
// summary file to write to
String summaryFile = userInput_subSystemName + "_Summary.csv";
String path = Directory.GetCurrentDirectory();
String pathAndFile = path + Path.DirectorySeparatorChar + summaryFile;
// regexes for the different line possibilities
Regex oneLineComment = new Regex(#"^\s*//"); // match whitespace to two slashes
Regex startBlockComment = new Regex(#"^\s*/\*.*"); // match whitespace to /*
Regex whiteSpaceOnly = new Regex(#"^\s*$"); // match whitespace only
Regex code = new Regex(#"\S*"); // match anything but whitespace
Regex endBlockComment = new Regex(#".*\*/"); // match anything and */ - only used after block comment detected
Regex oneLineBlockComment = new Regex(#"^\s*/\*.*\*/.*"); // match whitespace to /* ... */
Regex multiLineStringStart = new Regex("^[^\"]*#\".*"); // match #" - don't match "#"
Regex multiLineStringEnd = new Regex("^.*\".*"); // match double quotes - only used after multi line string start detected
Regex oneLineMLString = new Regex("^.*#\".*\""); // match #"..."
Regex vbaComment = new Regex(#"^\s*'"); // match whitespace to single quote
// Uncomment these two lines to test your regex with the function testRegex() below
//new Program().testRegex(oneLineMLString);
//return;
FileStream fs = null;
String line = null;
int codeLineCount = 0;
int commentLineCount = 0;
int wsLineCount = 0;
int multiLineStringCount = 0;
int fileCodeLineCount = 0;
int fileCommentLineCount = 0;
int fileWsLineCount = 0;
int fileMultiLineStringCount = 0;
Boolean inBlockComment = false;
Boolean inMultiLineString = false;
try
{
// write to summary CSV file, overwrite if exists, don't append
using (StreamWriter outFile = new StreamWriter(pathAndFile, false))
{
// outFile header
outFile.WriteLine("filename, codeLineCount, commentLineCount, wsLineCount, mlsLineCount");
// walk through files with specified extensions
foreach (String allowed_extension in allowedExtensions)
{
String extension = "*." + allowed_extension;
// reset accumulating values for extension
codeLineCount = 0;
commentLineCount = 0;
wsLineCount = 0;
multiLineStringCount = 0;
// Get all files in current directory and subdirectories with specified extension
String[] fileList = Directory.GetFiles(Directory.GetCurrentDirectory(), extension, SearchOption.AllDirectories);
// walk through all files of this type
for (int i = 0; i < fileList.Length; i++)
{
// reset values for this file
fileCodeLineCount = 0;
fileCommentLineCount = 0;
fileWsLineCount = 0;
fileMultiLineStringCount = 0;
inBlockComment = false;
inMultiLineString = false;
try
{
// open file
fs = new FileStream(fileList[i], FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (TextReader tr = new StreamReader(fs))
{
// walk through lines in file
while ((line = tr.ReadLine()) != null)
{
if (inBlockComment)
{
if (whiteSpaceOnly.IsMatch(line))
{
fileWsLineCount++;
}
else
{
fileCommentLineCount++;
}
if (endBlockComment.IsMatch(line)) inBlockComment = false;
}
else if (inMultiLineString)
{
fileMultiLineStringCount++;
if (multiLineStringEnd.IsMatch(line)) inMultiLineString = false;
}
else
{
// not in a block comment or multi-line string
if (oneLineComment.IsMatch(line))
{
fileCommentLineCount++;
}
else if (oneLineBlockComment.IsMatch(line))
{
fileCommentLineCount++;
}
else if ((startBlockComment.IsMatch(line)) && (!(oneLineBlockComment.IsMatch(line))))
{
fileCommentLineCount++;
inBlockComment = true;
}
else if (whiteSpaceOnly.IsMatch(line))
{
fileWsLineCount++;
}
else if (oneLineMLString.IsMatch(line))
{
fileCodeLineCount++;
}
else if ((multiLineStringStart.IsMatch(line)) && (!(oneLineMLString.IsMatch(line))))
{
fileCodeLineCount++;
inMultiLineString = true;
}
else if ((vbaComment.IsMatch(line)) && (allowed_extension.Equals("txt") || allowed_extension.Equals("bas"))
{
fileCommentLineCount++;
}
else
{
// none of the above, thus it is a code line
fileCodeLineCount++;
}
}
} // while
outFile.WriteLine(fileList[i] + ", " + fileCodeLineCount + ", " + fileCommentLineCount + ", " + fileWsLineCount + ", " + fileMultiLineStringCount);
fs.Close();
fs = null;
} // using
}
finally
{
if (fs != null) fs.Dispose();
}
// update accumulating values
codeLineCount = codeLineCount + fileCodeLineCount;
commentLineCount = commentLineCount + fileCommentLineCount;
wsLineCount = wsLineCount + fileWsLineCount;
multiLineStringCount = multiLineStringCount + fileMultiLineStringCount;
} // for (specific file)
outFile.WriteLine("Summary for: " + extension + ", " + codeLineCount + ", " + commentLineCount + ", " + wsLineCount + ", " + multiLineStringCount);
} // foreach (all files with specified extension)
} // using summary file streamwriter
Console.WriteLine("Analysis complete, file is: " + pathAndFile);
} // try block
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
}
catch (Exception e2)
{
Console.WriteLine("Error: " + e2.Message);
}
} // main
// local testing function for debugging purposes
private void testRegex(Regex rx)
{
String test = " asdfasd asdf #\" adf ++--// /*\" ";
if (rx.IsMatch(test))
{
Console.WriteLine(" -->| " + rx.ToString() + " | matched: " + test);
}
else
{
Console.WriteLine("No match");
}
}
} // class
} // namespace
Here's how it works:
the program has a set of the file extensions you want to analyze.
It walks through each extension in the set, getting all files of that type in the current and all subdirectories.
It selects each file, goes through each line of that file, compares each line to a regex to figure out what it's looking at, and increments the line count after it figures out what it's looking at.
If a line isn't whitespace, a single or multi-line comment, or a multi-line string, it counts it as a line of code. It reports all the counts for each of those types of lines (code, comments, whitespace, multi-line strings) and writes them to a CSV file. No need to explain why Visual Studio did or did not count something as a line of code.
Yes, there are three loops embedded in each other (O(n-cubed) O_O ) but it's just a simple, standalone developer tool, and the biggest code base I've run it on was about 350K lines and it took like 10 seconds to run on a Core i7.
Edit: Just ran it on the Firefox 12 code base, about 4.3 million lines (3.3M code, 1M comments), about 21K files, with an AMD Phenom processor - took 7 minutes, watched the performance tab in Task Manager, no stress. FYI.
My attitude is if I wrote it to be part of an instruction fed to a compiler, it's a line of code and should be counted.
It can easily be customized to ignore or count whatever you want (brackets, namespaces, the includes at the top of the file, etc). Just add the regex, test it with the function that's right there below the regexes, then update the if statement with that regex.

Resources