All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gatling.core.check.Check.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/*
 * Copyright 2011-2024 GatlingCorp (https://gatling.io)
 *
 * 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 io.gatling.core.check

import java.{ util => ju }

import scala.annotation.tailrec

import io.gatling.commons.validation._
import io.gatling.core.session.{ Expression, Session }

object Check {
  type PreparedCache = ju.Map[Any, Any]

  def newPreparedCache: PreparedCache =
    new ju.HashMap(2)

  def check[R](response: R, session: Session, checks: List[Check[R]]): (Session, Option[Failure]) = {
    val preparedCache: PreparedCache =
      if (checks.sizeIs > 1) {
        newPreparedCache
      } else {
        null
      }

    check(response, session, checks, preparedCache)
  }

  def check[R](response: R, session: Session, checks: List[Check[R]], preparedCache: PreparedCache): (Session, Option[Failure]) = {
    @tailrec
    def checkRec(currentSession: Session, checks: List[Check[R]], failure: Option[Failure]): (Session, Option[Failure]) =
      checks match {
        case Nil => (currentSession, failure)

        case check :: tail =>
          check.check(response, currentSession, preparedCache) match {
            case Success(checkResult) =>
              val newSession = checkResult.update(currentSession)
              checkRec(newSession, tail, failure)

            case f: Failure =>
              checkRec(currentSession, tail, if (failure.isDefined) failure else Some(f))
          }
      }

    checkRec(session, checks, None)
  }

  abstract class ConditionalCheck[R](condition: Option[(R, Session) => Validation[Boolean]]) extends Check[R] {
    override def checkIf(condition: Expression[Boolean]): Check[R] = checkIf((_: R, session: Session) => condition(session))

    protected def check0(response: R, session: Session, preparedCache: Check.PreparedCache): Validation[CheckResult]

    override def check(response: R, session: Session, preparedCache: Check.PreparedCache): Validation[CheckResult] =
      condition match {
        case Some(cond) =>
          cond(response, session).flatMap { boolean =>
            if (boolean) {
              check0(response, session, preparedCache)
            } else {
              CheckResult.NoopCheckResultSuccess
            }
          }
        case _ => check0(response, session, preparedCache)
      }
  }

  final case class Simple[R](
      f: (R, Session, Check.PreparedCache) => Validation[CheckResult],
      condition: Option[(R, Session) => Validation[Boolean]]
  ) extends ConditionalCheck[R](condition) {
    override def checkIf(condition: (R, Session) => Validation[Boolean]): Check[R] = copy(condition = Some(condition))

    override protected def check0(response: R, session: Session, preparedCache: Check.PreparedCache): Validation[CheckResult] =
      f(response, session, preparedCache)
  }

  final case class Default[R, P, X](
      preparer: Preparer[R, P],
      extractorExpression: Expression[Extractor[P, X]],
      validatorExpression: Expression[Validator[X]],
      displayActualValue: Boolean,
      customName: Option[String],
      condition: Option[(R, Session) => Validation[Boolean]],
      saveAs: Option[String]
  ) extends ConditionalCheck[R](condition) {
    override def checkIf(condition: (R, Session) => Validation[Boolean]): Check[R] = copy(condition = Some(condition))

    private val unbuiltName: String = customName.getOrElse("Check")

    protected def check0(response: R, session: Session, preparedCache: Check.PreparedCache): Validation[CheckResult] = {
      def memoizedPrepared: Validation[P] =
        if (preparedCache == null) {
          preparer(response)
        } else {
          preparedCache.computeIfAbsent(preparer, _ => preparer(response)).asInstanceOf[Validation[P]]
        }

      def builtName(extractor: Extractor[P, X], validator: Validator[X]): String =
        customName.getOrElse(s"${extractor.name}.${extractor.arity}.${validator.name}")

      for {
        extractor <- extractorExpression(session).mapFailure(message => s"$unbuiltName extractor resolution crashed: $message")
        validator <- validatorExpression(session).mapFailure(message => s"$unbuiltName validator resolution crashed: $message")
        prepared <- memoizedPrepared.mapFailure(message => s"${builtName(extractor, validator)} preparation crashed: $message")
        actual <- extractor(prepared).mapFailure(message => s"${builtName(extractor, validator)} extraction crashed: $message")
        matched <- validator(actual, displayActualValue).mapFailure(message => s"${builtName(extractor, validator)}, $message")
      } yield new CheckResult(matched, saveAs)
    }
  }
}

trait Check[R] {
  def check(response: R, session: Session, preparedCache: Check.PreparedCache): Validation[CheckResult]
  def checkIf(condition: Expression[Boolean]): Check[R]
  def checkIf(condition: (R, Session) => Validation[Boolean]): Check[R]
}

object CheckResult {
  val NoopCheckResultSuccess: Validation[CheckResult] = new CheckResult(None, None).success
}

final case class CheckResult(extractedValue: Option[Any], saveAs: Option[String]) {
  def update(session: Session): Session = {
    val maybeUpdatedSession =
      for {
        s <- saveAs
        v <- extractedValue
      } yield session.set(s, v)
    maybeUpdatedSession.getOrElse(session)
  }
}

trait UntypedCheckIfMaker[C <: Check[_]] {
  def make(thenCheck: C, condition: Expression[Boolean]): C
}

trait TypedCheckIfMaker[R, C <: Check[R]] {
  def make(thenCheck: C, condition: (R, Session) => Validation[Boolean]): C
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy