├── .gitignore
├── LICENSE
├── README.md
├── circle.yml
├── pom.xml
├── project
├── .gitignore
├── build.properties
├── build.scala
└── plugins.sbt
├── samples
├── maven
│ ├── .gitignore
│ ├── README
│ ├── combined-scala-java-multi-module-sonar
│ │ ├── module1
│ │ │ ├── pom.xml
│ │ │ └── src
│ │ │ │ ├── main
│ │ │ │ ├── java
│ │ │ │ │ └── module1
│ │ │ │ │ │ └── HelloWorld.java
│ │ │ │ └── scala
│ │ │ │ │ └── module1
│ │ │ │ │ └── HelloScala.scala
│ │ │ │ └── test
│ │ │ │ ├── java
│ │ │ │ └── module1
│ │ │ │ │ └── HelloWorldTest.java
│ │ │ │ └── scala
│ │ │ │ └── HelloScalaTest.scala
│ │ ├── module2
│ │ │ ├── pom.xml
│ │ │ └── src
│ │ │ │ ├── main
│ │ │ │ ├── java
│ │ │ │ │ └── module2
│ │ │ │ │ │ └── HelloWorld2.java
│ │ │ │ └── scala
│ │ │ │ │ └── module2
│ │ │ │ │ └── HelloScala2.scala
│ │ │ │ └── test
│ │ │ │ ├── java
│ │ │ │ └── HelloWorld2Test.java
│ │ │ │ └── scala
│ │ │ │ └── HelloScala2Test.scala
│ │ └── pom.xml
│ └── combined-scala-java-sonar
│ │ ├── pom.xml
│ │ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── module1
│ │ │ │ └── HelloWorld.java
│ │ └── scala
│ │ │ └── module1
│ │ │ └── HelloScala.scala
│ │ └── test
│ │ ├── java
│ │ └── module1
│ │ │ └── HelloWorldTest.java
│ │ └── scala
│ │ └── HelloScalaTest.scala
└── sbt
│ └── multi-module
│ ├── .gitignore
│ ├── README.md
│ ├── build.sbt
│ ├── module1
│ ├── build.sbt
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── com
│ │ │ └── buransky
│ │ │ └── plugins
│ │ │ └── scoverage
│ │ │ └── samples
│ │ │ └── sbt
│ │ │ └── multiModule
│ │ │ └── module1
│ │ │ ├── Beer.scala
│ │ │ └── Pub.scala
│ │ └── test
│ │ └── scala
│ │ └── com
│ │ └── buransky
│ │ └── plugins
│ │ └── scoverage
│ │ └── samples
│ │ └── sbt
│ │ └── multiModule
│ │ └── module1
│ │ ├── BeerSpec.scala
│ │ └── PubSpec.scala
│ ├── module2
│ ├── build.sbt
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── com
│ │ │ └── buransky
│ │ │ └── plugins
│ │ │ └── scoverage
│ │ │ └── samples
│ │ │ └── sbt
│ │ │ └── multiModule
│ │ │ └── module2
│ │ │ └── Animal.scala
│ │ └── test
│ │ └── scala
│ │ └── com
│ │ └── buransky
│ │ └── plugins
│ │ └── scoverage
│ │ └── samples
│ │ └── sbt
│ │ └── multiModule
│ │ └── module2
│ │ └── AnimalSpec.scala
│ ├── project
│ ├── Common.scala
│ ├── build.properties
│ └── plugins.sbt
│ └── sonar-project.properties
├── sonar-project.properties
└── src
├── main
├── resources
│ └── com
│ │ └── buransky
│ │ └── plugins
│ │ └── scoverage
│ │ └── widget.html.erb
└── scala
│ └── com
│ ├── buransky
│ └── plugins
│ │ └── scoverage
│ │ ├── ScoverageReportParser.scala
│ │ ├── StatementCoverage.scala
│ │ ├── language
│ │ └── Scala.scala
│ │ ├── measure
│ │ └── ScalaMetrics.scala
│ │ ├── pathcleaner
│ │ ├── BruteForceSequenceMatcher.scala
│ │ └── PathSanitizer.scala
│ │ ├── sensor
│ │ └── ScoverageSensor.scala
│ │ ├── util
│ │ ├── LogUtil.scala
│ │ └── PathUtil.scala
│ │ ├── widget
│ │ └── ScoverageWidget.scala
│ │ └── xml
│ │ ├── XmlScoverageReportConstructingParser.scala
│ │ └── XmlScoverageReportParser.scala
│ ├── ncredinburgh
│ └── sonar
│ │ └── scalastyle
│ │ ├── Constants.scala
│ │ ├── RepositoryRule.scala
│ │ ├── ScalastyleQualityProfile.scala
│ │ ├── ScalastyleRepository.scala
│ │ ├── ScalastyleResources.scala
│ │ ├── ScalastyleRunner.scala
│ │ └── ScalastyleSensor.scala
│ └── sagacify
│ └── sonar
│ └── scala
│ ├── Measures.scala
│ ├── ScalaPlugin.scala
│ └── ScalaSensor.scala
└── test
├── resources
├── ScalaFile1.scala
└── ScalaFile2.scala
└── scala
└── com
├── buransky
└── plugins
│ └── scoverage
│ ├── pathcleaner
│ └── BruteForceSequenceMatcherSpec.scala
│ ├── sensor
│ ├── ScoverageSensorSpec.scala
│ └── TestSensorContext.scala
│ ├── util
│ └── PathUtilSpec.scala
│ └── xml
│ ├── XmlScoverageReportConstructingParserSpec.scala
│ ├── XmlScoverageReportParserSpec.scala
│ └── data
│ └── XmlReportFile1.scala
├── ncredinburgh
└── sonar
│ └── scalastyle
│ ├── ScalastyleAdaptedQualityProfileSpec.scala
│ ├── ScalastyleDefaultQualityProfileSpec.scala
│ ├── ScalastyleRepositorySpec.scala
│ ├── ScalastyleResourcesSpec.scala
│ ├── ScalastyleRunnerSpec.scala
│ ├── ScalastyleSensorSpec.scala
│ └── testUtils
│ ├── TestRuleFinder.scala
│ └── TestRuleFinderWithTemplates.scala
└── sagacify
└── sonar
└── scala
├── BaseMetricSensorSpec.scala
├── MeasuresSpec.scala
└── ScalaPluginSpec.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | *.log
3 |
4 | # sbt specific
5 | .cache/
6 | .history/
7 | .lib/
8 | dist/*
9 | target/
10 | lib_managed/
11 | src_managed/
12 | project/boot/
13 | project/plugins/project/
14 |
15 | # Scala-IDE specific
16 | .scala_dependencies
17 | .worksheet
18 | .classpath
19 | .project
20 | .settings
21 | .cache
22 |
23 | # Intellij IDEA Specific
24 | .idea/*
25 | *.iml
26 | *.iws
27 | *.ipr
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
${line.line}\n") 129 | else Out(pre = false, appended = true, text + s"\n${line.line.trim}\n") 130 | 131 | case (out @ Out(false, true, text), line) => 132 | if (line.empty) out.copy(appended = false, text = text.trim + "
\n") 133 | else if (line.pre) Out(pre = true, appended = true, text + s"\n${line.line}\n") 134 | else Out(pre = false, appended = true, text + s"${line.line.trim}\n") 135 | 136 | case (out @ Out(true, false, text), line) => 137 | if (line.empty) out.copy(text = text + "\n") 138 | else if (line.pre) Out(pre = true, appended = true, text + s"${line.line}\n") 139 | else Out(pre = false, appended = true, text.trim + s"\n${line.line.trim}\n") 140 | 141 | case (out @ Out(true, true, text), line) => 142 | if (line.empty) out.copy(appended = false, text = text + "\n") 143 | else if (line.pre) Out(pre = true, appended = true, text + s"${line.line}\n") 144 | else Out(pre = false, appended = true, text + s"
${line.line.trim}\n") 145 | 146 | } match { 147 | case Out(true, _, text) => 148 | text.trim + "
" 149 | case Out(false, true, text) => 150 | text.trim + "" 151 | case Out(false, false, text) => 152 | text.trim 153 | } 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/main/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleRunner.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | 20 | package com.ncredinburgh.sonar.scalastyle 21 | 22 | import java.io.File 23 | 24 | import org.scalastyle.StyleError 25 | import org.scalastyle.StyleException 26 | import org.scalastyle.Message 27 | import org.scalastyle.FileSpec 28 | import org.scalastyle.Directory 29 | import org.scalastyle.ScalastyleChecker 30 | import org.scalastyle.ErrorLevel 31 | import org.scalastyle.ScalastyleConfiguration 32 | import org.scalastyle.ConfigurationChecker 33 | import org.slf4j.LoggerFactory 34 | import org.sonar.api.profiles.RulesProfile 35 | import org.sonar.api.rules.ActiveRule 36 | import scala.collection.JavaConversions._ 37 | 38 | /** 39 | * Runs Scalastyle based on active rules in the given RulesProfile 40 | */ 41 | class ScalastyleRunner(rp: RulesProfile) { 42 | private val log = LoggerFactory.getLogger(classOf[ScalastyleRunner]) 43 | 44 | def run(encoding: String, files: java.util.List[File]): List[Message[FileSpec]] = { 45 | log.debug("Using config " + config) 46 | 47 | val fileSpecs = Directory.getFilesAsJava(Some(encoding), files) 48 | val messages = new ScalastyleChecker[FileSpec]().checkFiles(config, fileSpecs) 49 | 50 | // only errors and exceptions are of interest 51 | messages.collect { _ match { 52 | case e: StyleError[_] => e 53 | case ex: StyleException[_] => ex 54 | }} 55 | 56 | } 57 | 58 | def config: ScalastyleConfiguration = { 59 | val sonarRules = rp.getActiveRulesByRepository(Constants.RepositoryKey) 60 | val checkers = sonarRules.map(ruleToChecker).toList 61 | new ScalastyleConfiguration("sonar", true, checkers) 62 | } 63 | 64 | private def ruleToChecker(activeRule: ActiveRule): ConfigurationChecker = { 65 | val sonarParams = activeRule.getActiveRuleParams.map(p => (p.getKey, p.getValue)).toMap 66 | 67 | val checkerParams = sonarParams.filterNot(keyVal => keyVal._1 == Constants.ClazzParam) 68 | val className = sonarParams(Constants.ClazzParam) 69 | val sonarKey = activeRule.getRuleKey 70 | 71 | ConfigurationChecker(className, ErrorLevel, true, sonarParams, None, Some(sonarKey)) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleSensor.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import org.scalastyle._ 22 | import org.slf4j.LoggerFactory 23 | import org.sonar.api.batch.fs.{InputFile, FileSystem} 24 | import org.sonar.api.batch.{Sensor, SensorContext} 25 | import org.sonar.api.component.ResourcePerspectives 26 | import org.sonar.api.issue.{Issuable, Issue} 27 | import org.sonar.api.profiles.RulesProfile 28 | import org.sonar.api.resources.Project 29 | import org.sonar.api.rule.RuleKey 30 | import org.sonar.api.rules.{Rule, RuleFinder, RuleQuery} 31 | 32 | 33 | import scala.collection.JavaConversions._ 34 | 35 | 36 | /** 37 | * Main sensor for return Scalastyle issues to Sonar. 38 | */ 39 | class ScalastyleSensor(resourcePerspectives: ResourcePerspectives, 40 | runner: ScalastyleRunner, 41 | fileSystem: FileSystem, 42 | ruleFinder: RuleFinder) 43 | extends Sensor { 44 | 45 | def this(resourcePerspectives: ResourcePerspectives, 46 | rulesProfile: RulesProfile, 47 | fileSystem: FileSystem, 48 | ruleFinder: RuleFinder) = this(resourcePerspectives, new ScalastyleRunner(rulesProfile), fileSystem, ruleFinder) 49 | 50 | 51 | private val log = LoggerFactory.getLogger(classOf[ScalastyleSensor]) 52 | 53 | private def predicates = fileSystem.predicates() 54 | 55 | private def scalaFilesPredicate = predicates.and(predicates.hasType(InputFile.Type.MAIN), predicates.hasLanguage(Constants.ScalaKey)) 56 | 57 | 58 | override def shouldExecuteOnProject(project: Project): Boolean = { 59 | fileSystem.files(scalaFilesPredicate).nonEmpty 60 | } 61 | 62 | override def analyse(project: Project, context: SensorContext): Unit = { 63 | val files = fileSystem.files(scalaFilesPredicate) 64 | val encoding = fileSystem.encoding.name 65 | val messages = runner.run(encoding, files.toList) 66 | 67 | messages foreach (processMessage(_)) 68 | } 69 | 70 | private def processMessage(message: Message[FileSpec]): Unit = message match { 71 | case error: StyleError[FileSpec] => processError(error) 72 | case exception: StyleException[FileSpec] => processException(exception) 73 | case _ => Unit 74 | } 75 | 76 | private def processError(error: StyleError[FileSpec]): Unit = { 77 | log.debug("Error message for rule " + error.clazz.getName) 78 | 79 | val inputFile = fileSystem.inputFile(predicates.hasPath(error.fileSpec.name)) 80 | val issuable = Option(resourcePerspectives.as(classOf[Issuable], inputFile)) 81 | val rule = findSonarRuleForError(error) 82 | 83 | log.debug("Matched to sonar rule " + rule) 84 | 85 | if (issuable.isDefined) { 86 | addIssue(issuable.get, error, rule) 87 | } else { 88 | log.error("issuable is null, cannot add issue") 89 | } 90 | } 91 | 92 | private def addIssue(issuable: Issuable, error: StyleError[FileSpec], rule: Rule): Unit = { 93 | val lineNum = sanitiseLineNum(error.lineNumber) 94 | val messageStr = error.customMessage getOrElse rule.getName 95 | 96 | val issue: Issue = issuable.newIssueBuilder.ruleKey(rule.ruleKey) 97 | .line(lineNum).message(messageStr).build 98 | issuable.addIssue(issue) 99 | } 100 | 101 | private def findSonarRuleForError(error: StyleError[FileSpec]): Rule = { 102 | val key = Constants.RepositoryKey 103 | val errorKey = error.key // == scalastyle ConfigurationChecker.customId 104 | log.debug("Looking for sonar rule for " + errorKey) 105 | ruleFinder.find(RuleQuery.create.withKey(errorKey).withRepositoryKey(key)) 106 | } 107 | 108 | private def processException(exception: StyleException[FileSpec]): Unit = { 109 | log.error("Got exception message from Scalastyle. " + 110 | "Check you have valid parameters configured for all rules. Exception message was: " + exception.message) 111 | } 112 | 113 | // sonar claims to accept null or a non zero lines, however if it is passed 114 | // null it blows up at runtime complaining it was passed 0 115 | private def sanitiseLineNum(maybeLine: Option[Int]) = if ((maybeLine getOrElse 0) != 0) { 116 | maybeLine.get 117 | } else { 118 | 1 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/scala/com/sagacify/sonar/scala/Measures.scala: -------------------------------------------------------------------------------- 1 | package com.sagacify.sonar.scala 2 | 3 | import scala.annotation.tailrec 4 | 5 | import scalariform.lexer.ScalaLexer 6 | import scalariform.lexer.Token 7 | import scalariform.lexer.Tokens.LINE_COMMENT 8 | import scalariform.lexer.Tokens.MULTILINE_COMMENT 9 | import scalariform.lexer.Tokens.XML_COMMENT 10 | import scalariform.lexer.Tokens.WS 11 | import scalariform.lexer.Tokens.EOF 12 | 13 | object Measures { 14 | 15 | /* applied on raw source code */ 16 | 17 | /* applied on lines of code */ 18 | 19 | /* applied on tokenised code */ 20 | 21 | @tailrec 22 | final def count_comment_lines(tokens: List[Token], i: Int = 0): Int = { 23 | tokens match { 24 | case Nil => i 25 | case token :: tail if token.tokenType.isComment => { 26 | token.tokenType match { 27 | case LINE_COMMENT => 28 | count_comment_lines(tail, i + 1) 29 | case MULTILINE_COMMENT => 30 | count_comment_lines(tail, i + token.rawText.count(_ == '\n') + 1) 31 | case XML_COMMENT => 32 | new scala.NotImplementedError("XML ?!"); i 33 | } 34 | } 35 | case _ :: tail => count_comment_lines(tail, i) 36 | } 37 | } 38 | 39 | @tailrec 40 | final def count_ncloc(tokens: List[Token], i: Int = 0): Int = { 41 | 42 | @tailrec 43 | def get_next_line(tokens: List[Token]): List[Token] = { 44 | tokens match { 45 | case Nil => Nil 46 | case token :: tail if token.tokenType == WS && 47 | token.text.contains('\n') => tail 48 | case token :: tail if token.tokenType == LINE_COMMENT => tail 49 | case token :: tail => get_next_line(tail) 50 | } 51 | } 52 | 53 | tokens match { 54 | case Nil => i 55 | case token :: tail if token.tokenType == WS => count_ncloc(tail, i) 56 | case token :: tail if token.tokenType == EOF => i 57 | case token :: tail => 58 | if( !token.tokenType.isNewline & !token.tokenType.isComment) { 59 | count_ncloc(get_next_line(tail), i + 1) 60 | } else { 61 | count_ncloc(tail, i) 62 | } 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/scala/com/sagacify/sonar/scala/ScalaPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.sagacify.sonar.scala 2 | 3 | import scala.collection.JavaConversions._ 4 | import scala.collection.mutable.ListBuffer 5 | 6 | import com.buransky.plugins.scoverage.measure.ScalaMetrics 7 | import com.buransky.plugins.scoverage.sensor.ScoverageSensor 8 | import com.buransky.plugins.scoverage.widget.ScoverageWidget 9 | import com.ncredinburgh.sonar.scalastyle.ScalastyleQualityProfile 10 | import com.ncredinburgh.sonar.scalastyle.ScalastyleRepository 11 | import com.ncredinburgh.sonar.scalastyle.ScalastyleSensor 12 | import org.sonar.api.config.Settings 13 | import org.sonar.api.Extension 14 | import org.sonar.api.resources.AbstractLanguage 15 | import org.sonar.api.SonarPlugin 16 | import scalariform.lexer.ScalaLexer 17 | import scalariform.lexer.Token 18 | 19 | /** 20 | * Defines Scala as a language for SonarQube. 21 | */ 22 | class Scala(s: Settings) extends AbstractLanguage("scala", "Scala") { 23 | 24 | override def getFileSuffixes: Array[String] = Array("scala") 25 | 26 | } 27 | 28 | object Scala { 29 | 30 | def tokenize(sourceCode: String, scalaVersion: String): List[Token] = 31 | ScalaLexer.createRawLexer(sourceCode, false, scalaVersion).toList 32 | 33 | } 34 | 35 | /** 36 | * Plugin entry point. 37 | */ 38 | class ScalaPlugin extends SonarPlugin { 39 | 40 | override def getExtensions: java.util.List[Class[_]] = 41 | ListBuffer[Class[_]] ( 42 | classOf[Scala], 43 | classOf[ScalaSensor], 44 | classOf[ScalastyleRepository], 45 | classOf[ScalastyleQualityProfile], 46 | classOf[ScalastyleSensor], 47 | classOf[ScalaMetrics], 48 | classOf[ScoverageSensor], 49 | classOf[ScoverageWidget] 50 | ) 51 | 52 | override val toString = getClass.getSimpleName 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/scala/com/sagacify/sonar/scala/ScalaSensor.scala: -------------------------------------------------------------------------------- 1 | package com.sagacify.sonar.scala 2 | 3 | import scala.io.Source 4 | import scala.collection.JavaConversions._ 5 | 6 | import org.sonar.api.batch.fs.FileSystem 7 | import org.sonar.api.batch.Sensor 8 | import org.sonar.api.batch.SensorContext 9 | import org.sonar.api.measures.{CoreMetrics => CM} 10 | import org.sonar.api.resources.Project 11 | 12 | 13 | class ScalaSensor(scala: Scala, fs: FileSystem) extends Sensor { 14 | 15 | def shouldExecuteOnProject(project: Project): Boolean = { 16 | return fs.hasFiles(fs.predicates().hasLanguage(scala.getKey())); 17 | } 18 | 19 | def analyse(project: Project, context: SensorContext): Unit = { 20 | 21 | val charset = fs.encoding().toString() 22 | val version = "2.11.8" 23 | 24 | val inputFiles = fs.inputFiles(fs.predicates().hasLanguage(scala.getKey())) 25 | 26 | inputFiles.foreach{ inputFile => 27 | context.saveMeasure(inputFile, CM.FILES, 1.0); 28 | 29 | val sourceCode = Source.fromFile(inputFile.file, charset).mkString 30 | val tokens = Scala.tokenize(sourceCode, version) 31 | 32 | context.saveMeasure(inputFile, 33 | CM.COMMENT_LINES, 34 | Measures.count_comment_lines(tokens)) 35 | context.saveMeasure(inputFile, 36 | CM.NCLOC, 37 | Measures.count_ncloc(tokens)) 38 | 39 | // context.saveMeasure(input, CM.CLASSES, classes) 40 | // context.saveMeasure(input, CM.FUNCTIONS, methods) 41 | // context.saveMeasure(input, CM.ACCESSORS, accessors) 42 | // context.saveMeasure(input, CM.COMPLEXITY_IN_FUNCTIONS, complexityInMethods) 43 | // context.saveMeasure(input, CM.COMPLEXITY_IN_CLASSES, fileComplexity) 44 | // context.saveMeasure(input, CM.COMPLEXITY, fileComplexity) 45 | // context.saveMeasure(input, CM.PUBLIC_API, publicApiChecker.getPublicApi()) 46 | // context.saveMeasure(input, CM.PUBLIC_DOCUMENTED_API_DENSITY, publicApiChecker.getDocumentedPublicApiDensity()) 47 | // context.saveMeasure(input, CM.PUBLIC_UNDOCUMENTED_API, publicApiChecker.getUndocumentedPublicApi()) 48 | 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/test/resources/ScalaFile1.scala: -------------------------------------------------------------------------------- 1 | class ScalaFile1 { 2 | val value = "value" 3 | 4 | def function: Unit = { 5 | println("function called.") 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/test/resources/ScalaFile2.scala: -------------------------------------------------------------------------------- 1 | // Expected Header Comment 2 | 3 | class ScalaFile2 { 4 | val value = "value" 5 | 6 | def function: Unit = { 7 | println("function called.") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/scala/com/buransky/plugins/scoverage/pathcleaner/BruteForceSequenceMatcherSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scoverage Plugin 3 | * Copyright (C) 2013 Rado Buransky 4 | * dev@sonar.codehaus.org 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 19 | */ 20 | package com.buransky.plugins.scoverage.pathcleaner 21 | 22 | import org.junit.runner.RunWith 23 | import org.scalatest.mock.MockitoSugar 24 | import org.scalatest.junit.JUnitRunner 25 | import org.scalatest.FlatSpec 26 | import com.buransky.plugins.scoverage.pathcleaner.BruteForceSequenceMatcher.PathSeq 27 | import org.scalatest.Matchers 28 | import java.io.File 29 | import org.mockito.Mockito._ 30 | 31 | @RunWith(classOf[JUnitRunner]) 32 | class BruteForceSequenceMatcherSpec extends FlatSpec with Matchers with MockitoSugar { 33 | 34 | // file-map of all files under baseDir/sonar.sources organized by their filename 35 | val filesMap: Map[String, Seq[PathSeq]] = Map ( 36 | "rootTestFile.scala" -> List(List("testProject", "main", "rootTestFile.scala")), 37 | "nestedTestFile.scala" -> List(List("testProject", "main", "some", "folders", "nestedTestFile.scala")), 38 | "multiFile.scala" -> List( 39 | List("testProject", "main", "some", "multiFile.scala"), 40 | List("testProject", "main", "some", "folder", "multiFile.scala") 41 | ) 42 | ) 43 | 44 | // baseDir = testProject sonar.sources = main 45 | val testee = new BruteForceSequenceMatcherTestee("/testProject/main", filesMap) 46 | 47 | 48 | 49 | behavior of "BruteForceSequenceMatcher with absolute report filenames" 50 | 51 | it should "provide just the filename for top level files" in { 52 | testee.getSourceRelativePath(List("testProject", "main", "rootTestFile.scala")).get shouldEqual List("rootTestFile.scala") 53 | } 54 | 55 | it should "provide the filename and the folders for nested files" in { 56 | testee.getSourceRelativePath(List("testProject", "main", "some", "folders", "nestedTestFile.scala")).get shouldEqual List("some", "folders", "nestedTestFile.scala") 57 | } 58 | 59 | it should "find the correct file if multiple files with same name exist" in { 60 | testee.getSourceRelativePath(List("testProject", "main", "some", "multiFile.scala")).get shouldEqual List("some", "multiFile.scala") 61 | testee.getSourceRelativePath(List("testProject", "main", "some", "folder", "multiFile.scala")).get shouldEqual List("some", "folder", "multiFile.scala") 62 | } 63 | 64 | 65 | 66 | 67 | behavior of "BruteForceSequenceMatcher with filenames relative to the base dir" 68 | 69 | it should "provide just the filename for top level files" in { 70 | testee.getSourceRelativePath(List("main", "rootTestFile.scala")).get shouldEqual List("rootTestFile.scala") 71 | } 72 | 73 | it should "provide the filename and the folders for nested files" in { 74 | testee.getSourceRelativePath(List("main", "some", "folders", "nestedTestFile.scala")).get shouldEqual List("some", "folders", "nestedTestFile.scala") 75 | } 76 | 77 | it should "find the correct file if multiple files with same name exist" in { 78 | testee.getSourceRelativePath(List("main", "some", "multiFile.scala")).get shouldEqual List("some", "multiFile.scala") 79 | testee.getSourceRelativePath(List("main", "some", "folder", "multiFile.scala")).get shouldEqual List("some", "folder", "multiFile.scala") 80 | } 81 | 82 | 83 | 84 | 85 | behavior of "BruteForceSequenceMatcher with filenames relative to the src dir" 86 | 87 | it should "provide just the filename for top level files" in { 88 | testee.getSourceRelativePath(List("rootTestFile.scala")).get shouldEqual List("rootTestFile.scala") 89 | } 90 | 91 | it should "provide the filename and the folders for nested files" in { 92 | testee.getSourceRelativePath(List("some", "folders", "nestedTestFile.scala")).get shouldEqual List("some", "folders", "nestedTestFile.scala") 93 | } 94 | 95 | it should "find the correct file if multiple files with same name exist" in { 96 | testee.getSourceRelativePath(List("some", "multiFile.scala")).get shouldEqual List("some", "multiFile.scala") 97 | testee.getSourceRelativePath(List("some", "folder", "multiFile.scala")).get shouldEqual List("some", "folder", "multiFile.scala") 98 | } 99 | 100 | 101 | 102 | 103 | class BruteForceSequenceMatcherTestee(absoluteSrcPath: String, filesMap: Map[String, Seq[PathSeq]]) 104 | extends BruteForceSequenceMatcher(mock[File], "") { 105 | 106 | def srcDir = { 107 | val dir = mock[File] 108 | when(dir.isAbsolute).thenReturn(true) 109 | when(dir.isDirectory).thenReturn(true) 110 | when(dir.getAbsolutePath).thenReturn(absoluteSrcPath) 111 | dir 112 | } 113 | 114 | override private[pathcleaner] def initSourceDir(): File = srcDir 115 | override private[pathcleaner] def initFilesMap(): Map[String, Seq[PathSeq]] = filesMap 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/scala/com/buransky/plugins/scoverage/sensor/ScoverageSensorSpec.scala: -------------------------------------------------------------------------------- 1 | // /* 2 | // * Sonar Scoverage Plugin 3 | // * Copyright (C) 2013 Rado Buransky 4 | // * dev@sonar.codehaus.org 5 | // * 6 | // * This program is free software; you can redistribute it and/or 7 | // * modify it under the terms of the GNU Lesser General Public 8 | // * License as published by the Free Software Foundation; either 9 | // * version 3 of the License, or (at your option) any later version. 10 | // * 11 | // * This program is distributed in the hope that it will be useful, 12 | // * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | // * Lesser General Public License for more details. 15 | // * 16 | // * You should have received a copy of the GNU Lesser General Public 17 | // * License along with this program; if not, write to the Free Software 18 | // * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 19 | // */ 20 | // package com.buransky.plugins.scoverage.sensor 21 | 22 | // import java.io.File 23 | // import java.util 24 | 25 | // import com.buransky.plugins.scoverage.language.Scala 26 | // import com.buransky.plugins.scoverage.{FileStatementCoverage, DirectoryStatementCoverage, ProjectStatementCoverage, ScoverageReportParser} 27 | // import org.junit.runner.RunWith 28 | // import org.mockito.Mockito._ 29 | // import org.scalatest.junit.JUnitRunner 30 | // import org.scalatest.mock.MockitoSugar 31 | // import org.scalatest.{FlatSpec, Matchers} 32 | // import org.sonar.api.batch.fs.{FilePredicate, FilePredicates, FileSystem} 33 | // import org.sonar.api.config.Settings 34 | // import org.sonar.api.resources.Project 35 | // import org.sonar.api.resources.Project.AnalysisType 36 | // import org.sonar.api.scan.filesystem.PathResolver 37 | 38 | // import scala.collection.JavaConversions._ 39 | // import com.buransky.plugins.scoverage.pathcleaner.PathSanitizer 40 | // import org.mockito.Matchers.any 41 | 42 | 43 | // @RunWith(classOf[JUnitRunner]) 44 | // class ScoverageSensorSpec extends FlatSpec with Matchers with MockitoSugar { 45 | // behavior of "shouldExecuteOnProject" 46 | 47 | // it should "succeed for Scala project" in new ShouldExecuteOnProject { 48 | // checkShouldExecuteOnProject(List("scala"), true) 49 | // } 50 | 51 | // it should "succeed for mixed projects" in new ShouldExecuteOnProject { 52 | // checkShouldExecuteOnProject(List("scala", "java"), true) 53 | // } 54 | 55 | // it should "fail for Java project" in new ShouldExecuteOnProject { 56 | // checkShouldExecuteOnProject(List("java"), false) 57 | // } 58 | 59 | // class ShouldExecuteOnProject extends ScoverageSensorScope { 60 | // protected def checkShouldExecuteOnProject(languages: Iterable[String], expectedResult: Boolean) { 61 | // // Setup 62 | // val project = mock[Project] 63 | // when(fileSystem.languages()).thenReturn(new util.TreeSet(languages)) 64 | 65 | // // Execute & asser 66 | // shouldExecuteOnProject(project) should equal(expectedResult) 67 | 68 | // verify(fileSystem, times(1)).languages 69 | 70 | // } 71 | // } 72 | 73 | // behavior of "analyse for single project" 74 | 75 | // it should "set 0% coverage for a project without children" in new AnalyseScoverageSensorScope { 76 | // // Setup 77 | // val pathToScoverageReport = "#path-to-scoverage-report#" 78 | // val reportAbsolutePath = "#report-absolute-path#" 79 | // val projectStatementCoverage = 80 | // ProjectStatementCoverage("project-name", List( 81 | // DirectoryStatementCoverage(File.separator, List( 82 | // DirectoryStatementCoverage("home", List( 83 | // FileStatementCoverage("a.scala", 3, 2, Nil) 84 | // )) 85 | // )), 86 | // DirectoryStatementCoverage("x", List( 87 | // FileStatementCoverage("b.scala", 1, 0, Nil) 88 | // )) 89 | // )) 90 | // val reportFile = mock[java.io.File] 91 | // val moduleBaseDir = mock[java.io.File] 92 | // val filePredicates = mock[FilePredicates] 93 | // when(reportFile.exists).thenReturn(true) 94 | // when(reportFile.isFile).thenReturn(true) 95 | // when(reportFile.getAbsolutePath).thenReturn(reportAbsolutePath) 96 | // when(settings.getString(SCOVERAGE_REPORT_PATH_PROPERTY)).thenReturn(pathToScoverageReport) 97 | // when(fileSystem.baseDir).thenReturn(moduleBaseDir) 98 | // when(fileSystem.predicates).thenReturn(filePredicates) 99 | // when(fileSystem.inputFiles(any[FilePredicate]())).thenReturn(Nil) 100 | // when(pathResolver.relativeFile(moduleBaseDir, pathToScoverageReport)).thenReturn(reportFile) 101 | // when(scoverageReportParser.parse(any[String](), any[PathSanitizer]())).thenReturn(projectStatementCoverage) 102 | 103 | // // Execute 104 | // analyse(project, context) 105 | // } 106 | 107 | // class AnalyseScoverageSensorScope extends ScoverageSensorScope { 108 | // val project = mock[Project] 109 | // val context = new TestSensorContext 110 | 111 | // override protected lazy val scoverageReportParser = mock[ScoverageReportParser] 112 | // override protected def createPathSanitizer(sonarSources: String) = mock[PathSanitizer] 113 | // } 114 | 115 | // class ScoverageSensorScope extends { 116 | // val scala = new Scala 117 | // val settings = mock[Settings] 118 | // val pathResolver = mock[PathResolver] 119 | // val fileSystem = mock[FileSystem] 120 | // } with ScoverageSensor(settings, pathResolver, fileSystem) 121 | 122 | // } 123 | -------------------------------------------------------------------------------- /src/test/scala/com/buransky/plugins/scoverage/sensor/TestSensorContext.scala: -------------------------------------------------------------------------------- 1 | // /* 2 | // * Sonar Scoverage Plugin 3 | // * Copyright (C) 2013 Rado Buransky 4 | // * dev@sonar.codehaus.org 5 | // * 6 | // * This program is free software; you can redistribute it and/or 7 | // * modify it under the terms of the GNU Lesser General Public 8 | // * License as published by the Free Software Foundation; either 9 | // * version 3 of the License, or (at your option) any later version. 10 | // * 11 | // * This program is distributed in the hope that it will be useful, 12 | // * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | // * Lesser General Public License for more details. 15 | // * 16 | // * You should have received a copy of the GNU Lesser General Public 17 | // * License along with this program; if not, write to the Free Software 18 | // * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 19 | // */ 20 | // package com.buransky.plugins.scoverage.sensor 21 | 22 | // import java.lang.Double 23 | // import java.util.Date 24 | // import java.{io, util} 25 | 26 | // import org.sonar.api.batch.fs.{FileSystem, InputFile, InputPath} 27 | // import org.sonar.api.batch.rule.ActiveRules 28 | // import org.sonar.api.batch.sensor.dependency.NewDependency 29 | // import org.sonar.api.batch.sensor.duplication.NewDuplication 30 | // import org.sonar.api.batch.sensor.highlighting.NewHighlighting 31 | // import org.sonar.api.batch.sensor.issue.NewIssue 32 | // import org.sonar.api.batch.sensor.measure.NewMeasure 33 | // import org.sonar.api.batch.{AnalysisMode, Event, SensorContext} 34 | // import org.sonar.api.config.Settings 35 | // import org.sonar.api.design.Dependency 36 | // import org.sonar.api.measures.{Measure, MeasuresFilter, Metric} 37 | // import org.sonar.api.resources.{ProjectLink, Resource} 38 | // import org.sonar.api.rules.Violation 39 | 40 | // import scala.collection.mutable 41 | 42 | // class TestSensorContext extends SensorContext { 43 | 44 | // private val measures = mutable.Map[String, Measure[_ <: io.Serializable]]() 45 | 46 | // override def saveDependency(dependency: Dependency): Dependency = ??? 47 | 48 | // override def isExcluded(reference: Resource): Boolean = ??? 49 | 50 | // override def deleteLink(key: String): Unit = ??? 51 | 52 | // override def isIndexed(reference: Resource, acceptExcluded: Boolean): Boolean = ??? 53 | 54 | // override def saveViolations(violations: util.Collection[Violation]): Unit = ??? 55 | 56 | // override def getParent(reference: Resource): Resource = ??? 57 | 58 | // override def getOutgoingDependencies(from: Resource): util.Collection[Dependency] = ??? 59 | 60 | // override def saveSource(reference: Resource, source: String): Unit = ??? 61 | 62 | // override def getMeasures[M](filter: MeasuresFilter[M]): M = ??? 63 | 64 | // override def getMeasures[M](resource: Resource, filter: MeasuresFilter[M]): M = ??? 65 | 66 | // override def deleteEvent(event: Event): Unit = ??? 67 | 68 | // override def saveViolation(violation: Violation, force: Boolean): Unit = ??? 69 | 70 | // override def saveViolation(violation: Violation): Unit = ??? 71 | 72 | // override def saveResource(resource: Resource): String = ??? 73 | 74 | // override def getEvents(resource: Resource): util.List[Event] = ??? 75 | 76 | // override def getDependencies: util.Set[Dependency] = ??? 77 | 78 | // override def getIncomingDependencies(to: Resource): util.Collection[Dependency] = ??? 79 | 80 | // override def index(resource: Resource): Boolean = ??? 81 | 82 | // override def index(resource: Resource, parentReference: Resource): Boolean = ??? 83 | 84 | // override def saveLink(link: ProjectLink): Unit = ??? 85 | 86 | // override def getMeasure[G <: io.Serializable](metric: Metric[G]): Measure[G] = measures.get(metric.getKey).orNull.asInstanceOf[Measure[G]] 87 | 88 | // override def getMeasure[G <: io.Serializable](resource: Resource, metric: Metric[G]): Measure[G] = ??? 89 | 90 | // override def getChildren(reference: Resource): util.Collection[Resource] = ??? 91 | 92 | // override def createEvent(resource: Resource, name: String, description: String, category: String, date: Date): Event = ??? 93 | 94 | // override def getResource[R <: Resource](reference: R): R = ??? 95 | 96 | // override def getResource(inputPath: InputPath): Resource = ??? 97 | 98 | // override def saveMeasure(measure: Measure[_ <: io.Serializable]): Measure[_ <: io.Serializable] = ??? 99 | 100 | // override def saveMeasure(metric: Metric[_ <: io.Serializable], value: Double): Measure[_ <: io.Serializable] = ??? 101 | 102 | // override def saveMeasure(resource: Resource, metric: Metric[_ <: io.Serializable], value: Double): Measure[_ <: io.Serializable] = ??? 103 | 104 | // override def saveMeasure(resource: Resource, measure: Measure[_ <: io.Serializable]): Measure[_ <: io.Serializable] = { 105 | // measures.put(resource.getKey, measure) 106 | // measure 107 | // } 108 | 109 | // override def saveMeasure(inputFile: InputFile, metric: Metric[_ <: io.Serializable], value: Double): Measure[_ <: io.Serializable] = ??? 110 | 111 | // override def saveMeasure(inputFile: InputFile, measure: Measure[_ <: io.Serializable]): Measure[_ <: io.Serializable] = ??? 112 | 113 | // override def newDuplication(): NewDuplication = ??? 114 | 115 | // override def activeRules(): ActiveRules = ??? 116 | 117 | // override def newHighlighting(): NewHighlighting = ??? 118 | 119 | // override def analysisMode(): AnalysisMode = ??? 120 | 121 | // override def fileSystem(): FileSystem = ??? 122 | 123 | // override def newDependency(): NewDependency = ??? 124 | 125 | // override def settings(): Settings = ??? 126 | 127 | // override def newMeasure[G <: io.Serializable](): NewMeasure[G] = ??? 128 | 129 | // override def newIssue(): NewIssue = ??? 130 | // } 131 | -------------------------------------------------------------------------------- /src/test/scala/com/buransky/plugins/scoverage/util/PathUtilSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scoverage Plugin 3 | * Copyright (C) 2013 Rado Buransky 4 | * dev@sonar.codehaus.org 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 19 | */ 20 | package com.buransky.plugins.scoverage.util 21 | 22 | import org.scalatest.{FlatSpec, Matchers} 23 | import org.junit.runner.RunWith 24 | import org.scalatest.junit.JUnitRunner 25 | 26 | @RunWith(classOf[JUnitRunner]) 27 | class PathUtilSpec extends FlatSpec with Matchers { 28 | 29 | val osName = System.getProperty("os.name") 30 | val separator = System.getProperty("file.separator") 31 | 32 | behavior of s"splitPath for $osName" 33 | 34 | it should "ignore the empty path" in { 35 | PathUtil.splitPath("") should equal(List.empty[String]) 36 | } 37 | 38 | it should "ignore a separator at the beginning" in { 39 | PathUtil.splitPath(s"${separator}a") should equal(List("a")) 40 | } 41 | 42 | it should "work with separator in the middle" in { 43 | PathUtil.splitPath(s"a${separator}b") should equal(List("a", "b")) 44 | } 45 | 46 | it should "work with an OS dependent absolute path" in { 47 | if (osName.startsWith("Windows")) { 48 | PathUtil.splitPath("C:\\test\\2") should equal(List("test", "2")) 49 | } else { 50 | PathUtil.splitPath("/test/2") should equal(List("test", "2")) 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/test/scala/com/buransky/plugins/scoverage/xml/XmlScoverageReportConstructingParserSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scoverage Plugin 3 | * Copyright (C) 2013 Rado Buransky 4 | * dev@sonar.codehaus.org 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 19 | */ 20 | package com.buransky.plugins.scoverage.xml 21 | 22 | import org.junit.runner.RunWith 23 | import org.scalatest.junit.JUnitRunner 24 | import org.scalatest.{Matchers, FlatSpec} 25 | import scala.io.Source 26 | import com.buransky.plugins.scoverage.xml.data.XmlReportFile1 27 | import scala._ 28 | import com.buransky.plugins.scoverage.{ProjectStatementCoverage, FileStatementCoverage, DirectoryStatementCoverage} 29 | import com.buransky.plugins.scoverage.pathcleaner.PathSanitizer 30 | import com.buransky.plugins.scoverage.StatementCoverage 31 | import com.buransky.plugins.scoverage.NodeStatementCoverage 32 | 33 | @RunWith(classOf[JUnitRunner]) 34 | class XmlScoverageReportConstructingParserSpec extends FlatSpec with Matchers { 35 | behavior of "parse source" 36 | 37 | it must "parse old broken Scoverage 0.95 file correctly" in { 38 | val sanitizer = new PathSanitizer() { 39 | def getSourceRelativePath(path: Seq[String]): Option[Seq[String]] = { 40 | // do nothing 41 | Some(path) 42 | } 43 | } 44 | assertReportFile(XmlReportFile1.scoverage095Data, 24.53, sanitizer)(assertScoverage095Data) 45 | } 46 | 47 | it must "parse new fixed Scoverage 1.0.4 file correctly" in { 48 | val sanitizer = new PathSanitizer() { 49 | def getSourceRelativePath(path: Seq[String]): Option[Seq[String]] = { 50 | // drop first 6 = /a1b2c3/workspace/sonar-test/src/main/scala 51 | Some(path.drop(6)) 52 | } 53 | } 54 | assertReportFile(XmlReportFile1.scoverage104Data, 50.0, sanitizer) { projectCoverage => 55 | assert(projectCoverage.name === "") 56 | assert(projectCoverage.children.size.toInt === 1) 57 | 58 | projectCoverage.children.head match { 59 | case rootDir: DirectoryStatementCoverage => { 60 | val rr = checkNode(rootDir, "com", 0, 0, 0.0).head 61 | val test = checkNode(rr, "rr", 0, 0, 0.0).head 62 | val sonar = checkNode(test, "test", 0, 0, 0.0).head 63 | val mainClass = checkNode(sonar, "sonar", 2, 1, 50.0).head 64 | 65 | checkNode(mainClass, "MainClass.scala", 2, 1, 50.0) 66 | } 67 | case other => fail(s"This is not a directory statement coverage! [$other]") 68 | } 69 | } 70 | } 71 | 72 | it must "parse file1 correctly even without XML declaration" in { 73 | val sanitizer = new PathSanitizer() { 74 | def getSourceRelativePath(path: Seq[String]): Option[Seq[String]] = { 75 | // do nothing 76 | Some(path) 77 | } 78 | } 79 | assertReportFile(XmlReportFile1.dataWithoutDeclaration, 24.53, sanitizer)(assertScoverage095Data) 80 | } 81 | 82 | private def assertReportFile(data: String, expectedCoverage: Double, pathSanitizer: PathSanitizer)(f: (ProjectStatementCoverage) => Unit) { 83 | val parser = new XmlScoverageReportConstructingParser(Source.fromString(data), pathSanitizer) 84 | val projectCoverage = parser.parse() 85 | 86 | // Assert coverage 87 | checkRate(expectedCoverage, projectCoverage.rate) 88 | 89 | f(projectCoverage) 90 | } 91 | 92 | private def assertScoverage095Data(projectCoverage: ProjectStatementCoverage): Unit = { 93 | // Assert structure 94 | projectCoverage.name should equal("") 95 | 96 | val projectChildren = projectCoverage.children.toList 97 | projectChildren.length should equal(1) 98 | projectChildren.head shouldBe a [DirectoryStatementCoverage] 99 | 100 | val aaa = projectChildren.head.asInstanceOf[DirectoryStatementCoverage] 101 | aaa.name should equal("aaa") 102 | checkRate(24.53, aaa.rate) 103 | 104 | val aaaChildren = aaa.children.toList.sortBy(_.statementCount) 105 | aaaChildren.length should equal(2) 106 | 107 | aaaChildren(1) shouldBe a [FileStatementCoverage] 108 | val errorCode = aaaChildren(1).asInstanceOf[FileStatementCoverage] 109 | errorCode.name should equal("ErrorCode.scala") 110 | errorCode.statementCount should equal (46) 111 | errorCode.coveredStatementsCount should equal (13) 112 | 113 | aaaChildren.head shouldBe a [FileStatementCoverage] 114 | val graph = aaaChildren.head.asInstanceOf[FileStatementCoverage] 115 | graph.name should equal("Graph.scala") 116 | graph.statementCount should equal (7) 117 | graph.coveredStatementsCount should equal (0) 118 | } 119 | 120 | private def checkRate(expected: Double, real: Double) { 121 | BigDecimal(real).setScale(2, BigDecimal.RoundingMode.HALF_UP).should(equal(BigDecimal(expected))) 122 | } 123 | 124 | private def checkNode(node: NodeStatementCoverage, name: String, count: Int, covered: Int, rate: Double): Iterable[NodeStatementCoverage] = { 125 | node.name shouldEqual name 126 | node.statementCount shouldEqual count 127 | node.coveredStatementsCount shouldEqual covered 128 | 129 | checkRate(rate, node.rate) 130 | 131 | node.children 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/test/scala/com/buransky/plugins/scoverage/xml/XmlScoverageReportParserSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scoverage Plugin 3 | * Copyright (C) 2013 Rado Buransky 4 | * dev@sonar.codehaus.org 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 19 | */ 20 | package com.buransky.plugins.scoverage.xml 21 | 22 | import org.scalatest.{FlatSpec, Matchers} 23 | import org.scalatest.junit.JUnitRunner 24 | import org.junit.runner.RunWith 25 | import com.buransky.plugins.scoverage.ScoverageException 26 | 27 | @RunWith(classOf[JUnitRunner]) 28 | class XmlScoverageReportParserSpec extends FlatSpec with Matchers { 29 | behavior of "parse file path" 30 | 31 | it must "fail for null path" in { 32 | the[IllegalArgumentException] thrownBy XmlScoverageReportParser().parse(null.asInstanceOf[String], null) 33 | } 34 | 35 | it must "fail for empty path" in { 36 | the[IllegalArgumentException] thrownBy XmlScoverageReportParser().parse("", null) 37 | } 38 | 39 | it must "fail for not existing path" in { 40 | the[ScoverageException] thrownBy XmlScoverageReportParser().parse("/x/a/b/c/1/2/3/4.xml", null) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleAdaptedQualityProfileSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import com.ncredinburgh.sonar.scalastyle.testUtils.TestRuleFinderWithTemplates 22 | import org.scalatest._ 23 | import org.scalatest.mock.MockitoSugar 24 | import org.sonar.api.profiles.RulesProfile 25 | import org.sonar.api.utils.ValidationMessages 26 | 27 | import scala.collection.JavaConversions._ 28 | 29 | /** 30 | * Tests an adapted ScalastyleQualityProfile, assuming the user instantiated all templates once 31 | */ 32 | class ScalastyleQualityProfileSpec extends FlatSpec with Matchers with MockitoSugar { 33 | trait Fixture { 34 | val validationMessages = ValidationMessages.create 35 | val testee = new ScalastyleQualityProfile(TestRuleFinderWithTemplates) 36 | } 37 | 38 | val rulesCount = 38 39 | val parametersCount = 21 40 | 41 | "a scalastyle quality profile" should "create a default profile" in new Fixture { 42 | val rulesProfile = testee.createProfile(validationMessages) 43 | 44 | rulesProfile.getClass shouldEqual classOf[RulesProfile] 45 | rulesProfile.getName shouldEqual Constants.ProfileName 46 | rulesProfile.getLanguage shouldEqual Constants.ScalaKey 47 | } 48 | 49 | "the default quality profile" should "have all the rules in default config" in new Fixture { 50 | val rulesProfile = testee.createProfile(validationMessages) 51 | 52 | rulesProfile.getActiveRules.size shouldBe rulesCount 53 | } 54 | 55 | it should "have all the parameters in default config" in new Fixture { 56 | val totalParameters = parametersCount + (rulesCount * 1) 57 | 58 | val rulesProfile = testee.createProfile(validationMessages) 59 | 60 | rulesProfile.getActiveRules.flatMap(_.getActiveRuleParams).size shouldBe totalParameters 61 | } 62 | 63 | it should "have correct values for parameters" in new Fixture { 64 | val ruleKey = "scalastyle_NumberOfMethodsInTypeChecker" 65 | 66 | val rulesProfile = testee.createProfile(validationMessages) 67 | val rule = rulesProfile.getActiveRule(Constants.RepositoryKey, ruleKey) 68 | val param = rule.getActiveRuleParams.head 69 | 70 | param.getKey shouldBe "maxMethods" 71 | param.getValue shouldBe "30" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleDefaultQualityProfileSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import com.ncredinburgh.sonar.scalastyle.testUtils.TestRuleFinderWithTemplates 22 | import org.scalatest._ 23 | import org.scalatest.mock.MockitoSugar 24 | import org.sonar.api.profiles.RulesProfile 25 | import org.sonar.api.utils.ValidationMessages 26 | import scala.collection.JavaConversions._ 27 | import com.ncredinburgh.sonar.scalastyle.testUtils.TestRuleFinder 28 | 29 | /** 30 | * Tests the default ScalastyleQualityProfile, only rules without parameters, no templates 31 | */ 32 | class ScalastyleDefaultQualityProfileSpec extends FlatSpec with Matchers with MockitoSugar { 33 | trait Fixture { 34 | val validationMessages = ValidationMessages.create 35 | val testee = new ScalastyleQualityProfile(TestRuleFinder) 36 | } 37 | 38 | val rulesCount = 19 // rules without templates 39 | 40 | "a scalastyle quality profile" should "create a default profile" in new Fixture { 41 | val rulesProfile = testee.createProfile(validationMessages) 42 | 43 | rulesProfile.getClass shouldEqual classOf[RulesProfile] 44 | rulesProfile.getName shouldEqual Constants.ProfileName 45 | rulesProfile.getLanguage shouldEqual Constants.ScalaKey 46 | } 47 | 48 | "the default quality profile" should "have all the rules in default config" in new Fixture { 49 | val rulesProfile = testee.createProfile(validationMessages) 50 | 51 | rulesProfile.getActiveRules.size shouldBe rulesCount 52 | } 53 | 54 | it should "have all the parameters in default config" in new Fixture { 55 | val totalParameters = (rulesCount * 1) 56 | 57 | val rulesProfile = testee.createProfile(validationMessages) 58 | 59 | rulesProfile.getActiveRules.flatMap(_.getActiveRuleParams).size shouldBe totalParameters 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleRepositorySpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import org.scalatest._ 22 | import org.sonar.api.rules.RulePriority 23 | import org.sonar.api.server.rule.{RuleParamType, RulesDefinition} 24 | 25 | 26 | import scala.collection.JavaConversions._ 27 | 28 | /** 29 | * Tests ScalastyleRepository 30 | */ 31 | class ScalastyleRepositorySpec extends FlatSpec with Matchers with Inspectors with BeforeAndAfterAll { 32 | 33 | val testee = new ScalastyleRepository 34 | val ctx = new RulesDefinition.Context() 35 | 36 | def rules = ctx.repository(Constants.RepositoryKey).rules() 37 | 38 | override def beforeAll() { 39 | testee.define(ctx) 40 | } 41 | 42 | "a scalastyle repository" should "return a list of rules" in { 43 | rules should not be empty 44 | } 45 | 46 | it should "use the same repository for all rules" in { 47 | forAll(rules) { 48 | r: RulesDefinition.Rule => 49 | r.repository().key() shouldEqual Constants.RepositoryKey 50 | r.repository().name() shouldEqual Constants.RepositoryName 51 | } 52 | } 53 | 54 | it should "consist of 63 rules" in { 55 | rules.size() shouldEqual 63 56 | } 57 | 58 | it should "set default severity to major" in { 59 | forAll(rules) {r: RulesDefinition.Rule => r.severity() shouldEqual RulePriority.MAJOR.name()} 60 | } 61 | 62 | it should "give a name to every rule" in { 63 | rules.filter(_.name == null) should be(empty) 64 | } 65 | 66 | it should "set the ClazzParam for every rule" in { 67 | rules.filter(_.param(Constants.ClazzParam) == null) should be(empty) 68 | } 69 | 70 | it should "name the rule properly" in { 71 | val rule = rules.find(_.key == "scalastyle_MagicNumberChecker") 72 | rule.get.name shouldEqual "Magic Number" 73 | } 74 | 75 | it should "describe the rule properly" in { 76 | val rule = rules.find(_.key == "scalastyle_MagicNumberChecker") 77 | rule.get.htmlDescription shouldEqual 78 | "Replacing a magic number with a named constant can make code easier to read and understand," + 79 | " and can avoid some subtle bugs.
\n" + 80 | "A simple assignment to a val is not considered to be a magic number, for example:
\n" + 81 | "val foo = 4\n
is not a magic number, but
\n" + 82 | "var foo = 4\n
is considered to be a magic number.
" 83 | } 84 | 85 | it should "determine the parameter of a rule with a parameter" in { 86 | val rule = rules.find(_.key == "scalastyle_ParameterNumberChecker") 87 | rule.get.params map (_.key) shouldEqual List("maxParameters", Constants.ClazzParam) 88 | } 89 | 90 | it should "determine parameters of a rule with multiple parameters" in { 91 | val rule = rules.find(_.key == "scalastyle_MethodNamesChecker") 92 | rule.get.params map (_.key) should contain theSameElementsAs List("regex", "ignoreRegex", "ignoreOverride", Constants.ClazzParam) 93 | } 94 | 95 | it should "determine correct type of integer parameters" in { 96 | val rule = rules.find(_.key == "scalastyle_ParameterNumberChecker") 97 | rule.get.param("maxParameters").`type` shouldEqual RuleParamType.INTEGER 98 | } 99 | 100 | it should "determine correct type of boolean parameters" in { 101 | val rule = rules.find(_.key == "scalastyle_MethodNamesChecker") 102 | rule.get.param("ignoreOverride").`type` shouldEqual RuleParamType.BOOLEAN 103 | } 104 | 105 | it should "determine correct type of regex parameters" in { 106 | val rule = rules.find(_.key == "scalastyle_ClassTypeParameterChecker") 107 | rule.get.param("regex").`type` shouldEqual RuleParamType.STRING 108 | } 109 | 110 | it should "describe the parameter properly" in { 111 | val rule = rules.find(_.key == "scalastyle_ClassTypeParameterChecker") 112 | rule.get.param("regex").description shouldEqual "Standard Scala regular expression syntax" 113 | } 114 | 115 | it should "provide default parameters to scalastyle preferred defaults for rules with a parameter" in { 116 | val rule = rules.find(_.key == "scalastyle_ParameterNumberChecker") 117 | rule.get.param("maxParameters").defaultValue.toInt shouldEqual 8 118 | } 119 | 120 | it should "provide default parameters to scalastyle preferred defaults for rules with multiple parameters" in { 121 | val rule = rules.find(_.key == "scalastyle_MethodNamesChecker") 122 | rule.get.param("regex").defaultValue shouldEqual "^[a-z][A-Za-z0-9]*(_=)?$" 123 | rule.get.param("ignoreRegex").defaultValue shouldEqual "^$" 124 | rule.get.param("ignoreOverride").defaultValue.toBoolean shouldEqual false 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleResourcesSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import org.scalatest.{FlatSpec, Inspectors, Matchers, PrivateMethodTester} 22 | import org.sonar.api.PropertyType 23 | import org.sonar.api.server.rule.RuleParamType 24 | 25 | import scala.xml.Elem 26 | 27 | /** 28 | * Tests ScalastyleResources 29 | */ 30 | class ScalastyleResourcesSpec extends FlatSpec with Matchers with Inspectors with PrivateMethodTester { 31 | 32 | it should "get default_config.xml from Scalastyle jar" in { 33 | val xmlFromClassPath = PrivateMethod[Elem]('xmlFromClassPath) 34 | val definitions = ScalastyleResources invokePrivate xmlFromClassPath("/scalastyle_definition.xml") 35 | assert(definitions.isInstanceOf[Elem]) 36 | } 37 | 38 | it should "get scalastyle_definition.xml from Scalastyle jar" in { 39 | val xmlFromClassPath = PrivateMethod[Elem]('xmlFromClassPath) 40 | val scalastyleDefinitions = ScalastyleResources invokePrivate xmlFromClassPath("/scalastyle_definition.xml") 41 | assert(scalastyleDefinitions.isInstanceOf[Elem]) 42 | } 43 | 44 | it should "get scalastyle_documentation.xml from Scalastyle jar" in { 45 | val xmlFromClassPath = PrivateMethod[Elem]('xmlFromClassPath) 46 | val scalastyleDocumentation = ScalastyleResources invokePrivate xmlFromClassPath("/scalastyle_documentation.xml") 47 | assert(scalastyleDocumentation.isInstanceOf[Elem]) 48 | } 49 | 50 | "the configuration" should "allow access to description in documentation for a checker" in { 51 | ScalastyleResources.description("line.size.limit") shouldEqual 52 | "Lines that are too long can be hard to read and horizontal scrolling is annoying.
" 53 | } 54 | 55 | it should "return all defined checkers" in { 56 | ScalastyleResources.allDefinedRules.size shouldEqual 63 57 | } 58 | 59 | it should "give rules a description" in { 60 | forAll(ScalastyleResources.allDefinedRules) {r: RepositoryRule => r.description.length should be > 0} 61 | } 62 | 63 | it should "give rules an id" in { 64 | forAll(ScalastyleResources.allDefinedRules) {r: RepositoryRule => r.id should not be empty} 65 | } 66 | 67 | it should "get all parameters of rules with a parameter" in { 68 | val rule = ScalastyleResources.allDefinedRules.find(_.clazz == "org.scalastyle.scalariform.ParameterNumberChecker") 69 | rule.get.params map (_.name) shouldEqual List("maxParameters") 70 | } 71 | 72 | it should "get all parameters of rules with multiple parameters" in { 73 | val rule = ScalastyleResources.allDefinedRules.find(_.clazz == "org.scalastyle.scalariform.MethodNamesChecker") 74 | rule.get.params map (_.name) shouldEqual List("regex", "ignoreRegex", "ignoreOverride") 75 | } 76 | 77 | it should "get labels from configuration" in { 78 | ScalastyleResources.label("disallow.space.after.token") shouldEqual "Space after tokens" 79 | ScalastyleResources.label("no.whitespace.before.left.bracket") shouldEqual "No whitespace before left bracket ''[''" 80 | } 81 | 82 | it should "get description from configuration" in { 83 | ScalastyleResources.description("magic.number") shouldEqual 84 | "Replacing a magic number with a named constant can make code easier to read and understand," + 85 | " and can avoid some subtle bugs.
\n" + 86 | "A simple assignment to a val is not considered to be a magic number, for example:
\n" + 87 | "val foo = 4\n
is not a magic number, but
\n" + 88 | "var foo = 4\n
is considered to be a magic number.
" 89 | 90 | // In case no long description found, return the short description 91 | ScalastyleResources.label("disallow.space.after.token") shouldEqual "Space after tokens" 92 | } 93 | 94 | it should "get parameter key from node" in { 95 | val xmlFromClassPath = PrivateMethod[Elem]('xmlFromClassPath) 96 | val nodeToRuleParamKey = PrivateMethod[String]('nodeToRuleParamKey) 97 | 98 | val key = "org.scalastyle.scalariform.ParameterNumberChecker" 99 | val definitions = ScalastyleResources invokePrivate xmlFromClassPath("/scalastyle_definition.xml") 100 | val ruleNodes = definitions \\ "scalastyle-definition" \ "checker" 101 | val ruleNode = ruleNodes find { _ \\ "@class" exists (_.text == key) } 102 | 103 | ruleNode match { 104 | case Some(node) => { 105 | val parameter = (node \ "parameters" \ "parameter").head 106 | ScalastyleResources invokePrivate nodeToRuleParamKey(parameter) shouldEqual "maxParameters" 107 | } 108 | case _ => fail("rule with key " + key + "could not found") 109 | } 110 | } 111 | 112 | it should "get property type from node" in { 113 | val xmlFromClassPath = PrivateMethod[Elem]('xmlFromClassPath) 114 | val nodeToRuleParamType = PrivateMethod[PropertyType]('nodeToRuleParamType) 115 | 116 | val key = "org.scalastyle.scalariform.ParameterNumberChecker" 117 | val definitions = ScalastyleResources invokePrivate xmlFromClassPath("/scalastyle_definition.xml") 118 | val ruleNodes = definitions \\ "scalastyle-definition" \ "checker" 119 | val ruleNode = ruleNodes find { _ \\ "@class" exists (_.text == key) } 120 | 121 | ruleNode match { 122 | case Some(node) => { 123 | val parameter = (node \ "parameters" \ "parameter").head 124 | ScalastyleResources invokePrivate nodeToRuleParamType(parameter) shouldEqual RuleParamType.INTEGER 125 | } 126 | case _ => fail("rule with key " + key + "could not found") 127 | } 128 | } 129 | 130 | it should "get default value from node" in { 131 | val xmlFromClassPath = PrivateMethod[Elem]('xmlFromClassPath) 132 | val nodeToDefaultValue = PrivateMethod[String]('nodeToDefaultValue) 133 | 134 | val key = "org.scalastyle.scalariform.ParameterNumberChecker" 135 | val definitions = ScalastyleResources invokePrivate xmlFromClassPath("/scalastyle_definition.xml") 136 | val ruleNodes = definitions \\ "scalastyle-definition" \ "checker" 137 | val ruleNode = ruleNodes find { _ \\ "@class" exists (_.text == key) } 138 | 139 | ruleNode match { 140 | case Some(node) => { 141 | val parameter = (node \ "parameters" \ "parameter").head 142 | ScalastyleResources invokePrivate nodeToDefaultValue(parameter) shouldEqual "8" 143 | } 144 | case _ => fail("rule with key " + key + "could not found") 145 | } 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleRunnerSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import java.io.File 22 | import java.nio.charset.StandardCharsets 23 | 24 | import org.mockito.Mockito._ 25 | import org.scalastyle._ 26 | import org.scalastyle.StyleError 27 | import org.scalatest.mock.MockitoSugar 28 | import org.scalatest.{FlatSpec, Matchers, PrivateMethodTester} 29 | import org.sonar.api.profiles.RulesProfile 30 | import org.sonar.api.rules.{Rule, RulePriority} 31 | 32 | import scala.collection.JavaConversions._ 33 | 34 | /** 35 | * Tests ScalastyleRunner 36 | */ 37 | class ScalastyleRunnerSpec extends FlatSpec with Matchers with MockitoSugar with PrivateMethodTester { 38 | 39 | trait Fixture { 40 | val checker1 = ConfigurationChecker("org.scalastyle.scalariform.MultipleStringLiteralsChecker", ErrorLevel, true, Map(), None, None) 41 | val checker2 = ConfigurationChecker("org.scalastyle.file.HeaderMatchesChecker", ErrorLevel, true, Map("header" -> "// Expected Header Comment"), None, None) 42 | val configuration = ScalastyleConfiguration("sonar", true, List(checker1, checker2)) 43 | val testeeSpy = spy(new ScalastyleRunner(mock[RulesProfile])) 44 | doReturn(configuration).when(testeeSpy).config 45 | val charset = StandardCharsets.UTF_8.name 46 | } 47 | 48 | 49 | "a scalastyle runner" should "report StyleError messages if there are rule violations" in new Fixture { 50 | val files = List(new File("src/test/resources/ScalaFile1.scala")) 51 | 52 | val messages = testeeSpy.run(charset, files).map(_.toString) 53 | 54 | messages should contain ("StyleError key=header.matches args=List() lineNumber=Some(1) column=None customMessage=None") 55 | 56 | } 57 | 58 | it should "not report StyleError messages if there are no violations" in new Fixture { 59 | val files = List(new File("src/test/resources/ScalaFile2.scala")) 60 | 61 | val messages = testeeSpy.run(charset, files) 62 | 63 | messages.length shouldEqual 0 64 | } 65 | 66 | it should "scan multiple files" in new Fixture { 67 | val files = List(new File("src/test/resources/ScalaFile1.scala"), new File("src/test/resources/ScalaFile2.scala")) 68 | 69 | val messages = testeeSpy.run(charset, files) 70 | 71 | messages.length shouldEqual 1 72 | } 73 | 74 | it should "convert rules to checker" in { 75 | val ruleToChecker = PrivateMethod[ConfigurationChecker]('ruleToChecker) 76 | val profile = RulesProfile.create(Constants.ProfileName, Constants.ScalaKey) 77 | val testee = new ScalastyleRunner(profile) 78 | val key = "multiple.string.literals" 79 | val className = "org.scalastyle.scalariform.MultipleStringLiteralsChecker" 80 | val rule = Rule.create 81 | rule.setRepositoryKey(Constants.RepositoryKey) 82 | .setKey(className) 83 | .setName(ScalastyleResources.label(key)) 84 | .setDescription(ScalastyleResources.description(key)) 85 | .setConfigKey(key) 86 | .setSeverity(RulePriority.MAJOR) 87 | rule.createParameter 88 | .setKey("allowed") 89 | .setDescription("") 90 | .setType("integer") 91 | .setDefaultValue("1") 92 | rule.createParameter 93 | .setKey("ignoreRegex") 94 | .setDescription("") 95 | .setType("integer") 96 | .setDefaultValue("^""$") 97 | 98 | // add synthetic parameter as reference to the class 99 | rule.createParameter 100 | .setKey(Constants.ClazzParam) 101 | .setDescription("Scalastyle checker that validates the rule.") 102 | .setType("string") 103 | .setDefaultValue("org.scalastyle.scalariform.MultipleStringLiteralsChecker") 104 | 105 | val activeRule = profile.activateRule(rule, rule.getSeverity) 106 | activeRule.setParameter("allowed", "1") 107 | activeRule.setParameter("ignoreRegex", "^""$") 108 | activeRule.setParameter(Constants.ClazzParam, "org.scalastyle.scalariform.MultipleStringLiteralsChecker") 109 | 110 | val checker = testee invokePrivate ruleToChecker(activeRule) 111 | val expectedParameters = Map("allowed" -> "1", "ignoreRegex" -> "^""$", Constants.ClazzParam -> "org.scalastyle.scalariform.MultipleStringLiteralsChecker") 112 | val expectedChecker = ConfigurationChecker(className, ErrorLevel, true, expectedParameters, None, Some(className)) 113 | 114 | checker shouldEqual expectedChecker 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/ScalastyleSensorSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.ncredinburgh.sonar.scalastyle 20 | 21 | import java.io.File 22 | import java.nio.charset.StandardCharsets 23 | 24 | import org.mockito.Matchers._ 25 | import org.mockito.Mockito._ 26 | import org.scalastyle._ 27 | import org.scalastyle.file.FileLengthChecker 28 | import org.scalastyle.scalariform.{ForBraceChecker, IfBraceChecker} 29 | import org.scalatest._ 30 | import org.scalatest.mock.MockitoSugar 31 | import org.sonar.api.batch.SensorContext 32 | import org.sonar.api.batch.fs._ 33 | import org.sonar.api.component.ResourcePerspectives 34 | import org.sonar.api.issue.{Issuable, Issue} 35 | import org.sonar.api.resources.Project 36 | import org.sonar.api.rules.{Rule, RuleFinder, RuleQuery} 37 | import org.sonar.core.issue.DefaultIssueBuilder 38 | 39 | import scala.collection.JavaConversions._ 40 | 41 | 42 | class ScalastyleSensorSpec extends FlatSpec with Matchers with MockitoSugar with PrivateMethodTester { 43 | 44 | trait Fixture { 45 | val fs = mock[FileSystem] 46 | val predicates = mock[FilePredicates] 47 | val project = mock[Project] 48 | val runner = mock[ScalastyleRunner] 49 | val perspective = mock[ResourcePerspectives] 50 | val issuable = mock[Issuable] 51 | val issueBuilder = new DefaultIssueBuilder().componentKey("foo").projectKey("bar") 52 | val rf = mock[RuleFinder] 53 | val aRule = Rule.create("repo", "key") 54 | 55 | val testee = new ScalastyleSensor(perspective, runner, fs, rf) 56 | val context = mock[SensorContext] 57 | 58 | when(runner.run(anyString, anyListOf(classOf[File]))).thenReturn(List()) 59 | when(fs.encoding).thenReturn(StandardCharsets.UTF_8) 60 | when(fs.predicates()).thenReturn(predicates) 61 | when(perspective.as(any(), any(classOf[InputPath]))).thenReturn(issuable) 62 | when(issuable.newIssueBuilder()).thenReturn(issueBuilder) 63 | when(rf.find(any[RuleQuery])).thenReturn(aRule) 64 | 65 | def mockScalaPredicate(scalaFiles: java.lang.Iterable[File]): Unit= { 66 | val scalaFilesPred = mock[FilePredicate] 67 | val hasTypePred = mock[FilePredicate] 68 | val langPred = mock[FilePredicate] 69 | when(predicates.hasType(InputFile.Type.MAIN)).thenReturn(hasTypePred) 70 | when(predicates.hasLanguage(Constants.ScalaKey)).thenReturn(langPred) 71 | when(predicates.and(hasTypePred, langPred)).thenReturn(scalaFilesPred) 72 | when(fs.files(scalaFilesPred)).thenReturn(scalaFiles) 73 | } 74 | } 75 | 76 | "A Scalastyle Sensor" should "execute when the project have Scala files" in new Fixture { 77 | mockScalaPredicate(List(new File("foo.scala"), new File("bar.scala"))) 78 | 79 | testee.shouldExecuteOnProject(project) shouldBe true 80 | } 81 | 82 | it should "not execute when there isn't any Scala files" in new Fixture { 83 | mockScalaPredicate(List()) 84 | 85 | testee.shouldExecuteOnProject(project) shouldBe false 86 | } 87 | 88 | 89 | 90 | it should "analyse all scala source files in project" in new Fixture { 91 | val scalaFiles = List(new File("foo.scala"), new File("bar.scala")) 92 | mockScalaPredicate(scalaFiles) 93 | 94 | testee.analyse(project, context) 95 | 96 | verify(runner).run(StandardCharsets.UTF_8.name(), scalaFiles) 97 | } 98 | 99 | it should "not create SonarQube issues when there isn't any scalastyle errors" in new Fixture { 100 | mockScalaPredicate(List(new File("foo.scala"), new File("bar.scala"))) 101 | when(runner.run(anyString, anyListOf(classOf[File]))).thenReturn(List()) 102 | 103 | testee.analyse(project, context) 104 | 105 | verify(issuable, never).addIssue(any[Issue]) 106 | } 107 | 108 | it should "report a scalastyle error as a SonarQube issue" in new Fixture { 109 | mockScalaPredicate(List(new File("foo.scala"), new File("bar.scala"))) 110 | 111 | val error = new StyleError[FileSpec]( 112 | new RealFileSpec("foo.scala", None), 113 | classOf[ForBraceChecker], 114 | "org.scalastyle.scalariform.ForBraceChecker", 115 | WarningLevel, 116 | List(), 117 | None 118 | ) 119 | when(runner.run(anyString, anyListOf(classOf[File]))).thenReturn(List(error)) 120 | 121 | testee.analyse(project, context) 122 | 123 | verify(issuable, times(1)).addIssue(any[Issue]) 124 | } 125 | 126 | it should "report scalastyle errors as SonarQube issues" in new Fixture { 127 | mockScalaPredicate(List(new File("foo.scala"), new File("bar.scala"))) 128 | 129 | val error1 = new StyleError[FileSpec](new RealFileSpec("foo.scala", None), classOf[FileLengthChecker], 130 | "org.scalastyle.file.FileLengthChecker", WarningLevel, List(), None) 131 | val error2 = new StyleError[FileSpec](new RealFileSpec("bar.scala", None), classOf[IfBraceChecker], 132 | "org.scalastyle.scalariform.IfBraceChecker", WarningLevel, List(), None) 133 | when(runner.run(anyString, anyListOf(classOf[File]))).thenReturn(List(error1, error2)) 134 | 135 | testee.analyse(project, context) 136 | 137 | verify(issuable, times(2)).addIssue(any[Issue]) 138 | } 139 | 140 | it should "find sonar rule for error" in new Fixture { 141 | val findSonarRuleForError = PrivateMethod[Rule]('findSonarRuleForError) 142 | val error = new StyleError[FileSpec](new RealFileSpec("foo.scala", None), classOf[FileLengthChecker], 143 | "org.scalastyle.file.FileLengthChecker", WarningLevel, List(), None) 144 | 145 | val rule = testee invokePrivate findSonarRuleForError(error) 146 | 147 | rule.getKey shouldEqual "key" 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/testUtils/TestRuleFinder.scala: -------------------------------------------------------------------------------- 1 | package com.ncredinburgh.sonar.scalastyle.testUtils 2 | 3 | import java.util 4 | import com.ncredinburgh.sonar.scalastyle.{Constants, ScalastyleResources} 5 | import org.sonar.api.rule.RuleKey 6 | import org.sonar.api.rules.{RulePriority, Rule, RuleQuery, RuleFinder} 7 | import scala.collection.JavaConversions._ 8 | import com.ncredinburgh.sonar.scalastyle.ScalastyleRepository 9 | import org.sonar.api.server.rule.RuleParamType 10 | import com.ncredinburgh.sonar.scalastyle.RepositoryRule 11 | 12 | object TestRuleFinder extends RuleFinder { 13 | 14 | override def findByKey(repositoryKey: String, key: String): Rule = findAll(RuleQuery.create()).find(r => r.getRepositoryKey == repositoryKey && r.getKey == key).orNull 15 | 16 | override def findByKey(key: RuleKey): Rule = findAll(RuleQuery.create()).find(r => r.getRepositoryKey == key.repository() && r.getKey == key.rule()).orNull 17 | 18 | override def findById(ruleId: Int): Rule = findAll(RuleQuery.create()).find(r => r.getId == ruleId).orNull 19 | 20 | override def findAll(query: RuleQuery): util.Collection[Rule] = { 21 | ScalastyleResources.allDefinedRules.filterNot(r => isTemplate(r)) map { 22 | defRule => 23 | val rule = Rule.create() 24 | val key = defRule.id 25 | rule.setRepositoryKey(Constants.RepositoryKey) 26 | rule.setLanguage(Constants.ScalaKey) 27 | rule.setKey(ScalastyleRepository.getStandardKey(defRule.clazz)) 28 | rule.setName(ScalastyleResources.label(key)) 29 | rule.setDescription(defRule.description) 30 | rule.setConfigKey(ScalastyleRepository.getStandardKey(defRule.clazz)) 31 | 32 | // currently all rules comes with "warning" default level so we can treat with major severity 33 | rule.setSeverity(RulePriority.MAJOR) 34 | 35 | // add parameters 36 | defRule.params foreach { 37 | param => 38 | rule 39 | .createParameter 40 | .setDefaultValue(param.defaultVal) 41 | .setType(param.`type`.`type`()) 42 | .setKey(param.name) 43 | .setDescription(param.desc) 44 | } 45 | 46 | // add synthetic parameter as reference to the class 47 | rule.createParameter(Constants.ClazzParam) 48 | .setDefaultValue(defRule.clazz) 49 | .setType(RuleParamType.STRING.`type`()) 50 | .setDescription("Scalastyle checker that validates the rule.") 51 | 52 | rule 53 | } 54 | } 55 | 56 | override def find(query: RuleQuery): Rule = ??? 57 | 58 | private def isTemplate(rule: RepositoryRule): Boolean = { 59 | rule.params.size() > 0 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/scala/com/ncredinburgh/sonar/scalastyle/testUtils/TestRuleFinderWithTemplates.scala: -------------------------------------------------------------------------------- 1 | package com.ncredinburgh.sonar.scalastyle.testUtils 2 | 3 | import java.util 4 | import com.ncredinburgh.sonar.scalastyle.{Constants, ScalastyleResources} 5 | import org.sonar.api.rule.RuleKey 6 | import org.sonar.api.rules.{RulePriority, Rule, RuleQuery, RuleFinder} 7 | import scala.collection.JavaConversions._ 8 | import com.ncredinburgh.sonar.scalastyle.ScalastyleRepository 9 | import org.sonar.api.server.rule.RuleParamType 10 | import com.ncredinburgh.sonar.scalastyle.RepositoryRule 11 | 12 | object TestRuleFinderWithTemplates extends RuleFinder { 13 | 14 | override def findByKey(repositoryKey: String, key: String): Rule = findAll(RuleQuery.create()).find(r => r.getRepositoryKey == repositoryKey && r.getKey == key).orNull 15 | 16 | override def findByKey(key: RuleKey): Rule = findAll(RuleQuery.create()).find(r => r.getRepositoryKey == key.repository() && r.getKey == key.rule()).orNull 17 | 18 | override def findById(ruleId: Int): Rule = findAll(RuleQuery.create()).find(r => r.getId == ruleId).orNull 19 | 20 | override def findAll(query: RuleQuery): util.Collection[Rule] = { 21 | ScalastyleResources.allDefinedRules map { 22 | defRule => 23 | val rule = Rule.create() 24 | val key = defRule.id 25 | rule.setRepositoryKey(Constants.RepositoryKey) 26 | rule.setLanguage(Constants.ScalaKey) 27 | rule.setKey(ScalastyleRepository.getStandardKey(defRule.clazz)) 28 | rule.setName(ScalastyleResources.label(key)) 29 | rule.setDescription(defRule.description) 30 | rule.setConfigKey(ScalastyleRepository.getStandardKey(defRule.clazz)) 31 | 32 | // currently all rules comes with "warning" default level so we can treat with major severity 33 | rule.setSeverity(RulePriority.MAJOR) 34 | 35 | // add parameters 36 | defRule.params foreach { 37 | param => 38 | rule 39 | .createParameter 40 | .setDefaultValue(param.defaultVal) 41 | .setType(param.`type`.`type`()) 42 | .setKey(param.name) 43 | .setDescription(param.desc) 44 | } 45 | 46 | // add synthetic parameter as reference to the class 47 | rule.createParameter(Constants.ClazzParam) 48 | .setDefaultValue(defRule.clazz) 49 | .setType(RuleParamType.STRING.`type`()) 50 | .setDescription("Scalastyle checker that validates the rule.") 51 | 52 | rule 53 | } 54 | } 55 | 56 | override def find(query: RuleQuery): Rule = ??? 57 | 58 | private def isTemplate(rule: RepositoryRule): Boolean = { 59 | rule.params.size() > 0 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/scala/com/sagacify/sonar/scala/BaseMetricSensorSpec.scala: -------------------------------------------------------------------------------- 1 | package com.sagacify.sonar.scala; 2 | 3 | import org.scalatest._ 4 | import org.mockito.Matchers.any; 5 | import org.mockito.Matchers.eq; 6 | import org.mockito.Mockito.mock; 7 | import org.mockito.Mockito.times; 8 | import org.mockito.Mockito.verify; 9 | import org.mockito.Mockito.verifyNoMoreInteractions; 10 | 11 | import java.io.IOException; 12 | import java.nio.file.Paths 13 | import scala.collection.JavaConversions._ 14 | 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.sonar.api.batch.SensorContext; 18 | import org.sonar.api.batch.fs.internal.DefaultFileSystem; 19 | import org.sonar.api.config.Settings; 20 | import org.sonar.api.measures.{CoreMetrics => CM} 21 | import org.sonar.api.measures.Measure; 22 | import org.sonar.api.measures.Metric; 23 | import org.sonar.api.resources.Project; 24 | 25 | import org.sonar.api.batch.fs.internal.DefaultInputFile; 26 | 27 | class ScalaSensorSpec extends FlatSpec with Matchers { 28 | 29 | val NUMBER_OF_FILES = 3; 30 | 31 | val scala = new Scala(new Settings()) 32 | 33 | def context = new { 34 | val fs = new DefaultFileSystem(Paths.get("./src/test/resources")) 35 | val project = mock(classOf[Project]) 36 | val sensor = new ScalaSensor(scala, fs) 37 | } 38 | 39 | // val project = mock(classOf[Project]) 40 | // val sensorContext = mock(classOf[SensorContext]) 41 | // val sensor = new BaseMetricsSensor(new Scala(new Settings()), fs) 42 | 43 | "A ScalaSensor" should "execute on a scala project" in { 44 | val c = context 45 | c.fs.add(new DefaultInputFile("p", "fake.scala").setLanguage("scala")); 46 | assert(c.sensor.shouldExecuteOnProject(c.project)) 47 | } 48 | 49 | it should "only execute on a scala project" in { 50 | val c = context 51 | c.fs.add(new DefaultInputFile("p", "fake.php").setLanguage("php")); 52 | assert(! c.sensor.shouldExecuteOnProject(c.project)) 53 | } 54 | 55 | it should "correctly measure ScalaFile1" in { 56 | val c = context 57 | c.fs.add( 58 | new DefaultInputFile("p", "ScalaFile1.scala").setLanguage("scala")); 59 | val sensorContext = mock(classOf[SensorContext]) 60 | c.sensor.analyse(c.project, sensorContext) 61 | 62 | val inputFiles = c.fs.inputFiles( 63 | c.fs.predicates().hasLanguage(scala.getKey())) 64 | 65 | inputFiles.foreach{ file => 66 | verify(sensorContext, times(1)) 67 | .saveMeasure(file, CM.FILES, 1) 68 | verify(sensorContext, times(1)) 69 | .saveMeasure(file, CM.COMMENT_LINES, 0) 70 | 71 | } 72 | } 73 | 74 | it should "correctly measure ScalaFile2" in { 75 | 76 | val c = context 77 | c.fs.add( 78 | new DefaultInputFile("p", "ScalaFile2.scala").setLanguage("scala")); 79 | val sensorContext = mock(classOf[SensorContext]) 80 | c.sensor.analyse(c.project, sensorContext) 81 | 82 | val inputFiles = c.fs.inputFiles( 83 | c.fs.predicates().hasLanguage(scala.getKey())) 84 | 85 | inputFiles.foreach{ file => 86 | verify(sensorContext, times(1)) 87 | .saveMeasure(file, CM.FILES, 1) 88 | verify(sensorContext, times(1)) 89 | .saveMeasure(file, CM.COMMENT_LINES, 1) 90 | 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/scala/com/sagacify/sonar/scala/MeasuresSpec.scala: -------------------------------------------------------------------------------- 1 | package com.sagacify.sonar.scala 2 | 3 | import org.scalatest._ 4 | // import scalariform.lexer.ScalaLexer 5 | // import scalariform.lexer.Token 6 | // import scalariform.lexer.Tokens.LINE_COMMENT 7 | // import scalariform.lexer.Tokens.MULTILINE_COMMENT 8 | // import scalariform.lexer.Tokens.XML_COMMENT 9 | // import scala.annotation.tailrec 10 | 11 | class MeasurersSpec extends FlatSpec with Matchers { 12 | 13 | val exampleSourceFile = """/* 14 | * Sonar Scala Plugin 15 | * Copyright (C) 2011-2016 SonarSource SA 16 | * mailto:contact AT sonarsource DOT com 17 | * 18 | * This program is free software; you can redistribute it and/or 19 | * modify it under the terms of the GNU Lesser General Public 20 | * License as published by the Free Software Foundation; either 21 | * version 3 of the License, or (at your option) any later version. 22 | * 23 | * This program is distributed in the hope that it will be useful, 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 26 | * Lesser General Public License for more details. 27 | * 28 | * You should have received a copy of the GNU Lesser General Public License 29 | * along with this program; if not, write to the Free Software Foundation, 30 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 31 | */ 32 | package com.sagacify.example 33 | 34 | import collection.mutable.Stack 35 | import org.scalatest._ 36 | 37 | class ScalaSensorSpec extends FlatSpec with Matchers { 38 | 39 | // Example test 40 | "A Stack" should "pop values in last-in-first-out order" in { 41 | val stack = new Stack[Int] 42 | stack.push(1) // This is 43 | stack.push(2) // a pointless 44 | stack.pop() should be (2) // example 45 | stack.pop() should be (1) 46 | } 47 | 48 | it should "throw NoSuchElementException if an empty stack is popped" in { 49 | val emptyStack = new Stack[Int] 50 | a [NoSuchElementException] should be thrownBy { 51 | emptyStack.pop() 52 | } 53 | } 54 | } 55 | """ 56 | 57 | "A Comment lines counter" should "count line comments" in { 58 | val tokens = Scala.tokenize("// this is a test", "2.11.8") 59 | val count = Measures.count_comment_lines(tokens) 60 | assert(count == 1) 61 | } 62 | 63 | it should "count multiline comments" in { 64 | val tokens = Scala.tokenize("/* this\n *is\n *a\n *test*/", "2.11.8") 65 | val count = Measures.count_comment_lines(tokens) 66 | assert(count == 4) 67 | } 68 | 69 | it should "count trailing comments." in { 70 | val tokens = Scala.tokenize("case class Test() // this is a test", "2.11.8") 71 | val count = Measures.count_comment_lines(tokens) 72 | assert(count == 1) 73 | } 74 | 75 | it should "count the correct number of comments" in { 76 | val tokens = Scala.tokenize(exampleSourceFile, "2.11.8") 77 | val count = Measures.count_comment_lines(tokens) 78 | assert(count == 23) 79 | } 80 | 81 | "A Non-Comment lines counter" should "count non-comment lines of codes" in { 82 | val tokens = Scala.tokenize("package com.example", "2.11.8") 83 | println(tokens) 84 | val count = Measures.count_ncloc(tokens) 85 | assert(count == 1) 86 | } 87 | 88 | it should "count lines of code with a trailing comment" in { 89 | val tokens = Scala.tokenize("case class Test() /*\n * test\n */", "2.11.8") 90 | val count = Measures.count_ncloc(tokens) 91 | assert(count == 1) 92 | } 93 | 94 | it should "count trailing code." in { 95 | val tokens = Scala.tokenize("/* this is a test */ case class Test()", "2.11.8") 96 | val count = Measures.count_ncloc(tokens) 97 | assert(count == 1) 98 | } 99 | 100 | it should "count the correct number of comments" in { 101 | val tokens = Scala.tokenize(exampleSourceFile, "2.11.8") 102 | val count = Measures.count_ncloc(tokens) 103 | assert(count == 18) 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/test/scala/com/sagacify/sonar/scala/ScalaPluginSpec.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Sonar Scalastyle Plugin 3 | * Copyright (C) 2014 All contributors 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3 of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 18 | */ 19 | package com.sagacify.sonar.scala 20 | 21 | import com.ncredinburgh.sonar.scalastyle.ScalastyleSensor 22 | import com.ncredinburgh.sonar.scalastyle.ScalastyleRepository 23 | import com.ncredinburgh.sonar.scalastyle.ScalastyleQualityProfile 24 | import org.scalatest.{FlatSpec, Matchers} 25 | 26 | /** 27 | * Tests ScalastylePlugin 28 | */ 29 | class ScalastylePluginSpec extends FlatSpec with Matchers { 30 | 31 | val testee = new ScalaPlugin 32 | 33 | "a scala plugin" should "provide a scala sensor" in { 34 | assert(testee.getExtensions.contains(classOf[ScalaSensor])) 35 | } 36 | 37 | it should "provide a scalastyle sensor" in { 38 | assert(testee.getExtensions.contains(classOf[ScalastyleSensor])) 39 | } 40 | 41 | it should "provide a scalastyle repository" in { 42 | assert(testee.getExtensions.contains(classOf[ScalastyleRepository])) 43 | } 44 | 45 | it should "provide a scala language" in { 46 | assert(testee.getExtensions.contains(classOf[Scala])) 47 | } 48 | 49 | it should "provide a scalastyle quality profile" in { 50 | assert(testee.getExtensions.contains(classOf[ScalastyleQualityProfile])) 51 | } 52 | } 53 | --------------------------------------------------------------------------------