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

io.gatling.core.check.CheckBuilder.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.concurrent.ThreadLocalRandom

import io.gatling.commons.util.{ Arrays, Equality }
import io.gatling.commons.validation._
import io.gatling.core.session._

trait CheckBuilder[T, P] {
  def build[C <: Check[R], R](materializer: CheckMaterializer[T, C, R, P]): C
}

object CheckBuilder {
  // T: Check type, only used for CheckMaterializer Type Class
  // P: Prepared
  // X: Extracted
  trait Find[T, P, X] {
    def find: Validate[T, P, X]
  }

  object Find {
    class Default[T, P, X](extractor: Expression[Extractor[P, X]], displayActualValue: Boolean) extends Find[T, P, X] {
      override def find: Validate[T, P, X] = Validate.Default(extractor, displayActualValue)
    }
  }

  trait MultipleFind[T, P, X] extends Find[T, P, X] {
    override def find: Validate[T, P, X]
    def find(occurrence: Int): Validate[T, P, X]
    def findAll: Validate[T, P, Seq[X]]
    def findRandom: Validate[T, P, X]
    @SuppressWarnings(Array("org.wartremover.warts.DefaultArguments"))
    def findRandom(num: Int, failIfLess: Boolean = false): Validate[T, P, Seq[X]]
    def count: Validate[T, P, Int]
  }

  object MultipleFind {
    abstract class Default[T, P, X](displayActualValue: Boolean) extends MultipleFind[T, P, X] {
      protected def findExtractor(occurrence: Int): Expression[Extractor[P, X]]

      protected def findAllExtractor: Expression[Extractor[P, Seq[X]]]

      private def findRandomExtractor: Expression[Extractor[P, X]] = findAllExtractor.map { fae =>
        new Extractor[P, X] {
          override def name: String = fae.name
          override def arity: String = "findRandom"

          @SuppressWarnings(Array("org.wartremover.warts.SeqApply"))
          override def apply(prepared: P): Validation[Option[X]] =
            fae(prepared)
              .map(_.collect { case seq if seq.nonEmpty => seq(ThreadLocalRandom.current.nextInt(seq.size)) })
        }
      }

      private def findManyRandomExtractor(num: Int, failIfLess: Boolean): Expression[Extractor[P, Seq[X]]] = findAllExtractor.map { fae =>
        new Extractor[P, Seq[X]] {
          override def name: String = fae.name
          override def arity: String = s"findRandom($num, $failIfLess)"

          @SuppressWarnings(Array("org.wartremover.warts.SeqApply"))
          override def apply(prepared: P): Validation[Option[Seq[X]]] =
            fae(prepared)
              .flatMap {
                case Some(seq) =>
                  if (failIfLess && seq.sizeIs < num) {
                    s"Failed to collect $num matches".failure
                  } else if (seq.isEmpty) {
                    Validation.NoneSuccess
                  } else {
                    val randomSeq =
                      if (seq.sizeIs <= num) {
                        seq
                      } else {
                        val shuffledIndices = Arrays.shuffle(seq.indices.toArray)
                        for (i <- 0 until num) yield seq(shuffledIndices(i))
                      }

                    Some(randomSeq).success
                  }

                case _ => Validation.NoneSuccess
              }
        }
      }

      protected def countExtractor: Expression[Extractor[P, Int]]

      override def find: Validate[T, P, X] = find(0)

      override def find(occurrence: Int): Validate[T, P, X] = Validate.Default(findExtractor(occurrence), displayActualValue)

      override def findAll: Validate[T, P, Seq[X]] = Validate.Default(findAllExtractor, displayActualValue)

      override def findRandom: Validate[T, P, X] = Validate.Default(findRandomExtractor, displayActualValue)

      override def findRandom(num: Int, failIfLess: Boolean): Validate[T, P, Seq[X]] =
        Validate.Default(findManyRandomExtractor(num, failIfLess), displayActualValue)

      override def count: Validate[T, P, Int] = Validate.Default(countExtractor, displayActualValue)
    }
  }

  trait Validate[T, P, X] {
    private[gatling] def transform0[X2](
        extractedF: Validation[Option[X]] => Validation[Option[X2]],
        arityF: String => String
    ): Validate[T, P, X2]
    def transform[X2](transformation: X => X2): Validate[T, P, X2]
    def transformWithSession[X2](transformation: (X, Session) => X2): Validate[T, P, X2]
    def transformOption[X2](transformation: Option[X] => Validation[Option[X2]]): Validate[T, P, X2]
    def transformOptionWithSession[X2](transformation: (Option[X], Session) => Validation[Option[X2]]): Validate[T, P, X2]
    def withDefault(defaultValue: Expression[X]): Validate[T, P, X]
    def validate(validator: Expression[Validator[X]]): Final[T, P]
    def validate(opName: String, validator: (Option[X], Session) => Validation[Option[X]]): Final[T, P]
    def is(expected: Expression[X])(implicit equality: Equality[X]): Final[T, P]
    def isNull: Final[T, P]
    def not(expected: Expression[X])(implicit equality: Equality[X]): Final[T, P]
    def notNull: Final[T, P]
    def in(expected: X*): Final[T, P]
    def in(expected: Expression[Seq[X]]): Final[T, P]
    def exists: Final[T, P]
    def notExists: Final[T, P]
    def optional: Final[T, P]
    def lt(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P]
    def lte(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P]
    def gt(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P]
    def gte(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P]
  }

  object Validate {
    private val TransformErrorMapper: String => String = "transform crashed: " + _
    private val TransformOptionErrorMapper: String => String = "transformOption crashed: " + _
    private val TransformArity: String => String = _ + ".transform"
    private val TransformOptionArity: String => String = _ + ".transformOption"

    final case class Default[T, P, X](extractor: Expression[Extractor[P, X]], displayActualValue: Boolean) extends Validate[T, P, X] {
      private def mapExtractor[X2](
          extractedF: Validation[Option[X]] => Validation[Option[X2]],
          arityF: String => String
      )(extractor: Extractor[P, X]) =
        new Extractor[P, X2] {
          override def name: String = extractor.name
          override def arity: String = arityF(extractor.arity)
          override def apply(prepared: P): Validation[Option[X2]] = extractedF(extractor(prepared))
        }

      override private[gatling] def transform0[X2](
          extractedF: Validation[Option[X]] => Validation[Option[X2]],
          arityF: String => String
      ): Validate[T, P, X2] =
        copy(extractor = extractor.map(mapExtractor(extractedF, arityF)))

      private def transformWithSession0[X2](
          extractedF: (Session, Validation[Option[X]]) => Validation[Option[X2]],
          arityF: String => String
      ): Validate[T, P, X2] =
        copy(extractor =
          session =>
            extractor(session).map(
              mapExtractor(
                extractedF(session, _),
                arityF
              )
            )
        )

      override def transform[X2](transformation: X => X2): Validate[T, P, X2] =
        transform0(
          extracted =>
            safely(TransformErrorMapper) {
              extracted.map(_.map(transformation))
            },
          TransformArity
        )

      override def transformWithSession[X2](transformation: (X, Session) => X2): Validate[T, P, X2] =
        transformWithSession0(
          (session, extracted) =>
            safely(TransformErrorMapper) {
              extracted.map(_.map(transformation(_, session)))
            },
          TransformArity
        )

      override def transformOption[X2](transformation: Option[X] => Validation[Option[X2]]): Validate[T, P, X2] =
        transform0(
          extracted =>
            safely(TransformOptionErrorMapper) {
              extracted.flatMap(transformation)
            },
          TransformOptionArity
        )

      override def transformOptionWithSession[X2](transformation: (Option[X], Session) => Validation[Option[X2]]): Validate[T, P, X2] =
        transformWithSession0(
          (session, extracted) =>
            safely(TransformOptionErrorMapper) {
              extracted.flatMap(transformation(_, session))
            },
          TransformOptionArity
        )

      override def withDefault(defaultValue: Expression[X]): Validate[T, P, X] =
        transformOptionWithSession((actual, session) =>
          actual match {
            case None => defaultValue(session).map(Option(_))
            case _    => actual.success
          }
        )

      override def validate(validator: Expression[Validator[X]]): Final[T, P] =
        new Final.Default[T, P, X](this.extractor, validator, displayActualValue, None, None)

      override def validate(opName: String, validator: (Option[X], Session) => Validation[Option[X]]): Final[T, P] =
        validate(session =>
          new Validator[X] {
            override val name: String = opName
            override def apply(actual: Option[X], displayActualValue: Boolean): Validation[Option[X]] = validator(actual, session)
          }.success
        )

      override def is(expected: Expression[X])(implicit equality: Equality[X]): Final[T, P] =
        validate(expected.map(new Matcher.Is(_, equality)))
      override def isNull: Final[T, P] = validate(new Matcher.IsNull[X].expressionSuccess)
      override def not(expected: Expression[X])(implicit equality: Equality[X]): Final[T, P] =
        validate(expected.map(new Matcher.Not(_, equality)))
      override def notNull: Final[T, P] = validate(new Matcher.NotNull[X].expressionSuccess)
      override def in(expected: X*): Final[T, P] = validate(expected.expressionSuccess.map(new Matcher.In(_)))
      override def in(expected: Expression[Seq[X]]): Final[T, P] = validate(expected.map(new Matcher.In(_)))
      override def exists: Final[T, P] = validate(new Validator.Exists[X]().expressionSuccess)
      override def notExists: Final[T, P] = validate(new Validator.NotExists[X]().expressionSuccess)
      override def optional: Final[T, P] = validate(new Validator.Optional[X]().expressionSuccess)
      override def lt(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P] =
        validate(expected.map(new Matcher.Compare("lessThan", "less than", ordering.lt, _)))
      override def lte(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P] =
        validate(expected.map(new Matcher.Compare("lessThanOrEqual", "less than or equal to", ordering.lteq, _)))
      override def gt(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P] =
        validate(expected.map(new Matcher.Compare("greaterThan", "greater than", ordering.gt, _)))
      override def gte(expected: Expression[X])(implicit ordering: Ordering[X]): Final[T, P] =
        validate(expected.map(new Matcher.Compare("greaterThanOrEqual", "greater than or equal to", ordering.gteq, _)))
    }
  }

  trait Final[T, P] extends CheckBuilder[T, P] {
    def name(n: String): Final[T, P]
    def saveAs(key: String): Final[T, P]
  }

  object Final {
    final case class Default[T, P, X](
        extractor: Expression[Extractor[P, X]],
        validator: Expression[Validator[X]],
        displayActualValue: Boolean,
        customName: Option[String],
        saveAs: Option[String]
    ) extends Final[T, P] {
      override def name(n: String): Final[T, P] = copy(customName = Some(n))

      override def saveAs(key: String): Final[T, P] = copy(saveAs = Some(key))

      override def build[C <: Check[R], R](materializer: CheckMaterializer[T, C, R, P]): C =
        materializer.materialize(Check.Default(_, extractor, validator, displayActualValue, customName, None, saveAs))
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy