0

I have a nodejs project written in coffeescript which I use the sonar javascript plugin to analyse.

I have all my source in a folder called "src" and tests in a folder called "test".

The test files have the same names as the source files.

When I run tests

  • the coffescript is compiled to javascript and copied to "lib/src" and "lib/test"
  • the javascript is then instrumented for coverage and copied to "lib-cov/lib/src" and "lib-cov/lib/test" (actually only the src is instrumented, the tests are just copied again to maintain relative paths)

After running the tests on the instrumented code I get 2 reports "reports/lcov.info" and "reports/TEST-all.xml"

This works on one of my projects but in another I get duplicate resource errors from 2 of the source files (I cannot figure out what's special about them)

The "sonar-project.properties" looks like this:

sonar.projectKey=ddm:ddm-server
sonar.projectName=D4A Dependency Manager Server
sonar.projectVersion=1.0

sonar.sources=lib/src
sonar.exclusions=index.js
sonar.language=js

sonar.javascript.jstestdriver.reportsPath=reports
sonar.javascript.lcov.reportPath=reports/lcov.info  

The files in the "lib/src" directory are (there are no subdirectories):

  • Archive.js
  • Get.js
  • index.js
  • Library.js
  • Post.js
  • Registry.js
  • Version.js

Only "Archive.js" and "Post.js" cause a problem

and result in the following error:

ERROR: Caused by: Duplicate source for resource: org.sonar.api.resources.File@16d828ef[key=Archive.js,dir=<null>,filename=Archive.js,language=JavaScript]

If I exclude "Archive.js" and "Post.js" everything works fine - Is there something special about these names?

FYI: the complete output from the sonar runner in Jenkins is:

[DDM_SERVER_CONTINUOUS_BUILD_AND_TEST] $ /home/jenkins/tools/hudson.plugins.sonar.SonarRunnerInstallation/Sonar_Runner_2.3/sonar-runner-2.3/bin/sonar-runner -Dsonar.jdbc.driver=com.mysql.jdbc.Driver -Dsonar.jdbc.url=jdbc:mysql://sonar.lgcoreapps.com:3306/sonar?autoReconnect=true&useUnicode=true&characterEncoding=utf8 ******** ******** -Dsonar.host.url=http://sonar.lgcoreapps.com ******** ******** -Dsonar.projectBaseDir=<http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/> -Dsonar.exclusions=index.js
SonarQube Runner 2.3
Java 1.7.0_09-icedtea Oracle Corporation (64-bit)
Linux 2.6.32-358.el6.x86_64 amd64
INFO: Runner configuration file: /home/jenkins/tools/hudson.plugins.sonar.SonarRunnerInstallation/Sonar_Runner_2.3/sonar-runner-2.3/conf/sonar-runner.properties
INFO: Project configuration file: <http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/sonar-project.properties>
INFO: Default locale: "en_US", source code encoding: "US-ASCII" (analysis is platform dependent)
INFO: Work directory: <http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/.sonar>
INFO: SonarQube Server 3.5.1
13:46:58.260 INFO  - Load batch settings
13:46:58.349 INFO  - User cache: /home/jenkins/.sonar/cache
13:46:58.351 INFO  - Install plugins
13:46:59.109 INFO  - -------------  Executing Project Scan
13:46:59.750 INFO  - Install JDBC driver
13:46:59.754 INFO  - Apply project exclusions
13:46:59.758 INFO  - Create JDBC datasource for jdbc:mysql://sonar.lgcoreapps.com:3306/sonar?autoReconnect=true&useUnicode=true&characterEncoding=utf8
13:47:00.110 INFO  - Initializing Hibernate
13:47:03.520 INFO  - -------------  Inspecting D4A Dependency Manager Server
13:47:03.525 INFO  - Load module settings
13:47:04.145 INFO  - Quality profile : [name=Sonar way,language=js]
13:47:04.171 INFO  - Excluded sources: 
13:47:04.172 INFO  -   index.js
13:47:04.172 INFO  - Excluded tests: 
13:47:04.172 INFO  -   **/package-info.java
13:47:04.203 INFO  - Configure Maven plugins
13:47:04.309 INFO  - Compare to previous analysis (2014-02-07)
13:47:04.335 INFO  - Compare over 5 days (2014-02-02, analysis of 2014-02-07 12:04:17.0)
13:47:04.352 INFO  - Compare over 30 days (2014-01-08, analysis of 2014-02-07 12:04:17.0)
13:47:04.654 INFO  - Base dir: <http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/>
13:47:04.654 INFO  - Working dir: <http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/.sonar>
13:47:04.654 INFO  - Source dirs: <http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/lib/src>
13:47:04.654 INFO  - Source encoding: US-ASCII, default locale: en_US
13:47:04.676 INFO  - Sensor JavaScriptSourceImporter...
13:47:04.797 INFO  - Sensor JavaScriptSourceImporter done: 121 ms
13:47:04.797 INFO  - Sensor JavaScriptSquidSensor...
13:47:05.858 INFO  - Sensor JavaScriptSquidSensor done: 1061 ms
13:47:05.858 INFO  - Sensor LCOVSensor...
13:47:05.859 INFO  - Analysing <http://jenkins.lgcoreapps.com/job/DDM_SERVER_CONTINUOUS_BUILD_AND_TEST/ws/reports/lcov.info>
13:47:05.902 INFO  - Sensor LCOVSensor done: 44 ms
13:47:05.903 INFO  - Sensor JsTestDriverSensor...
INFO: ------------------------------------------------------------------------
INFO: EXECUTION FAILURE
INFO: ------------------------------------------------------------------------
Total time: 8.617s
Final Memory: 9M/28M
INFO: ------------------------------------------------------------------------
ERROR: Error during Sonar runner execution
ERROR: Unable to execute Sonar
ERROR: Caused by: Duplicate source for resource: org.sonar.api.resources.File@563e2119[key=Archive.js,dir=<null>,filename=Archive.js,language=JavaScript]
ERROR: 
ERROR: To see the full stack trace of the errors, re-run SonarQube Runner with the -e switch.
ERROR: Re-run SonarQube Runner using the -X switch to enable full debug logging.
Build step 'Invoke Standalone Sonar Analysis' marked build as failure

EDIT: After confirming that the analysis completes ok when not submitting the test report , here is the TEST-all.xml from the reports directory in case it also provides a clue

<testsuite name="Mocha Tests" tests="36" failures="0" errors="0" skipped="0" timestamp="Fri, 07 Feb 2014 10:23:12 GMT" time="1.073">
<testcase classname="Archive" name="should respond with 404 Not Found if pathInfo is not &quot;/&quot; or &quot;&quot;" time="0.005"/>
<testcase classname="Archive when the archive does not exist" name="should respond with 404 not Found" time="0.002"/>
<testcase classname="Archive when the archive exists" name="should respond with the archive" time="0.004"/>
<testcase classname="Get with an empty repository" name="should respond with an empty list of libraries" time="0.003"/>
<testcase classname="Get with a single library in the repository" name="should respond with a single entry list of libraries" time="0.002"/>
<testcase classname="Get with a multiple libraries in the repository" name="should respond with a list of libraries" time="0.004"/>
<testcase classname="Get with a multiple libraries in the repository" name="should prepend the root path to library paths correctly" time="0.001"/>
<testcase classname="Get with a multiple libraries in the repository" name="should pass requests with pathInfo not equal to &quot;/&quot; or &quot;&quot; to a Library instance" time="0.001"/>
<testcase classname="Library with an empty repository" name="should respond with 404 not Found" time="0.003"/>
<testcase classname="Library with an empty library (no versions)" name="should respond with an empty list of versions" time="0.003"/>
<testcase classname="Library with a single version" name="should respond with a single entry list of versions" time="0.002"/>
<testcase classname="Library with multiple versions" name="should respond with a list of versions" time="0.002"/>
<testcase classname="Library with multiple versions" name="should prepend the root path to library paths correctly" time="0.002"/>
<testcase classname="Library with multiple versions" name="should pass requests with pathInfo not equal to &quot;/&quot; or &quot;&quot; to a Version instance" time="0.001"/>
<testcase classname="Post" name="should respond with 400 Bad Request if the name is not specified" time="0.001"/>
<testcase classname="Post" name="should respond with 400 Bad Request if the version is not specified" time="0.001"/>
<testcase classname="Post" name="should respond with 415 Unsupported Media Type if the content-type is not application/octet-stream" time="0.001"/>
<testcase classname="Post" name="should respond with a new resource on success and write the archive and dependencies to the file system" time="0.015"/>
<testcase classname="Post" name="should respond with 400 Bad Request if dependencies are specified in an invalid format" time="0.005"/>
<testcase classname="Post" name="should add the dependencies to the new resource if present in the headers" time="0.015"/>
<testcase classname="Post" name="should prepend the root path to library paths correctly" time="0.011"/>
<testcase classname="Post" name="should respond with 405 Method Not Allowed if pathInfo is not &quot;/&quot; or &quot;&quot;" time="0.001"/>
<testcase classname="Registry GET" name="should be handled by an instance of Get" time="0.001"/>
<testcase classname="Registry POST" name="should be handled by an instance of Post" time="0"/>
<testcase classname="Registry UNKNOWN" name="should respond with 405 Method Not Allowed" time="0"/>
<testcase classname="Version when the version does not exist" name="should respond with 404 not Found" time="0.003"/>
<testcase classname="Version when the version exists" name="should respond with the version details and an empty list of dependencies" time="0.002"/>
<testcase classname="Version when the version exists" name="should pass requests with pathInfo equal to &quot;/archive&quot; to an Archive instance" time="0.001"/>
<testcase classname="Version when the version exists" name="should respond with 404 Not Found if pathInfo is not &quot;/&quot; or &quot;&quot; or &quot;/archive&quot;" time="0"/>
<testcase classname="Version when the version exists with a dependencies.json file" name="should respond with the list of dependencies" time="0.005"/>
<testcase classname="Version when the version exists with a dependencies.json file" name="should prepend the root path to library paths correctly" time="0.004"/>
<testcase classname="index with an empty repository GET &quot;/&quot;" name="should return an empty list of libraries" time="0.022"/>
<testcase classname="index with an empty repository after posting a library to &quot;/&quot; GET &quot;/&quot;" name="should respond with a single entry list of libraries" time="0.005"/>
<testcase classname="index with an empty repository after posting a library to &quot;/&quot; GET &quot;/library&quot;" name="should respond with a single entry list of versions" time="0.005"/>
<testcase classname="index with an empty repository after posting a library to &quot;/&quot; GET &quot;/library/0.0.0&quot;" name="should respond with the library resource" time="0.009"/>
<testcase classname="index with an empty repository after posting a library to &quot;/&quot; GET &quot;/library/0.0.0/archive&quot;" name="should respond with the library archive" time="0.006"/>
</testsuite>
pghalliday
  • 301
  • 2
  • 7
  • Could you please provide the full log? – David RACODON - QA Consultant Feb 08 '14 at 18:39
  • Could you please confirm that if you remove "sonar.javascript.jstestdriver.reportsPath=reports" from the sonar-project.properties file, the analysis succeeds? – David RACODON - QA Consultant Feb 09 '14 at 20:16
  • Thanks, yes it works now, but I guess my next question is how do i submit the test report to sonar? – pghalliday Feb 10 '14 at 09:25
  • Also added the test report to the question in case it offers a clue - i didn't see anything odd – pghalliday Feb 10 '14 at 09:38
  • Ok I found the problem - Post and Archive appear as class names in the test output. This causes the issue. If I avoid having tests that result in class names that mirror the files in the source then everything works. This is an odd restriction though. It's either a problem in the sonar javascript plugin or a problem with the way the mocha xunit reporter generates xunit output from BDD tests. However my feeling is that this is a problem in the sonar javascript implementation – pghalliday Feb 10 '14 at 09:47
  • Actually maybe the output of the xunit reporter could be improved - it looks very strange after it has been ingested by sonar. However, the improvement I would make (to use the file name as the class and concatenate the describe/it text into the test name) would result in more class names that match the source file names – pghalliday Feb 10 '14 at 10:00
  • I have a new plan - I will create a custom xunit reporter that concatenates the describe/it texts and uses them for the test name and then add a configuartion option to set a classname to a single value which I can then ensure doesn't conflict with source files (I will default it to "Test"). This should produce nicely organized test reports in sonar and avoid the issue. Still feels like a hack though – pghalliday Feb 10 '14 at 10:18
  • thanks - resolved now :) – pghalliday Feb 11 '14 at 10:23

1 Answers1

0

So having confirmed that the problem is caused by having a test report that contains classnames that mirror the source file names. I was faced with the choice of either restructuring my tests or using a different mocha reporter. The default mocha xunit reporter misuses the classname field as far as i can tell (at least with BDD tests) and the changes to my test structure that would make it work were not acceptable to me.

The solution has been to implement and use "mocha-sonar-reporter" now available from the npm registry

pghalliday
  • 301
  • 2
  • 7