Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge session/EL refactoring

  • Loading branch information...
commit 7f79cc08277dee78e4cc0fd6334a244e5dc34120 2 parents 78104ae + 48b24f1
@slandelle slandelle authored
Showing with 1,500 additions and 1,396 deletions.
  1. +142 −141 gatling-app/src/main/scala/com/excilys/ebi/gatling/app/Gatling.scala
  2. +167 −164 gatling-app/src/test/scala/com/excilys/ebi/gatling/app/test/CompileTest.scala
  3. +1 −1  gatling-bundle/src/main/assembly/assembly-structure/user-files/simulations/advanced/SomeScenario.scala
  4. +1 −1  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/report/GlobalReportGenerator.scala
  5. +1 −1  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/report/RequestDetailsReportGenerator.scala
  6. +1 −1  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/report/StatsReportGenerator.scala
  7. +7 −8 gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/FileDataReader.scala
  8. +5 −3 gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/Records.scala
  9. +1 −1  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/GeneralStatsBuffers.scala
  10. +1 −1  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/LatencyPerSecBuffers.scala
  11. +1 −1  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/RequestsPerSecBuffers.scala
  12. +1 −1  ...-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/ResponseTimePerSecBuffers.scala
  13. +2 −1  ...g-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/ResponseTimeRangeBuffers.scala
  14. +1 −1  ...-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/TransactionsPerSecBuffers.scala
  15. +2 −2 gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/package.scala
  16. +4 −0 gatling-core/pom.xml
  17. +20 −9 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/Predef.scala
  18. +6 −7 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/GroupAction.scala
  19. +4 −6 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/TryMaxAction.scala
  20. +19 −13 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/WhileAction.scala
  21. +4 −4 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/builder/GroupActionBuilder.scala
  22. +3 −5 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/builder/WhileActionBuilder.scala
  23. +14 −25 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/Check.scala
  24. +48 −37 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/CheckBuilder.scala
  25. +0 −24 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/CheckResult.scala
  26. +6 −3 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/package.scala
  27. +1 −1  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/message/DataWriterMessage.scala
  28. +3 −7 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/message/RequestStatus.scala
  29. +1 −1  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/reader/ChartRequestRecord.scala
  30. +2 −2 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/reader/DataReader.scala
  31. +2 −4 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/writer/ConsoleDataWriter.scala
  32. +1 −1  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/writer/DataWriter.scala
  33. +0 −83 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/ELParser.scala
  34. +104 −0 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/Expression.scala
  35. +51 −79 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/Session.scala
  36. +12 −12 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/handler/CounterBasedterationHandler.scala
  37. +8 −8 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/handler/TimerBasedIterationHandler.scala
  38. +9 −4 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/package.scala
  39. +1 −1  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/structure/Assertions.scala
  40. +3 −3 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/structure/ConditionalStatements.scala
  41. +1 −1  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/structure/Feeds.scala
  42. +2 −2 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/structure/Groups.scala
  43. +29 −15 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/structure/Loops.scala
  44. +1 −1  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/structure/package.scala
  45. +0 −2  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/util/StringHelper.scala
  46. +43 −0 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/util/TypeHelper.scala
  47. +0 −1  gatling-core/src/test/scala/com/excilys/ebi/gatling/core/result/message/RequestRecordSpec.scala
  48. +5 −6 gatling-core/src/test/scala/com/excilys/ebi/gatling/core/result/writer/FileDataWriterSpec.scala
  49. +67 −31 gatling-core/src/test/scala/com/excilys/ebi/gatling/core/session/ELParserSpec.scala
  50. +9 −9 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/Predef.scala
  51. +27 −18 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/action/HttpRequestAction.scala
  52. +7 −4 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/action/HttpRequestActionBuilder.scala
  53. +5 −6 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/ahc/GatlingAsyncHandlerActor.scala
  54. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/cache/CacheHandling.scala
  55. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/HttpCheck.scala
  56. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/HttpExtractorCheckBuilder.scala
  57. +3 −3 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/HttpMultipleCheckBuilder.scala
  58. +4 −4 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/HttpSingleCheckBuilder.scala
  59. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/after/HttpBodyResponseTimeCheckBuilder.scala
  60. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/body/HttpBodyCssCheckBuilder.scala
  61. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/body/HttpBodyJsonPathCheckBuilder.scala
  62. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/body/HttpBodyRegexCheckBuilder.scala
  63. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/body/HttpBodyStringCheckBuilder.scala
  64. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/body/HttpBodyXPathCheckBuilder.scala
  65. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/bodypart/ChecksumCheck.scala
  66. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/bodypart/HttpChecksumCheckBuilder.scala
  67. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/header/HttpHeaderCheckBuilder.scala
  68. +8 −3 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/header/HttpHeaderRegexCheckBuilder.scala
  69. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/status/CurrentLocationCheckBuilder.scala
  70. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/check/status/HttpStatusCheckBuilder.scala
  71. +7 −8 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/cookie/CookieHandling.scala
  72. +2 −3 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/referer/RefererHandling.scala
  73. +3 −3 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/HttpRequestBody.scala
  74. +80 −103 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/AbstractHttpRequestBuilder.scala
  75. +50 −53 ...c/main/scala/com/excilys/ebi/gatling/http/request/builder/AbstractHttpRequestWithBodyAndParamsBuilder.scala
  76. +48 −47 ...g-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/AbstractHttpRequestWithBodyBuilder.scala
  77. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/DeleteHttpRequestBuilder.scala
  78. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/GetHttpRequestBuilder.scala
  79. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/HeadHttpRequestBuilder.scala
  80. +13 −14 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/HttpRequestBaseBuilder.scala
  81. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/PostHttpRequestBuilder.scala
  82. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/PutHttpRequestBuilder.scala
  83. +16 −13 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/UploadedFile.scala
  84. +2 −2 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/request/builder/package.scala
  85. +26 −8 gatling-http/src/main/scala/com/excilys/ebi/gatling/http/util/HttpHelper.scala
  86. +11 −9 gatling-maven-plugin/src/main/java/com/excilys/ebi/gatling/mojo/GatlingMojo.java
  87. +1 −1  gatling-metrics/src/main/scala/com/excilys/ebi/gatling/metrics/types/RequestMetrics.scala
  88. +2 −0  gatling-recorder/src/main/resources/templates/simulation.ssp
  89. +324 −321 gatling-recorder/src/main/scala/com/excilys/ebi/gatling/recorder/ui/frame/ConfigurationFrame.scala
  90. +6 −0 pom.xml
View
283 gatling-app/src/main/scala/com/excilys/ebi/gatling/app/Gatling.scala
@@ -22,6 +22,7 @@ import com.excilys.ebi.gatling.app.CommandLineConstants._
import com.excilys.ebi.gatling.charts.report.ReportsGenerator
import com.excilys.ebi.gatling.core.config.{ GatlingFiles, GatlingPropertiesBuilder }
import com.excilys.ebi.gatling.core.config.GatlingConfiguration
+import com.excilys.ebi.gatling.core.config.GatlingConfiguration.configuration
import com.excilys.ebi.gatling.core.result.reader.DataReader
import com.excilys.ebi.gatling.core.runner.{ Runner, Selection }
import com.excilys.ebi.gatling.core.scenario.configuration.Simulation
@@ -36,150 +37,150 @@ import scopt.OptionParser
*/
object Gatling extends Logging {
- val SUCCESS = 0
- val INCORRECT_ARGUMENTS = 1
- val SIMULATION_CHECK_FAILED = 2
-
- /**
- * Entry point of Application
- *
- * @param args Arguments of the main method
- */
- def main(args: Array[String]) {
- sys.exit(runGatling(args))
- }
-
- def fromMap(props: JMap[String, Any]) = {
- GatlingConfiguration.setUp(props)
- new Gatling().start
- }
-
- def runGatling(args: Array[String]) = {
- val props = new GatlingPropertiesBuilder
-
- val cliOptsParser = new OptionParser("gatling") {
- opt(CLI_NO_REPORTS, CLI_NO_REPORTS_ALIAS, "Runs simulation but does not generate reports", { props.noReports })
- opt(CLI_REPORTS_ONLY, CLI_REPORTS_ONLY_ALIAS, "<directoryName>", "Generates the reports for the simulation in <directoryName>", { v: String => props.reportsOnly(v) })
- opt(CLI_DATA_FOLDER, CLI_DATA_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> as the absolute path of the directory where feeders are stored", { v: String => props.dataDirectory(v) })
- opt(CLI_RESULTS_FOLDER, CLI_RESULTS_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> as the absolute path of the directory where results are stored", { v: String => props.resultsDirectory(v) })
- opt(CLI_REQUEST_BODIES_FOLDER, CLI_REQUEST_BODIES_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> as the absolute path of the directory where request bodies are stored", { v: String => props.requestBodiesDirectory(v) })
- opt(CLI_SIMULATIONS_FOLDER, CLI_SIMULATIONS_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> to discover simulations that could be run", { v: String => props.sourcesDirectory(v) })
- opt(CLI_SIMULATIONS_BINARIES_FOLDER, CLI_SIMULATIONS_BINARIES_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> to discover already compiled simulations", { v: String => props.binariesDirectory(v) })
- opt(CLI_SIMULATION, CLI_SIMULATION_ALIAS, "<className>", "Runs <className> simulation", { v: String => props.clazz(v) })
- opt(CLI_OUTPUT_DIRECTORY_BASE_NAME, CLI_OUTPUT_DIRECTORY_BASE_NAME_ALIAS, "<name>", "Use <name> for the base name of the output directory", { v: String => props.outputDirectoryBaseName(v) })
- }
-
- // if arguments are incorrect, usage message is displayed
- if (cliOptsParser.parse(args)) fromMap(props.build)
- else INCORRECT_ARGUMENTS
- }
+ val SUCCESS = 0
+ val INCORRECT_ARGUMENTS = 1
+ val SIMULATION_CHECK_FAILED = 2
+
+ /**
+ * Entry point of Application
+ *
+ * @param args Arguments of the main method
+ */
+ def main(args: Array[String]) {
+ sys.exit(runGatling(args))
+ }
+
+ def fromMap(props: JMap[String, Any]) = {
+ GatlingConfiguration.setUp(props)
+ new Gatling().start
+ }
+
+ def runGatling(args: Array[String]) = {
+ val props = new GatlingPropertiesBuilder
+
+ val cliOptsParser = new OptionParser("gatling") {
+ opt(CLI_NO_REPORTS, CLI_NO_REPORTS_ALIAS, "Runs simulation but does not generate reports", { props.noReports })
+ opt(CLI_REPORTS_ONLY, CLI_REPORTS_ONLY_ALIAS, "<directoryName>", "Generates the reports for the simulation in <directoryName>", { v: String => props.reportsOnly(v) })
+ opt(CLI_DATA_FOLDER, CLI_DATA_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> as the absolute path of the directory where feeders are stored", { v: String => props.dataDirectory(v) })
+ opt(CLI_RESULTS_FOLDER, CLI_RESULTS_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> as the absolute path of the directory where results are stored", { v: String => props.resultsDirectory(v) })
+ opt(CLI_REQUEST_BODIES_FOLDER, CLI_REQUEST_BODIES_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> as the absolute path of the directory where request bodies are stored", { v: String => props.requestBodiesDirectory(v) })
+ opt(CLI_SIMULATIONS_FOLDER, CLI_SIMULATIONS_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> to discover simulations that could be run", { v: String => props.sourcesDirectory(v) })
+ opt(CLI_SIMULATIONS_BINARIES_FOLDER, CLI_SIMULATIONS_BINARIES_FOLDER_ALIAS, "<directoryPath>", "Uses <directoryPath> to discover already compiled simulations", { v: String => props.binariesDirectory(v) })
+ opt(CLI_SIMULATION, CLI_SIMULATION_ALIAS, "<className>", "Runs <className> simulation", { v: String => props.clazz(v) })
+ opt(CLI_OUTPUT_DIRECTORY_BASE_NAME, CLI_OUTPUT_DIRECTORY_BASE_NAME_ALIAS, "<name>", "Use <name> for the base name of the output directory", { v: String => props.outputDirectoryBaseName(v) })
+ }
+
+ // if arguments are incorrect, usage message is displayed
+ if (cliOptsParser.parse(args)) fromMap(props.build)
+ else INCORRECT_ARGUMENTS
+ }
}
class Gatling extends Logging {
- import GatlingConfiguration.configuration
-
- private def defaultOutputDirectoryBaseName(clazz: Class[Simulation]) = configuration.simulation.outputDirectoryBaseName.getOrElse(formatToFilename(clazz.getSimpleName))
-
- def start = {
- val (outputDirectoryName, simulation) = GatlingFiles.reportsOnlyDirectory.map((_, None))
- .getOrElse {
- val simulations = GatlingFiles.binariesDirectory
- .map(SimulationClassLoader.fromClasspathBinariesDirectory) // expect simulations to have been pre-compiled (ex: IDE)
- .getOrElse(SimulationClassLoader.fromSourcesDirectory(GatlingFiles.sourcesDirectory))
- .simulationClasses(configuration.simulation.clazz)
- .sortWith(_.getName < _.getName)
-
- val selection = configuration.simulation.clazz.map { _ =>
- val simulation = simulations.head
- val outputDirectoryBaseName = defaultOutputDirectoryBaseName(simulation)
- new Selection(simulation, outputDirectoryBaseName, outputDirectoryBaseName)
- }.getOrElse(interactiveSelect(simulations))
-
- val (runId, simulation) = new Runner(selection).run
- (runId, Some(simulation))
- }
-
- val dataReader = DataReader.newInstance(outputDirectoryName)
-
- val result = simulation match {
- case Some(simulation) => if (checkSimulation(simulation, dataReader)) Gatling.SUCCESS else Gatling.SIMULATION_CHECK_FAILED
- case None => Gatling.SUCCESS
- }
-
- if (!configuration.charting.noReports) generateReports(outputDirectoryName, dataReader)
-
- result
- }
-
- private def interactiveSelect(simulations: List[Class[Simulation]]): Selection = {
-
- val simulation = selectSimulationClass(simulations)
-
- val myDefaultOutputDirectoryBaseName = defaultOutputDirectoryBaseName(simulation)
-
- println("Select simulation id (default is '" + myDefaultOutputDirectoryBaseName + "'). Accepted characters are a-z, A-Z, 0-9, - and _")
- val simulationId = {
- val userInput = Console.readLine.trim
-
- require(userInput.matches("[\\w-_]*"), userInput + " contains illegal characters")
-
- if (!userInput.isEmpty) userInput else myDefaultOutputDirectoryBaseName
- }
-
- println("Select run description (optional)")
- val runDescription = Console.readLine.trim
-
- new Selection(simulation, simulationId, runDescription)
- }
-
- private def selectSimulationClass(simulations: List[Class[Simulation]]): Class[Simulation] = {
-
- val selection = simulations.size match {
- case 0 =>
- // If there is no simulation file
- println("There is no simulation script. Please check that your scripts are in user-files/simulations")
- sys.exit
- case 1 =>
- info(simulations.head.getName + " is the only simulation, executing it.")
- 0
- case size =>
- println("Choose a simulation number:")
- for ((simulation, index) <- simulations.zipWithIndex) {
- println(" [" + index + "] " + simulation.getName)
- }
- Console.readInt
- }
-
- val validRange = 0 until simulations.size
- if (validRange contains selection)
- simulations(selection)
- else {
- println("Invalid selection, must be in " + validRange)
- selectSimulationClass(simulations)
- }
- }
-
- /**
- * This method call the statistics module to generate the charts and statistics
- *
- * @param outputDirectoryName The directory from which the simulation.log will be parsed
- */
- private def generateReports(outputDirectoryName: String, dataReader: DataReader) {
- println("Generating reports...")
- val start = currentTimeMillis
- val indexFile = ReportsGenerator.generateFor(outputDirectoryName, dataReader)
- println("Reports generated in " + (currentTimeMillis - start) / 1000 + "s.")
- println("Please open the following file : " + indexFile)
- }
-
- private def checkSimulation(simulation: Simulation, dataReader: DataReader) = {
- val successful = Assertion.assertThat(simulation.assertions, dataReader)
-
- if (successful) println("Simulation successful.")
- else println("Simulation failed.")
-
- successful
- }
+ import GatlingConfiguration.configuration
+
+ private def defaultOutputDirectoryBaseName(clazz: Class[Simulation]) = configuration.simulation.outputDirectoryBaseName.getOrElse(formatToFilename(clazz.getSimpleName))
+
+ def start = {
+ val (outputDirectoryName, simulation) = GatlingFiles.reportsOnlyDirectory.map((_, None))
+ .getOrElse {
+ val simulations = GatlingFiles.binariesDirectory
+ .map(SimulationClassLoader.fromClasspathBinariesDirectory) // expect simulations to have been pre-compiled (ex: IDE)
+ .getOrElse(SimulationClassLoader.fromSourcesDirectory(GatlingFiles.sourcesDirectory))
+ .simulationClasses(configuration.simulation.clazz)
+ .sortWith(_.getName < _.getName)
+
+ val selection = configuration.simulation.clazz.map { _ =>
+ val simulation = simulations.head
+ val outputDirectoryBaseName = defaultOutputDirectoryBaseName(simulation)
+ new Selection(simulation, outputDirectoryBaseName, outputDirectoryBaseName)
+ }.getOrElse(interactiveSelect(simulations))
+
+ val (runId, simulation) = new Runner(selection).run
+ (runId, Some(simulation))
+ }
+
+ val dataReader = DataReader.newInstance(outputDirectoryName)
+
+ val result = simulation match {
+ case Some(simulation) => if (checkSimulation(simulation, dataReader)) Gatling.SUCCESS else Gatling.SIMULATION_CHECK_FAILED
+ case None => Gatling.SUCCESS
+ }
+
+ if (!configuration.charting.noReports) generateReports(outputDirectoryName, dataReader)
+
+ result
+ }
+
+ private def interactiveSelect(simulations: List[Class[Simulation]]): Selection = {
+
+ val simulation = selectSimulationClass(simulations)
+
+ val myDefaultOutputDirectoryBaseName = defaultOutputDirectoryBaseName(simulation)
+
+ println("Select simulation id (default is '" + myDefaultOutputDirectoryBaseName + "'). Accepted characters are a-z, A-Z, 0-9, - and _")
+ val simulationId = {
+ val userInput = Console.readLine.trim
+
+ require(userInput.matches("[\\w-_]*"), userInput + " contains illegal characters")
+
+ if (!userInput.isEmpty) userInput else myDefaultOutputDirectoryBaseName
+ }
+
+ println("Select run description (optional)")
+ val runDescription = Console.readLine.trim
+
+ new Selection(simulation, simulationId, runDescription)
+ }
+
+ private def selectSimulationClass(simulations: List[Class[Simulation]]): Class[Simulation] = {
+
+ val selection = simulations.size match {
+ case 0 =>
+ // If there is no simulation file
+ println("There is no simulation script. Please check that your scripts are in user-files/simulations")
+ sys.exit
+ case 1 =>
+ info(simulations.head.getName + " is the only simulation, executing it.")
+ 0
+ case size =>
+ println("Choose a simulation number:")
+ for ((simulation, index) <- simulations.zipWithIndex) {
+ println(" [" + index + "] " + simulation.getName)
+ }
+ Console.readInt
+ }
+
+ val validRange = 0 until simulations.size
+ if (validRange contains selection)
+ simulations(selection)
+ else {
+ println("Invalid selection, must be in " + validRange)
+ selectSimulationClass(simulations)
+ }
+ }
+
+ /**
+ * This method call the statistics module to generate the charts and statistics
+ *
+ * @param outputDirectoryName The directory from which the simulation.log will be parsed
+ */
+ private def generateReports(outputDirectoryName: String, dataReader: DataReader) {
+ println("Generating reports...")
+ val start = currentTimeMillis
+ val indexFile = ReportsGenerator.generateFor(outputDirectoryName, dataReader)
+ println("Reports generated in " + (currentTimeMillis - start) / 1000 + "s.")
+ println("Please open the following file : " + indexFile)
+ }
+
+ private def checkSimulation(simulation: Simulation, dataReader: DataReader) = {
+ val successful = Assertion.assertThat(simulation.assertions, dataReader)
+
+ if (successful) println("Simulation successful.")
+ else println("Simulation failed.")
+
+ successful
+ }
}
View
331 gatling-app/src/test/scala/com/excilys/ebi/gatling/app/test/CompileTest.scala
@@ -14,190 +14,193 @@
* limitations under the License.
*/
package com.excilys.ebi.gatling.app.test
+
import com.excilys.ebi.gatling.core.Predef._
+import com.excilys.ebi.gatling.http.Headers.Names._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.jdbc.Predef._
-import akka.util.duration._
-import com.excilys.ebi.gatling.http.Headers.Names._
-import bootstrap._
import assertions._
+import bootstrap._
+
+import akka.util.duration._
object CompileTest extends Simulation {
- val iterations = 10
- val pause1 = 1
- val pause2 = 2
- val pause3 = 3
+ val iterations = 10
+ val pause1 = 1
+ val pause2 = 2
+ val pause3 = 3
- val baseUrl = "http://localhost:3000"
+ val baseUrl = "http://localhost:3000"
- val httpConf = httpConfig.baseURL(baseUrl).proxy("91.121.211.157", 80).httpsPort(4443).credentials("rom", "test")
+ val httpConf = httpConfig.baseURL(baseUrl).proxy("91.121.211.157", 80).httpsPort(4443).credentials("rom", "test")
- val httpConf2 = httpConfig
- .baseURL("http://172.30.5.143:8080")
- .proxy("172.31.76.106", 8080)
- .httpsPort(8081)
- .acceptHeader("*/*")
- .acceptCharsetHeader("ISO-8859-1,utf-8;q=0.7,*;q=0.3")
- .acceptLanguageHeader("fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4")
- .acceptEncodingHeader("gzip,deflate,sdch")
- .userAgentHeader("Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/18.0.1025.151 Chrome/18.0.1025.151 Safari/535.19")
+ val httpConf2 = httpConfig
+ .baseURL("http://172.30.5.143:8080")
+ .proxy("172.31.76.106", 8080)
+ .httpsPort(8081)
+ .acceptHeader("*/*")
+ .acceptCharsetHeader("ISO-8859-1,utf-8;q=0.7,*;q=0.3")
+ .acceptLanguageHeader("fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4")
+ .acceptEncodingHeader("gzip,deflate,sdch")
+ .userAgentHeader("Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/12.04 Chromium/18.0.1025.151 Chrome/18.0.1025.151 Safari/535.19")
- val httpConfToVerifyUserProvidedInfoExtractors = httpConfig
- .requestInfoExtractor((request: Request) => { List.empty })
- .responseInfoExtractor((response: Response) => { List.empty })
+ val httpConfToVerifyUserProvidedInfoExtractors = httpConfig
+ .requestInfoExtractor((request: Request) => { List.empty })
+ .responseInfoExtractor((response: Response) => { List.empty })
- val usersInformation = tsv("user_information.tsv")
+ val usersInformation = tsv("user_information.tsv")
- val loginChain = exec(http("First Request Chain").get("/")).pause(1, 2)
+ val loginChain = exec(http("First Request Chain").get("/")).pause(1, 2)
- val testData = tsv("test-data.tsv")
+ val testData = tsv("test-data.tsv")
- val richTestData = testData.queue.map {
- _.map {
- case (key @ "keyOfAMultivaluedColumn", value) => (key, value.split(","))
- case keyVal => keyVal
- }
- }
+ val richTestData = testData.queue.map {
+ _.map {
+ case (key @ "keyOfAMultivaluedColumn", value) => (key, value.split(","))
+ case keyVal => keyVal
+ }
+ }
- val testData2 = jdbcFeeder("jdbc:postgresql:gatling", "gatling", "gatling", """
+ val testData2 = jdbcFeeder("jdbc:postgresql:gatling", "gatling", "gatling", """
select login as "username", password
from usr
where id in (select usr_id from usr_role where role_id='ROLE_USER')
and id not in (select usr_id from usr_role where role_id='ROLE_ADMIN')
and (select count(*) from usr_account where usr_id=id) >=2""")
- val testData3 = Array(Map("foo" -> "bar")).circular
-
- val lambdaUser = scenario("Standard User")
- .exec(loginChain)
- // First request outside iteration
- .repeat(2) {
- feed(richTestData)
- .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
- }
- .repeat(2, "counterName") {
- feed(testData.circular)
- .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
- }
- .during(10 seconds) {
- feed(testData)
- .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
- }
- .asLongAs(true) {
- feed(testData)
- .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
- }
- .exec(http("Catégorie Poney").get("/").queryParam("omg"))
- .exec(http("Catégorie Poney").get("/").queryParam("omg", "foo"))
- .exec(http("Catégorie Poney").get("/").queryParam("omg", "${foo}"))
- .exec(http("Catégorie Poney").get("/").queryParam("omg", session => "foo"))
- .exec(http("Catégorie Poney").get("/").multiValuedQueryParam("omg", List("foo")))
- .exec(http("Catégorie Poney").get("/").multiValuedQueryParam("omg", "${foo}"))
- .exec(http("Catégorie Poney").get("/").multiValuedQueryParam("omg", session => List("foo")))
- .randomSwitch(
- 40 -> exec(http("Catégorie Poney").get("/")),
- 50 -> exec(http("Catégorie Poney").get("/")))
- .pause(pause2, pause3)
- // Loop
- .repeat(iterations, "titi") {
- // What will be repeated ?
- // First request to be repeated
- exec(session => {
- println("iterate: " + session.getAttribute("titi"))
- session
- })
- .exec(
- http("Page accueil").get("http://localhost:3000")
- .check(
- xpath("//input[@value='${aaaa_value}']/@id").saveAs("sessionParam"),
- xpath("//input[@id='${aaaa_value}']/@value").notExists,
- css(""".foo"""),
- css("""#foo""", "href"),
- css(""".foo""").count.is(1),
- css(""".foo""").notExists,
- regex("""<input id="text1" type="text" value="aaaa" />"""),
- regex("""<input id="text1" type="text" value="aaaa" />""").count.is(1),
- regex("""<input id="text1" type="test" value="aaaa" />""").notExists,
- status.in(200 to 210).saveAs("blablaParam"),
- xpath("//input[@value='aaaa']/@id").not("omg"),
- xpath("//input[@id='text1']/@value").is("aaaa").saveAs("test2"),
- md5.is("0xA59E79AB53EEF2883D72B8F8398C9AC3"),
- responseTimeInMillis.lessThan(1000),
- latencyInMillis.lessThan(1000)))
- .during(12000 milliseconds, "foo") {
- exec(http("In During 1").get("http://localhost:3000/aaaa"))
- .pause(2)
- .repeat(2, "tutu") {
- exec(session => {
- println("--nested loop: " + session.getAttribute("tutu"))
- session
- })
- }
- .exec(session => {
- println("-loopDuring: " + session.getAttribute("foo"))
- session
- })
- .exec(http("In During 2").get("/"))
- .pause(2)
- }
- .pause(pause2)
- .during(12000 milliseconds, "hehe") {
- exec(http("In During 1").get("/"))
- .pause(2)
- .exec(session => {
- println("-iterate1: " + session.getAttribute("titi") + ", doFor: " + session.getAttribute("hehe"))
- session
- })
- .repeat(2, "hoho") {
- exec(session => {
- println("--iterate1: " + session.getAttribute("titi") + ", doFor: " + session.getAttribute("hehe") + ", iterate2: " + session.getAttribute("hoho"))
- session
- })
- }
- .exec(http("In During 2").get("/"))
- .pause(2)
- }
- .exec(session => session.setAttribute("test2", "bbbb"))
- .doIfOrElse("test2", "aaaa") {
- exec(http("IF=TRUE Request").get("/"))
- } {
- exec(http("IF=FALSE Request").get("/"))
- }.pause(pause2)
- .exec(http("Url from session").get("/aaaa"))
- .pause(1000 milliseconds, 3000 milliseconds)
- // Second request to be repeated
- .exec(http("Create Thing blabla").post("/things").queryParam("login").queryParam("password").fileBody("create_thing", Map("name" -> "blabla")).asJSON)
- .pause(pause1)
- // Third request to be repeated
- .exec(http("Liste Articles").get("/things").queryParam("firstname").queryParam("lastname"))
- .pauseExp(pause1)
- .exec(http("Test Page").get("/tests").check(header(CONTENT_TYPE).is("text/html; charset=utf-8").saveAs("sessionParam")))
- // Fourth request to be repeated
- .pauseExp(100 milliseconds)
- // switch
- .randomSwitch(
- 40 -> exec(http("Possibility 1").get("/p1")),
- 55 -> exec(http("Possibility 2").get("/p2")) // last 5% bypass
- )
- .exec(http("Create Thing omgomg")
- .post("/things").queryParam("postTest", "${sessionParam}").fileBody("create_thing", Map("name" -> "${sessionParam}")).asJSON
- .check(status.is(201).saveAs("status")))
- }
- // Head request
- .exec(http("head on root").head("/"))
- // Second request outside iteration
- .exec(http("Ajout au panier").get("/").check(regex("""<input id="text1" type="text" value="(.*)" />""").saveAs("input")))
- .pause(pause1)
-
- setUp(lambdaUser.users(5).ramp(10).protocolConfig(httpConf))
-
- assertThat(
- global.responseTime.mean.lessThan(50),
- global.responseTime.max.between(50, 500),
- global.successfulRequests.count.greaterThan(1500),
- global.allRequests.percent.is(100),
- global.responseTime.min.assert(_ % 2 == 0, (name, result) => "My custom assert on " + name + " (" + result + ")"),
- details("Users" / "Search" / "Index page").responseTime.mean.greaterThan(0).lessThan(50),
- details("Admins" / "Create").failedRequests.percent.lessThan(90))
+ val testData3 = Array(Map("foo" -> "bar")).circular
+
+ val lambdaUser = scenario("Standard User")
+ .exec(loginChain)
+ // First request outside iteration
+ .repeat(2) {
+ feed(richTestData)
+ .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
+ }
+ .repeat(2, "counterName") {
+ feed(testData.circular)
+ .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
+ }
+ .during(10 seconds) {
+ feed(testData)
+ .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
+ }
+ .asLongAs(true) {
+ feed(testData)
+ .exec(http("Catégorie Poney").get("/").queryParam("omg").queryParam("socool").basicAuth("", "").check(xpath("//input[@id='text1']/@value").transform(_ + "foo").saveAs("aaaa_value"), jsonPath("//foo/bar[2]/baz")))
+ }
+ .exec(http("Catégorie Poney").get("/").queryParam("omg"))
+ .exec(http("Catégorie Poney").get("/").queryParam("omg", "foo"))
+ .exec(http("Catégorie Poney").get("/").queryParam("omg", "${foo}"))
+ .exec(http("Catégorie Poney").get("/").queryParam("omg", session => "foo"))
+ .exec(http("Catégorie Poney").get("/").multiValuedQueryParam("omg", List("foo")))
+ .exec(http("Catégorie Poney").get("/").multiValuedQueryParam("omg", "${foo}"))
+ .exec(http("Catégorie Poney").get("/").multiValuedQueryParam("omg", List("foo")))
+ .randomSwitch(
+ 40 -> exec(http("Catégorie Poney").get("/")),
+ 50 -> exec(http("Catégorie Poney").get("/")))
+ .pause(pause2, pause3)
+ // Loop
+ .repeat(iterations, "titi") {
+ // What will be repeated ?
+ // First request to be repeated
+ exec(session => {
+ println("iterate: " + session("titi"))
+ session
+ })
+ .exec(
+ http("Page accueil").get("http://localhost:3000")
+ .check(
+ xpath("//input[@value='${aaaa_value}']/@id").saveAs("sessionParam"),
+ xpath("//input[@id='${aaaa_value}']/@value").notExists,
+ css(""".foo"""),
+ css("""#foo""", "href"),
+ css(""".foo""").count.is(1),
+ css(""".foo""").notExists,
+ regex("""<input id="text1" type="text" value="aaaa" />"""),
+ regex("""<input id="text1" type="text" value="aaaa" />""").count.is(1),
+ regex("""<input id="text1" type="test" value="aaaa" />""").notExists,
+ status.in(200 to 210).saveAs("blablaParam"),
+ xpath("//input[@value='aaaa']/@id").not("omg"),
+ xpath("//input[@id='text1']/@value").is("aaaa").saveAs("test2"),
+ md5.is("0xA59E79AB53EEF2883D72B8F8398C9AC3"),
+ responseTimeInMillis.lessThan(1000),
+ latencyInMillis.lessThan(1000)))
+ .during(12000 milliseconds, "foo") {
+ exec(http("In During 1").get("http://localhost:3000/aaaa"))
+ .pause(2)
+ .repeat(2, "tutu") {
+ exec(session => {
+ println("--nested loop: " + session("tutu"))
+ session
+ })
+ }
+ .exec(session => {
+ println("-loopDuring: " + session("foo"))
+ session
+ })
+ .exec(http("In During 2").get("/"))
+ .pause(2)
+ }
+ .pause(pause2)
+ .during(12000 milliseconds, "hehe") {
+ exec(http("In During 1").get("/"))
+ .pause(2)
+ .exec(session => {
+ println("-iterate1: " + session("titi") + ", doFor: " + session("hehe"))
+ session
+ })
+ .repeat(2, "hoho") {
+ exec(session => {
+ println("--iterate1: " + session("titi") + ", doFor: " + session("hehe") + ", iterate2: " + session("hoho"))
+ session
+ })
+ }
+ .exec(http("In During 2").get("/"))
+ .pause(2)
+ }
+ .exec(session => session.set("test2", "bbbb"))
+ .doIfOrElse("test2", "aaaa") {
+ exec(http("IF=TRUE Request").get("/"))
+ } {
+ exec(http("IF=FALSE Request").get("/"))
+ }.pause(pause2)
+ .exec(http("Url from session").get("/aaaa"))
+ .pause(1000 milliseconds, 3000 milliseconds)
+ // Second request to be repeated
+ .exec(http("Create Thing blabla").post("/things").queryParam("login").queryParam("password").fileBody("create_thing", Map("name" -> "blabla")).asJSON)
+ .pause(pause1)
+ // Third request to be repeated
+ .exec(http("Liste Articles").get("/things").queryParam("firstname").queryParam("lastname"))
+ .pauseExp(pause1)
+ .exec(http("Test Page").get("/tests").check(header(CONTENT_TYPE).is("text/html; charset=utf-8").saveAs("sessionParam")))
+ // Fourth request to be repeated
+ .pauseExp(100 milliseconds)
+ // switch
+ .randomSwitch(
+ 40 -> exec(http("Possibility 1").get("/p1")),
+ 55 -> exec(http("Possibility 2").get("/p2")) // last 5% bypass
+ )
+ .exec(http("Create Thing omgomg")
+ .post("/things").queryParam("postTest", "${sessionParam}").fileBody("create_thing", Map("name" -> "${sessionParam}")).asJSON
+ .check(status.is(201).saveAs("status")))
+ }
+ // Head request
+ .exec(http("head on root").head("/"))
+ // Second request outside iteration
+ .exec(http("Ajout au panier").get("/").check(regex("""<input id="text1" type="text" value="(.*)" />""").saveAs("input")))
+ .exec(http("Ajout au panier").get("/").check(regex(session => """<input id="text1" type="text" value="smth" />""").saveAs("input")))
+ .pause(pause1)
+
+ setUp(lambdaUser.users(5).ramp(10).protocolConfig(httpConf))
+
+ assertThat(
+ global.responseTime.mean.lessThan(50),
+ global.responseTime.max.between(50, 500),
+ global.successfulRequests.count.greaterThan(1500),
+ global.allRequests.percent.is(100),
+ global.responseTime.min.assert(_ % 2 == 0, (name, result) => "My custom assert on " + name + " (" + result + ")"),
+ details("Users" / "Search" / "Index page").responseTime.mean.greaterThan(0).lessThan(50),
+ details("Admins" / "Create").failedRequests.percent.lessThan(90))
}
View
2  gatling-bundle/src/main/assembly/assembly-structure/user-files/simulations/advanced/SomeScenario.scala
@@ -56,7 +56,7 @@ object SomeScenario {
.headers(headers_8))
.pause(6, 7)
}
- .doIf(session => session.getAttribute("username") != "user7") {
+ .doIf(session => session.get("username") != "user7") {
exec(
http("request_9")
.get("/logout")
View
2  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/report/GlobalReportGenerator.scala
@@ -20,7 +20,7 @@ import com.excilys.ebi.gatling.charts.config.ChartsFiles.globalFile
import com.excilys.ebi.gatling.charts.series.Series
import com.excilys.ebi.gatling.charts.template.GlobalPageTemplate
import com.excilys.ebi.gatling.charts.util.Colors.{ BLUE, CYAN, GREEN, LIGHT_BLUE, LIGHT_LIME, LIGHT_ORANGE, LIGHT_PINK, LIGHT_PURPLE, LIGHT_RED, LIME, PINK, PURPLE, RED, YELLOW }
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.{ KO, OK }
+import com.excilys.ebi.gatling.core.result.message.{ KO, OK }
import com.excilys.ebi.gatling.core.result.reader.DataReader
class GlobalReportGenerator(runOn: String, dataReader: DataReader, componentLibrary: ComponentLibrary) extends ReportGenerator(runOn, dataReader, componentLibrary) {
View
2  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/report/RequestDetailsReportGenerator.scala
@@ -21,7 +21,7 @@ import com.excilys.ebi.gatling.charts.series.Series
import com.excilys.ebi.gatling.charts.template.RequestDetailsPageTemplate
import com.excilys.ebi.gatling.charts.util.Colors.{ BLUE, RED, TRANSLUCID_BLUE, TRANSLUCID_RED }
import com.excilys.ebi.gatling.core.result.{ Group, RequestPath }
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.{ KO, OK }
+import com.excilys.ebi.gatling.core.result.message.{ KO, OK }
import com.excilys.ebi.gatling.core.result.reader.DataReader
class RequestDetailsReportGenerator(runOn: String, dataReader: DataReader, componentLibrary: ComponentLibrary) extends ReportGenerator(runOn, dataReader, componentLibrary) {
View
2  gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/report/StatsReportGenerator.scala
@@ -20,7 +20,7 @@ import com.excilys.ebi.gatling.charts.config.ChartsFiles.{ GLOBAL_PAGE_NAME, jsS
import com.excilys.ebi.gatling.charts.template.{ StatsJsTemplate, StatsJsonTemplate, StatsTsvTemplate }
import com.excilys.ebi.gatling.core.config.GatlingConfiguration.configuration
import com.excilys.ebi.gatling.core.result.{ RequestPath, Group }
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.{ KO, OK }
+import com.excilys.ebi.gatling.core.result.message.{ KO, OK }
import com.excilys.ebi.gatling.core.result.reader.DataReader
class StatsReportGenerator(runOn: String, dataReader: DataReader, componentLibrary: ComponentLibrary) {
View
15 gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/FileDataReader.scala
@@ -27,8 +27,7 @@ import com.excilys.ebi.gatling.core.config.GatlingConfiguration.configuration
import com.excilys.ebi.gatling.core.config.GatlingFiles.simulationLogDirectory
import com.excilys.ebi.gatling.core.result.Group
import com.excilys.ebi.gatling.core.result.message.RecordType.{ ACTION, GROUP, RUN, SCENARIO }
-import com.excilys.ebi.gatling.core.result.message.RequestStatus
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.{ KO, OK }
+import com.excilys.ebi.gatling.core.result.message.{ KO, OK, RequestStatus }
import com.excilys.ebi.gatling.core.result.message.RunRecord
import com.excilys.ebi.gatling.core.result.reader.{ DataReader, GeneralStats }
import com.excilys.ebi.gatling.core.util.DateHelper.parseTimestampString
@@ -136,7 +135,7 @@ class FileDataReader(runUuid: String) extends DataReader(runUuid) with Logging {
.getSessionDeltaPerSecBuffers(scenarioName)
.compute(buckets)
- def numberOfRequestsPerSecond(status: Option[RequestStatus.RequestStatus], requestName: Option[String], group: Option[Group]): Seq[(Int, Int)] = resultsHolder
+ def numberOfRequestsPerSecond(status: Option[RequestStatus], requestName: Option[String], group: Option[Group]): Seq[(Int, Int)] = resultsHolder
.getRequestsPerSecBuffer(requestName, group, status).map
.toList
.map {
@@ -144,7 +143,7 @@ class FileDataReader(runUuid: String) extends DataReader(runUuid) with Logging {
}
.sorted
- def numberOfTransactionsPerSecond(status: Option[RequestStatus.RequestStatus], requestName: Option[String], group: Option[Group]): Seq[(Int, Int)] = resultsHolder
+ def numberOfTransactionsPerSecond(status: Option[RequestStatus], requestName: Option[String], group: Option[Group]): Seq[(Int, Int)] = resultsHolder
.getTransactionsPerSecBuffer(requestName, group, status).map
.toList
.map {
@@ -192,7 +191,7 @@ class FileDataReader(runUuid: String) extends DataReader(runUuid) with Logging {
(process(ok), process(ko))
}
- def generalStats(status: Option[RequestStatus.RequestStatus], requestName: Option[String], group: Option[Group]): GeneralStats = resultsHolder
+ def generalStats(status: Option[RequestStatus], requestName: Option[String], group: Option[Group]): GeneralStats = resultsHolder
.getGeneralStatsBuffers(requestName, group, status)
.compute
@@ -210,19 +209,19 @@ class FileDataReader(runUuid: String) extends DataReader(runUuid) with Logging {
("failed", counts.ko))
}
- def responseTimeGroupByExecutionStartDate(status: RequestStatus.RequestStatus, requestName: Option[String], group: Option[Group]): Seq[(Int, (Int, Int))] = resultsHolder
+ def responseTimeGroupByExecutionStartDate(status: RequestStatus, requestName: Option[String], group: Option[Group]): Seq[(Int, (Int, Int))] = resultsHolder
.getResponseTimePerSecBuffers(requestName, group, Some(status))
.map
.toList
.sorted
- def latencyGroupByExecutionStartDate(status: RequestStatus.RequestStatus, requestName: Option[String], group: Option[Group]): Seq[(Int, (Int, Int))] = resultsHolder
+ def latencyGroupByExecutionStartDate(status: RequestStatus, requestName: Option[String], group: Option[Group]): Seq[(Int, (Int, Int))] = resultsHolder
.getLatencyPerSecBuffers(requestName, group, Some(status))
.map
.toList
.sorted
- def responseTimeAgainstGlobalNumberOfRequestsPerSec(status: RequestStatus.RequestStatus, requestName: Option[String], group: Option[Group]): Seq[(Int, Int)] = {
+ def responseTimeAgainstGlobalNumberOfRequestsPerSec(status: RequestStatus, requestName: Option[String], group: Option[Group]): Seq[(Int, Int)] = {
val globalCountsByBucket = resultsHolder.getRequestsPerSecBuffer(None, None, None).map
View
8 gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/Records.scala
@@ -15,8 +15,7 @@
*/
package com.excilys.ebi.gatling.charts.result.reader
-import com.excilys.ebi.gatling.core.result.message.RequestStatus
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.RequestStatus
+import com.excilys.ebi.gatling.core.result.message.{ KO, OK, RequestStatus }
object ActionRecord {
@@ -29,7 +28,10 @@ object ActionRecord {
val requestEnd = reduceAccuracy((strings(5).toLong - runStart).toInt)
val responseStart = reduceAccuracy((strings(6).toLong - runStart).toInt)
val executionEnd = reduceAccuracy((strings(7).toLong - runStart).toInt)
- val status = RequestStatus.withName(strings(8))
+ val status = strings(8) match {
+ case "OK" => OK
+ case _ => KO
+ }
val executionStartBucket = bucketFunction(executionStart)
val executionEndBucket = bucketFunction(executionEnd)
val responseTime = reduceAccuracy(executionEnd - executionStart)
View
2  ...ing-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/GeneralStatsBuffers.scala
@@ -33,7 +33,7 @@ abstract class GeneralStatsBuffers(durationInSec: Long) extends Buffers {
val generalStatsBuffers: mutable.Map[BufferKey, GeneralStatsBuffer] = new JHashMap[BufferKey, GeneralStatsBuffer]
- def getGeneralStatsBuffers(request: Option[String], group: Option[Group], status: Option[RequestStatus.RequestStatus]): GeneralStatsBuffer =
+ def getGeneralStatsBuffers(request: Option[String], group: Option[Group], status: Option[RequestStatus]): GeneralStatsBuffer =
generalStatsBuffers.getOrElseUpdate(computeKey(request, group, status), new GeneralStatsBuffer(durationInSec))
def updateGeneralStatsBuffers(record: ActionRecord, group: Option[Group]) {
View
2  ...ng-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/LatencyPerSecBuffers.scala
@@ -28,7 +28,7 @@ trait LatencyPerSecBuffers extends Buffers {
val latencyPerSecBuffers: mutable.Map[BufferKey, RangeBuffer] = new JHashMap[BufferKey, RangeBuffer]
- def getLatencyPerSecBuffers(requestName: Option[String], group: Option[Group], status: Option[RequestStatus.RequestStatus]): RangeBuffer = latencyPerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new RangeBuffer)
+ def getLatencyPerSecBuffers(requestName: Option[String], group: Option[Group], status: Option[RequestStatus]): RangeBuffer = latencyPerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new RangeBuffer)
def updateLatencyPerSecBuffers(record: ActionRecord, group: Option[Group]) {
recursivelyUpdate(record, group) {
View
2  ...g-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/RequestsPerSecBuffers.scala
@@ -28,7 +28,7 @@ trait RequestsPerSecBuffers extends Buffers {
val requestsPerSecBuffers: mutable.Map[BufferKey, CountBuffer] = new JHashMap[BufferKey, CountBuffer]
- def getRequestsPerSecBuffer(requestName: Option[String], group: Option[Group], status: Option[RequestStatus.RequestStatus]): CountBuffer = requestsPerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new CountBuffer)
+ def getRequestsPerSecBuffer(requestName: Option[String], group: Option[Group], status: Option[RequestStatus]): CountBuffer = requestsPerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new CountBuffer)
def updateRequestsPerSecBuffers(record: ActionRecord, group: Option[Group]) {
recursivelyUpdate(record, group) { (record, group) =>
View
2  ...arts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/ResponseTimePerSecBuffers.scala
@@ -28,7 +28,7 @@ trait ResponseTimePerSecBuffers extends Buffers {
val responseTimePerSecBuffers: mutable.Map[BufferKey, RangeBuffer] = new JHashMap[BufferKey, RangeBuffer]
- def getResponseTimePerSecBuffers(requestName: Option[String], group: Option[Group], status: Option[RequestStatus.RequestStatus]): RangeBuffer = responseTimePerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new RangeBuffer)
+ def getResponseTimePerSecBuffers(requestName: Option[String], group: Option[Group], status: Option[RequestStatus]): RangeBuffer = responseTimePerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new RangeBuffer)
def updateResponseTimePerSecBuffers(record: ActionRecord, group: Option[Group]) {
recursivelyUpdate(record, group) { (record, group) =>
View
3  ...harts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/ResponseTimeRangeBuffers.scala
@@ -22,6 +22,7 @@ import scala.collection.mutable
import com.excilys.ebi.gatling.charts.result.reader.ActionRecord
import com.excilys.ebi.gatling.core.result.Group
+import com.excilys.ebi.gatling.core.result.message.KO
trait ResponseTimeRangeBuffers extends Buffers {
@@ -49,7 +50,7 @@ trait ResponseTimeRangeBuffers extends Buffers {
def update(record: ActionRecord) {
- if (record.status == RequestStatus.KO) ko += 1
+ if (record.status == KO) ko += 1
else if (record.responseTime < configuration.charting.indicators.lowerBound) low += 1
else if (record.responseTime > configuration.charting.indicators.higherBound) high += 1
else middle += 1
View
2  ...arts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/TransactionsPerSecBuffers.scala
@@ -28,7 +28,7 @@ trait TransactionsPerSecBuffers extends Buffers {
val transactionsPerSecBuffers: mutable.Map[BufferKey, CountBuffer] = new JHashMap[BufferKey, CountBuffer]
- def getTransactionsPerSecBuffer(requestName: Option[String], group: Option[Group], status: Option[RequestStatus.RequestStatus]): CountBuffer = transactionsPerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new CountBuffer)
+ def getTransactionsPerSecBuffer(requestName: Option[String], group: Option[Group], status: Option[RequestStatus]): CountBuffer = transactionsPerSecBuffers.getOrElseUpdate(computeKey(requestName, group, status), new CountBuffer)
def updateTransactionsPerSecBuffers(record: ActionRecord, group: Option[Group]) {
recursivelyUpdate(record, group) { (record, group) =>
View
4 gatling-charts/src/main/scala/com/excilys/ebi/gatling/charts/result/reader/buffers/package.scala
@@ -20,7 +20,7 @@ import com.excilys.ebi.gatling.core.result.message.RequestStatus
package object buffers {
- type BufferKey = (Option[Group], Option[String], Option[RequestStatus.RequestStatus])
+ type BufferKey = (Option[Group], Option[String], Option[RequestStatus])
- def computeKey(request: Option[String], group: Option[Group], status: Option[RequestStatus.RequestStatus]): BufferKey = (group, request, status)
+ def computeKey(request: Option[String], group: Option[Group], status: Option[RequestStatus]): BufferKey = (group, request, status)
}
View
4 gatling-core/pom.xml
@@ -25,6 +25,10 @@
<artifactId>scala-compiler</artifactId>
</dependency>
<dependency>
+ <groupId>org.scalaz</groupId>
+ <artifactId>scalaz-core_2.9.2</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor</artifactId>
</dependency>
View
29 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/Predef.scala
@@ -20,18 +20,28 @@ import scala.tools.nsc.io.{ File, Path }
import com.excilys.ebi.gatling.core.check.{ Check, CheckBuilder, ExtractorCheckBuilder, MatcherCheckBuilder }
import com.excilys.ebi.gatling.core.feeder.FeederBuiltIns
import com.excilys.ebi.gatling.core.feeder.csv.SeparatedValuesParser
-import com.excilys.ebi.gatling.core.session.ELParser.parseEL
+import com.excilys.ebi.gatling.core.session.Expression
import com.excilys.ebi.gatling.core.structure.{ AssertionBuilder, ChainBuilder, ScenarioBuilder }
+import scalaz._
+import Scalaz._
+
object Predef {
- implicit def stringToEvaluatableString(string: String) = parseEL(string)
- implicit def toSessionFunction[X](x: X) = (session: Session) => x
- implicit def checkBuilderToCheck[C <: Check[R, XC], R, XC](checkBuilder: CheckBuilder[C, R, XC]) = checkBuilder.build
- implicit def matcherCheckBuilderToCheckBuilder[C <: Check[R, XC], R, XC, X](matcherCheckBuilder: MatcherCheckBuilder[C, R, XC, X]) = matcherCheckBuilder.exists
- implicit def matcherCheckBuilderToCheck[C <: Check[R, XC], R, XC, X](matcherCheckBuilder: MatcherCheckBuilder[C, R, XC, X]) = matcherCheckBuilder.exists.build
- implicit def extractorCheckBuilderToMatcherCheckBuilder[C <: Check[R, XC], R, XC, X](extractorCheckBuilder: ExtractorCheckBuilder[C, R, XC, X]) = extractorCheckBuilder.find
- implicit def extractorCheckBuilderToCheckBuilder[C <: Check[R, XC], R, XC, X](extractorCheckBuilder: ExtractorCheckBuilder[C, R, XC, X]) = extractorCheckBuilder.find.exists
- implicit def extractorCheckBuilderToCheck[C <: Check[R, XC], R, XC, X](extractorCheckBuilder: ExtractorCheckBuilder[C, R, XC, X]) = extractorCheckBuilder.find.exists.build
+ implicit def stringToStringExpression(string: String) = Expression[String](string)
+ implicit def value2Success[T](value: T): Validation[String, T] = value.success
+ implicit def value2Expression[T](value: T): Expression[T] = (session: Session) => value.success
+ implicit def checkBuilder2Check[C <: Check[R, XC], R, XC](checkBuilder: CheckBuilder[C, R, XC]) = checkBuilder.build
+ implicit def matcherCheckBuilder2CheckBuilder[C <: Check[R, XC], R, XC, X](matcherCheckBuilder: MatcherCheckBuilder[C, R, XC, X]) = matcherCheckBuilder.exists
+ implicit def matcherCheckBuilder2Check[C <: Check[R, XC], R, XC, X](matcherCheckBuilder: MatcherCheckBuilder[C, R, XC, X]) = matcherCheckBuilder.exists.build
+ implicit def extractorCheckBuilder2MatcherCheckBuilder[C <: Check[R, XC], R, XC, X](extractorCheckBuilder: ExtractorCheckBuilder[C, R, XC, X]) = extractorCheckBuilder.find
+ implicit def extractorCheckBuilder2CheckBuilder[C <: Check[R, XC], R, XC, X](extractorCheckBuilder: ExtractorCheckBuilder[C, R, XC, X]) = extractorCheckBuilder.find.exists
+ implicit def extractorCheckBuilder2Check[C <: Check[R, XC], R, XC, X](extractorCheckBuilder: ExtractorCheckBuilder[C, R, XC, X]) = extractorCheckBuilder.find.exists.build
+ implicit def stringMap2ExpressionMap(map: Map[String, Any]): Map[String, Expression[Any]] = map.map { entry =>
+ entry._2 match {
+ case string: String => entry._1 -> Expression[String](string)
+ case any => entry._1 -> any.success
+ }
+ }
def csv(fileName: String) = SeparatedValuesParser.csv(fileName, None)
def csv(fileName: String, escapeChar: String) = SeparatedValuesParser.csv(fileName, Some(escapeChar))
@@ -53,6 +63,7 @@ object Predef {
type Simulation = com.excilys.ebi.gatling.core.scenario.configuration.Simulation
type Feeder[T] = com.excilys.ebi.gatling.core.feeder.Feeder[T]
type Assertion = com.excilys.ebi.gatling.core.structure.Assertion
+ type Expression[T] = com.excilys.ebi.gatling.core.session.Expression[T]
def scenario(scenarioName: String): ScenarioBuilder = ScenarioBuilder.scenario(scenarioName)
val bootstrap = ChainBuilder.emptyChain
View
13 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/GroupAction.scala
@@ -16,18 +16,17 @@
package com.excilys.ebi.gatling.core.action
import com.excilys.ebi.gatling.core.result.writer.DataWriter
-import com.excilys.ebi.gatling.core.session.EvaluatableString
-import com.excilys.ebi.gatling.core.session.Session
+import com.excilys.ebi.gatling.core.session.{ Expression, Session }
import akka.actor.ActorRef
+import scalaz._
-class GroupAction(groupName: EvaluatableString, event: String, val next: ActorRef) extends Action {
+class GroupAction(groupName: Expression[String], event: String, val next: ActorRef) extends Action {
def execute(session: Session) {
- val resolvedGroupName = try {
- groupName(session)
- } catch {
- case e: Exception => error("Group name resolution crashed", e); "no-group-name"
+ val resolvedGroupName = groupName(session) match {
+ case Success(name) => name
+ case Failure(message) => error("Could not resolve group name: " + message); "no-group-name"
}
DataWriter.group(session.scenarioName, resolvedGroupName, session.userId, event)
View
10 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/TryMaxAction.scala
@@ -18,14 +18,12 @@ package com.excilys.ebi.gatling.core.action
import com.excilys.ebi.gatling.core.session.Session
import akka.actor.ActorRef
+import grizzled.slf4j.Logging
-object TryMaxAction {
+object TryMaxAction extends Logging {
def apply(times: Int, next: ActorRef, counterName: String): WhileAction = {
- def f(s: Session) = {
- val counterValue = s.getTypedAttribute[Int](counterName)
- counterValue == 0 || (s.isFailed && counterValue < times)
- }
- new WhileAction(f, next, counterName)
+ val continueCondition = (s: Session) => s.safeGetAs[Int](counterName).map(counterValue => counterValue == 0 || (s.isFailed && counterValue < times))
+ new WhileAction(continueCondition, next, counterName)
}
}
View
32 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/WhileAction.scala
@@ -15,10 +15,12 @@
*/
package com.excilys.ebi.gatling.core.action
-import com.excilys.ebi.gatling.core.session.Session
+import com.excilys.ebi.gatling.core.session.{ Expression, Session }
import com.excilys.ebi.gatling.core.session.handler.{ CounterBasedIterationHandler, TimerBasedIterationHandler }
import akka.actor.ActorRef
+import scalaz._
+import scalaz.Scalaz._
/**
* Action in charge of controlling a while loop execution.
@@ -28,7 +30,7 @@ import akka.actor.ActorRef
* @param next the chain executed if testFunction evaluates to false
* @param counterName the name of the counter for this loop
*/
-class WhileAction(condition: Session => Boolean, val next: ActorRef, val counterName: String) extends Action with TimerBasedIterationHandler with CounterBasedIterationHandler with Bypass {
+class WhileAction(condition: Expression[Boolean], val next: ActorRef, val counterName: String) extends Action with TimerBasedIterationHandler with CounterBasedIterationHandler with Bypass {
var loopNextAction: ActorRef = _
@@ -52,17 +54,21 @@ class WhileAction(condition: Session => Boolean, val next: ActorRef, val counter
val sessionWithTimerIncremented = increment(init(session))
- val evaluatedCondition = try {
- condition(sessionWithTimerIncremented)
- } catch {
- case e: Exception =>
- warn("Condition evaluation crashed, exiting loop", e)
- false
- }
+ // as WhileAction is not supervised, there's no one to restore its state (loopNextAction) on crash, so we try to avoid it
+ val evaluatedCondition =
+ try condition(sessionWithTimerIncremented)
+ catch {
+ case e: Exception =>
+ error("Loop condition evaluation crashed", e)
+ ("Loop condition evaluation crashed: " + e.getMessage).failure
+ }
- if (evaluatedCondition)
- loopNextAction ! sessionWithTimerIncremented
- else
- next ! expire(session)
+ evaluatedCondition match {
+ case Success(true) => loopNextAction ! sessionWithTimerIncremented
+ case Success(false) => next ! expire(session)
+ case Failure(message) =>
+ error("Error, exiting loop " + message)
+ next ! expire(session)
+ }
}
}
View
8 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/builder/GroupActionBuilder.scala
@@ -18,18 +18,18 @@ package com.excilys.ebi.gatling.core.action.builder
import com.excilys.ebi.gatling.core.action.{ GroupAction, system }
import com.excilys.ebi.gatling.core.config.ProtocolConfigurationRegistry
import com.excilys.ebi.gatling.core.result.message.RecordEvent.{ END, START }
-import com.excilys.ebi.gatling.core.session.EvaluatableString
+import com.excilys.ebi.gatling.core.session.Expression
import akka.actor.{ ActorRef, Props }
object GroupActionBuilder {
- def start(groupName: EvaluatableString) = new GroupActionBuilder(groupName, START, null)
+ def start(groupName: Expression[String]) = new GroupActionBuilder(groupName, START, null)
- def end(groupName: EvaluatableString) = new GroupActionBuilder(groupName, END, null)
+ def end(groupName: Expression[String]) = new GroupActionBuilder(groupName, END, null)
}
-class GroupActionBuilder(groupName: EvaluatableString, event: String, next: ActorRef) extends ActionBuilder {
+class GroupActionBuilder(groupName: Expression[String], event: String, next: ActorRef) extends ActionBuilder {
def withNext(next: ActorRef) = new GroupActionBuilder(groupName, event, next)
View
8 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/action/builder/WhileActionBuilder.scala
@@ -15,18 +15,16 @@
*/
package com.excilys.ebi.gatling.core.action.builder
-import java.util.UUID.randomUUID
-
import com.excilys.ebi.gatling.core.action.{ WhileAction, system }
import com.excilys.ebi.gatling.core.config.ProtocolConfigurationRegistry
-import com.excilys.ebi.gatling.core.session.Session
+import com.excilys.ebi.gatling.core.session.Expression
import com.excilys.ebi.gatling.core.structure.ChainBuilder
import akka.actor.{ ActorRef, Props }
object WhileActionBuilder {
- def apply(condition: Session => Boolean, loopNext: ChainBuilder, counterName: String) = new WhileActionBuilder(condition, loopNext, counterName, null)
+ def apply(condition: Expression[Boolean], loopNext: ChainBuilder, counterName: String) = new WhileActionBuilder(condition, loopNext, counterName, null)
}
/**
@@ -37,7 +35,7 @@ object WhileActionBuilder {
* @param loopNext chain that will be executed if condition evaluates to true
* @param next action that will be executed if condition evaluates to false
*/
-class WhileActionBuilder(condition: Session => Boolean, loopNext: ChainBuilder, counterName: String, next: ActorRef) extends ActionBuilder {
+class WhileActionBuilder(condition: Expression[Boolean], loopNext: ChainBuilder, counterName: String, next: ActorRef) extends ActionBuilder {
def withNext(next: ActorRef) = new WhileActionBuilder(condition, loopNext, counterName, next)
View
39 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/Check.scala
@@ -18,7 +18,10 @@ package com.excilys.ebi.gatling.core.check
import scala.annotation.tailrec
import com.excilys.ebi.gatling.core.check.CheckContext.useCheckContext
-import com.excilys.ebi.gatling.core.session.Session
+import com.excilys.ebi.gatling.core.session.{ Expression, Session }
+
+import scalaz._
+import Scalaz._
object Check {
@@ -30,24 +33,18 @@ object Check {
* @param checks the checks to be applied
* @return the result of the checks: Success or the first encountered Failure
*/
- def applyChecks[R](session: Session, response: R, checks: List[Check[R, _]]): (Session, CheckResult) = {
+ def applyChecks[R](session: Session, response: R, checks: List[Check[R, _]]): Validation[String, Session] = {
@tailrec
- def applyChecksRec(session: Session, checks: List[Check[R, _]], previousCheckResult: CheckResult): (Session, CheckResult) = checks match {
- case Nil =>
- (session, previousCheckResult)
-
+ def applyChecksRec(checks: List[Check[R, _]], validation: Validation[String, Session]): Validation[String, Session] = checks match {
+ case Nil => validation
case check :: otherChecks =>
- val (newSession, checkResult) = check(response, session)
-
- checkResult match {
- case failure @ Failure(_) => (newSession.setFailed, failure)
- case success @ Success(extractedValue) => applyChecksRec(newSession, otherChecks, success)
- }
+ val newValidation = validation.flatMap(check.apply(response))
+ applyChecksRec(otherChecks, newValidation)
}
useCheckContext {
- applyChecksRec(session, checks, Success(None))
+ applyChecksRec(checks, session.success)
}
}
}
@@ -58,19 +55,11 @@ object Check {
* @param expression the function that returns the expression representing what the check should look for
* @param matcher the matcher that will try to match the result of the extraction
* @param saveAs the session attribute that will be used to store the extracted value if the checks are successful
- * @param strategy the strategy used to perform the Check
*/
-class Check[R, XC](val expression: Session => XC, matcher: Matcher[R, XC], saveAs: Option[String]) {
-
- def apply(response: R, session: Session): (Session, CheckResult) = matcher(expression, session, response) match {
- case success @ Success(extractedValue) =>
- val newSessionWithSaved = for {
- extractedValue <- extractedValue
- saveAs <- saveAs
- } yield session.setAttribute(saveAs, extractedValue)
-
- (newSessionWithSaved.getOrElse(session), success)
+class Check[R, XC](val expression: Expression[XC], matcher: Matcher[R, XC], saveAs: Option[String]) {
- case failure => (session, failure)
+ def apply(response: R)(session: Session): Validation[String, Session] = {
+ val validation = expression(session).flatMap(matcher(response, session, _))
+ validation.map { value => saveAs.map(session.set(_, value)).getOrElse(session) }
}
}
View
85 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/CheckBuilder.scala
@@ -16,6 +16,10 @@
package com.excilys.ebi.gatling.core.check
import com.excilys.ebi.gatling.core.session.Session
+import com.excilys.ebi.gatling.core.session.Expression
+
+import scalaz._
+import Scalaz._
/**
* A partial CheckBuilder
@@ -60,20 +64,20 @@ trait MultipleExtractorCheckBuilder[C <: Check[R, XC], R, XC, X] extends Extract
object MatcherCheckBuilder {
val existsStrategy = new MatchStrategy[Any] {
def apply(value: Option[Any], session: Session) = value match {
- case Some(extracted) if (!extracted.isInstanceOf[Seq[_]] || !extracted.asInstanceOf[Seq[_]].isEmpty) => Success(value)
- case _ => Failure("Check 'exists' failed, found " + value)
+ case Some(extracted) => value.success
+ case _ => ("Check 'exists' failed, found " + value).failure
}
}
val notExistsStrategy = new MatchStrategy[Any] {
def apply(value: Option[Any], session: Session) = value match {
- case Some(extracted) if (!extracted.isInstanceOf[Seq[_]] || extracted.asInstanceOf[Seq[_]].isEmpty) => Failure("Check 'notExists' failed, found " + extracted)
- case _ => Success(value)
+ case Some(extracted) if (!extracted.isInstanceOf[Seq[_]] || extracted.asInstanceOf[Seq[_]].isEmpty) => ("Check 'notExists' failed, found " + extracted).failure
+ case _ => value.success
}
}
val whateverStrategy = new MatchStrategy[Any] {
- def apply(value: Option[Any], session: Session) = Success(value)
+ def apply(value: Option[Any], session: Session) = value.success
}
}
@@ -103,10 +107,11 @@ class MatcherCheckBuilder[C <: Check[R, XC], R, XC, X](checkBuilderFactory: Chec
def matchWith(strategy: MatchStrategy[X]) = {
val matcher = new Matcher[R, XC] {
- def apply(expression: Session => XC, session: Session, response: R) = {
- val evaluatedExpression = expression(session)
+
+ def apply(response: R, session: Session, expression: XC): Validation[String, Any] = {
+
val extractor = extractorFactory(response)
- val extractedValue = extractor(evaluatedExpression)
+ val extractedValue = extractor(expression)
strategy(extractedValue, session)
}
}
@@ -118,24 +123,37 @@ class MatcherCheckBuilder[C <: Check[R, XC], R, XC, X](checkBuilderFactory: Chec
* @param expected the expected value
* @return a partial CheckBuilder with a "is equal to" MatchStrategy
*/
- def is(expected: Session => X) = matchWith(new MatchStrategy[X] {
- def apply(value: Option[X], session: Session) = value.map { extracted =>
- val expectedValue = expected(session)
- if (extracted == expectedValue) Success(value)
- else Failure("Check 'is' failed, found " + extracted + " but expected " + expectedValue)
-
- }.getOrElse(Failure("Check 'is' failed, found nothing"))
+ def is(expected: Expression[X]) = matchWith(new MatchStrategy[X] {
+ def apply(value: Option[X], session: Session) = value match {
+ case Some(extracted) => expected(session).flatMap { expectedValue =>
+ if (expectedValue == extracted) value.success
+ else ("Check 'is' failed, found " + extracted + " but expected " + expectedValue).failure
+ }
+ case None => "Check 'is' failed, found nothing".failure
+ }
})
- def lessThan(expected: Session => X) = matchWith(new MatchStrategy[X] {
+ /**
+ * @param expected the expected value
+ * @return a partial CheckBuilder with a "is different from" MatchStrategy
+ */
+ def not(expected: Expression[X]) = matchWith(new MatchStrategy[X] {
+ def apply(value: Option[X], session: Session) = value match {
+ case Some(extracted) => expected(session).flatMap { expectedValue =>
+ if (expectedValue != extracted) value.success
+ else ("Check 'not' failed, found " + extracted + " but expected different from " + expectedValue).failure
+ }
+ case None => value.success
+ }
+ })
- def compare(expected: X, extracted: X, ok: Boolean) =
- if (ok) Success(Some(extracted))
- else Failure("Check 'lessThan' failed, found " + extracted + " but expected " + expected)
+ def lessThan(expected: Expression[X]) = matchWith(new MatchStrategy[X] {
- def apply(value: Option[X], session: Session) = value.map { extracted =>
- val expectedValue = expected(session)
+ def compare(expected: Any, extracted: X, ok: Boolean) =
+ if (ok) Some(extracted).success
+ else ("Check 'lessThan' failed, found " + extracted + " but expected " + expected).failure
+ def compareByType(extracted: X)(expectedValue: Any) = {
if (extracted.isInstanceOf[Long] & expectedValue.isInstanceOf[Long]) {
compare(expectedValue, extracted, extracted.asInstanceOf[Long] <= expectedValue.asInstanceOf[Long])
@@ -149,22 +167,14 @@ class MatcherCheckBuilder[C <: Check[R, XC], R, XC, X](checkBuilderFactory: Chec
compare(expectedValue, extracted, extracted.asInstanceOf[Float] <= expectedValue.asInstanceOf[Float])
} else
- Failure("Check 'lessThan' failed trying to compare thing that are not numbers of the same type, found " + extracted + " but expected " + expectedValue)
-
- }.getOrElse(Failure("Check 'lessThan' failed, found nothing"))
- })
+ ("Check 'lessThan' failed trying to compare thing that are not numbers of the same type, found " + extracted + " but expected " + expectedValue).failure
+ }
- /**
- * @param expected the expected value
- * @return a partial CheckBuilder with a "is different from" MatchStrategy
- */
- def not(expected: Session => X) = matchWith(new MatchStrategy[X] {
def apply(value: Option[X], session: Session) = value.map { extracted =>
val expectedValue = expected(session)
- if (extracted != expectedValue) Success(value)
- else Failure("Check 'not' failed, found " + extracted + " but expected different from " + expectedValue)
+ expectedValue.flatMap(compareByType(extracted))
- }.getOrElse(Success(value))
+ }.getOrElse("Check 'lessThan' failed, found nothing".failure)
})
/**
@@ -181,11 +191,12 @@ class MatcherCheckBuilder[C <: Check[R, XC], R, XC, X](checkBuilderFactory: Chec
* @param expected the expected sequence
* @return a partial CheckBuilder with a "belongs to the sequence" MatchStrategy
*/
- def in(expected: Session => Seq[X]) = matchWith(new MatchStrategy[X] {
+ def in(expected: Expression[Seq[X]]) = matchWith(new MatchStrategy[X] {
def apply(value: Option[X], session: Session) = value.map { extracted =>
- val expectedValue = expected(session)
- if (expectedValue.contains(extracted)) Success(value)
- else Failure("Check 'in' failed, found " + extracted + " but expected " + expectedValue)
+ expected(session).flatMap { expectedValue=>
+ if (expectedValue.contains(extracted)) value.success
+ else ("Check 'in' failed, found " + extracted + " but expected " + expectedValue).failure
+ }
}.getOrElse(Failure("Check 'in' failed, found nothing"))
})
View
24 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/CheckResult.scala
@@ -1,24 +0,0 @@
-/**
- * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.excilys.ebi.gatling.core.check
-
-/**
- * The outcome of a check
- */
-sealed trait CheckResult
-
-case class Success(extractedValue: Option[_]) extends CheckResult
-case class Failure(errorMessage: String) extends CheckResult
View
9 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/check/package.scala
@@ -15,8 +15,11 @@
*/
package com.excilys.ebi.gatling.core
+import com.excilys.ebi.gatling.core.check.Check
import com.excilys.ebi.gatling.core.session.Session
+import scalaz._
+
package object check {
/**
@@ -32,13 +35,13 @@ package object check {
/**
* A strategy for matching an extracted value
*/
- type MatchStrategy[X] = (Option[X], Session) => CheckResult
+ type MatchStrategy[X] = (Option[X], Session) => Validation[String, Option[Any]]
/**
* A function to be applied on an extracted value to produce a CheckResult
*/
- type Matcher[R, XC] = (Session => XC, Session, R) => CheckResult
-
+ type Matcher[R, XC] = (R, Session, XC) => Validation[String, Any]
+
/**
* A function for production a complete CheckBuilder
*/
View
2  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/message/DataWriterMessage.scala
@@ -59,7 +59,7 @@ case class RequestRecord(
requestSendingEndDate: Long,
responseReceivingStartDate: Long,
executionEndDate: Long,
- requestStatus: RequestStatus.RequestStatus,
+ requestStatus: RequestStatus,
requestMessage: Option[String] = None,
extraInfo: List[String] = Nil) extends DataWriterMessage {
val recordType = ACTION
View
10 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/message/RequestStatus.scala
@@ -15,10 +15,6 @@
*/
package com.excilys.ebi.gatling.core.result.message
-/**
- * This Enumeration lists the possible states of actions' results
- */
-object RequestStatus extends Enumeration {
- type RequestStatus = Value
- val OK, KO = Value
-}
+sealed trait RequestStatus
+case object OK extends RequestStatus
+case object KO extends RequestStatus
View
2  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/reader/ChartRequestRecord.scala
@@ -17,7 +17,7 @@ package com.excilys.ebi.gatling.core.result.reader
import org.joda.time.DateTime
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.RequestStatus
+import com.excilys.ebi.gatling.core.result.message.RequestStatus
import grizzled.slf4j.Logging
View
4 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/reader/DataReader.scala
@@ -18,7 +18,7 @@ package com.excilys.ebi.gatling.core.result.reader
import com.excilys.ebi.gatling.core.config.GatlingConfiguration.configuration
import com.excilys.ebi.gatling.core.result.Group
import com.excilys.ebi.gatling.core.result.message.{ RequestStatus, RunRecord }
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.RequestStatus
+import com.excilys.ebi.gatling.core.result.message.RequestStatus
object DataReader {
val NO_PLOT_MAGIC_VALUE = -1
@@ -56,5 +56,5 @@ abstract class DataReader(runUuid: String) {
def latencyGroupByExecutionStartDate(status: RequestStatus, requestName: Option[String] = None, group: Option[Group] = None): Seq[(Int, (Int, Int))]
- def responseTimeAgainstGlobalNumberOfRequestsPerSec(status: RequestStatus.RequestStatus, requestName: Option[String] = None, group: Option[Group] = None): Seq[(Int, Int)]
+ def responseTimeAgainstGlobalNumberOfRequestsPerSec(status: RequestStatus, requestName: Option[String] = None, group: Option[Group] = None): Seq[(Int, Int)]
}
View
6 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/writer/ConsoleDataWriter.scala
@@ -21,11 +21,9 @@ import java.util.{ HashMap => JHashMap }
import scala.collection.mutable.{ HashMap, LinkedHashMap }
import com.excilys.ebi.gatling.core.result.{ Group, RequestPath }
-import com.excilys.ebi.gatling.core.result.message.{ RunRecord, ScenarioRecord, ShortScenarioDescription }
-import com.excilys.ebi.gatling.core.result.message.GroupRecord
+import com.excilys.ebi.gatling.core.result.message.{ GroupRecord, KO, OK }
+import com.excilys.ebi.gatling.core.result.message.{ RequestRecord, RunRecord, ScenarioRecord, ShortScenarioDescription }
import com.excilys.ebi.gatling.core.result.message.RecordEvent.{ END, START }
-import com.excilys.ebi.gatling.core.result.message.RequestRecord
-import com.excilys.ebi.gatling.core.result.message.RequestStatus.{ KO, OK }
import grizzled.slf4j.Logging
View
2  gatling-core/src/main/scala/com/excilys/ebi/gatling/core/result/writer/DataWriter.scala
@@ -59,7 +59,7 @@ object DataWriter {
requestSendingEndDate: Long,
responseReceivingStartDate: Long,
executionEndDate: Long,
- requestResult: RequestStatus.RequestStatus,
+ requestResult: RequestStatus,
requestMessage: Option[String] = None,
extraInfo: List[String] = Nil) = {
View
83 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/ELParser.scala
@@ -1,83 +0,0 @@
-/**
- * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.excilys.ebi.gatling.core.session
-
-import com.excilys.ebi.gatling.core.util.NumberHelper.isNumeric
-
-import grizzled.slf4j.Logging
-
-object ELParser extends Logging {
-
- val elPattern = """\$\{(.+?)\}""".r
- val elSizePattern = """(.+?)\.size\(\)""".r
- val elOccurrencePattern = """(.+?)\((.+)\)""".r
-
- def parseEL(elString: String): EvaluatableString = {
-
- def parseStaticParts: Array[String] = elPattern.pattern.split(elString, -1)
-
- def parseDynamicParts: Seq[Session => Any] = elPattern
- .findAllIn(elString)
- .matchData
- .map {
- _.group(1) match {
- case elOccurrencePattern(key, occurrence) => {
- val occurrenceFunction =
- if (isNumeric(occurrence)) (session: Session) => Some(occurrence.toInt)
- else (session: Session) => session.getAttributeAsOption(occurrence)
-
- (session: Session) =>
- (for {
- resolvedOccurrence <- occurrenceFunction(session)
- attr <- session.getAttributeAsOption[Any](key)
- if (attr.isInstanceOf[Seq[_]])
- seq = attr.asInstanceOf[Seq[_]]
- if seq.isDefinedAt(resolvedOccurrence)
- } yield seq(resolvedOccurrence)).getOrElse { warn("Couldn't resolve EL " + elString); "" }
- }
- case elSizePattern(key) => {
- (session: Session) =>
- (for {
- attr <- session.getAttributeAsOption[Any](key)
- if (attr.isInstanceOf[Seq[_]])
- seq = attr.asInstanceOf[Seq[_]]
- } yield seq.size).getOrElse { warn("Couldn't resolve EL " + elString); 0 }
-
- }
- case key => (session: Session) => session.getAttributeAsOption[Any](key).getOrElse { warn("Couldn't resolve EL " + elString); "" }
- }
- }.toSeq
-
- val dynamicParts = parseDynamicParts
-
- if (dynamicParts.isEmpty) {
- // no interpolation
- (session: Session) => elString
-
- } else {
- val staticParts = parseStaticParts
-
- val functions = dynamicParts.zip(staticParts)
-
- (session: Session) => functions
- .foldLeft(new StringBuilder) { (buffer, function) =>
- val (dynamicPart, staticPart) = function
- buffer.append(staticPart).append(dynamicPart(session))
- }.append(staticParts.last)
- .toString
- }
- }
-}
View
104 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/Expression.scala
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2011-2012 eBusiness Information, Groupe Excilys (www.excilys.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.excilys.ebi.gatling.core.session
+
+import com.excilys.ebi.gatling.core.util.TypeHelper
+import grizzled.slf4j.Logging
+import scalaz._
+import Scalaz._
+
+trait Part[+T] {
+ def resolve(session: Session): Validation[String, T]
+}
+
+case class StaticPart(string: String) extends Part[String] {
+ def resolve(session: Session): Validation[String, String] = string.success
+}
+
+case class AttributePart(name: String) extends Part[Any] {
+ def resolve(session: Session): Validation[String, Any] = session.safeGetAs[Any](name)
+}
+
+case class SeqSizePart(name: String) extends Part[Int] {
+ def resolve(session: Session): Validation[String, Int] = session.safeGetAs[Seq[_]](name).map(_.size)
+}
+
+case class SeqElementPart(name: String, index: String) extends Part[Any] {
+ def resolve(session: Session): Validation[String, Any] = {
+
+ def seqElementPart(index: Int): Validation[String, Any] = session.safeGetAs[Seq[_]](name).flatMap(_.lift(index).toSuccess(undefinedSeqIndexMessage(name, index)))
+
+ try {
+ val intIndex = index.toInt
+ seqElementPart(intIndex)
+ } catch {
+ case e: NumberFormatException => session.safeGetAs[Int](index).flatMap(seqElementPart(_))
+ }
+ }
+}
+
+sealed abstract class ELParserException(message: String) extends Exception(message)
+class ELMissingAttributeName(el: String) extends ELParserException("An attribute name is missing in this expression : " + el)
+class ELNestedAttributeDefinition(el: String) extends ELParserException("There is a nested attribute definition in this expression : " + el)
+
+object ELParser extends Logging {
+
+ val elPattern = """\$\{(.*?)\}""".r
+ val elSeqSizePattern = """(.+?)\.size""".r
+ val elSeqElementPattern = """(.+?)\((.+)\)""".r
+
+ def apply[T: ClassManifest](string: String): List[Part[Any]] = {
+
+ val staticParts = elPattern.split(string).map(StaticPart(_)).toList
+
+ val dynamicParts = elPattern
+ .findAllIn(string)
+ .matchData
+ .map {
+ _.group(1) match {
+ case elSeqElementPattern(key, occurrence) => SeqElementPart(key, occurrence)
+ case elSeqSizePattern(key) => SeqSizePart(key)
+ case key if key contains "${" => throw new ELNestedAttributeDefinition(string)
+ case key if key.isEmpty => throw new ELMissingAttributeName(string)
+ case key => AttributePart(key)
+ }
+ }
+ .toList
+
+ val indexedStaticParts = staticParts.zipWithIndex.map { case (part, index) => (part, index * 2) }.filter { case (part, index) => !part.string.isEmpty }
+ val indexedDynamicParts = dynamicParts.zipWithIndex.map { case (part, index) => (part, index * 2 + 1) }
+
+ (indexedStaticParts ::: indexedDynamicParts).sortBy(_._2).map(_._1)
+ }
+}
+
+object Expression {
+
+ def apply[T: ClassManifest](elString: String): Expression[T] = {
+
+ ELParser(elString) match {
+ case List(StaticPart(string)) => (session: Session) => TypeHelper.as[T](string)
+ case List(dynamicPart) => dynamicPart.resolve _ andThen (_.flatMap(TypeHelper.as[T](_)))
+ case parts => (session: Session) => {
+ val resolvedString = parts.map(_.resolve(session))
+ .sequence[({ type l[a] = Validation[String, a] })#l, Any]
+ .map(_.mkString)
+
+ resolvedString.flatMap(TypeHelper.as[T](_))
+ }
+ }
+ }
+}
View
130 gatling-core/src/main/scala/com/excilys/ebi/gatling/core/session/Session.scala
@@ -15,7 +15,11 @@
*/
package com.excilys.ebi.gatling.core.session
+import com.excilys.ebi.gatling.core.util.TypeHelper
+