Skip to content

Behavior modification

Scenario modifiers do not define the primary behavior of a scenario. Instead, they constrain or modify the behavior of a scenario for the purposes of a particular test. Scenario modifiers are especially useful if you just want to group together related constraints or modifiers.

Scenario modifiers such as speed() are part of the domain model.

Scenario modifier application

Purpose

Constrain or modify the behavior of a scenario

Category

Scenario member

Syntax

[<label>: ]<scenario-modifier>(<params>)

Syntax parameters

<label>
Is an optional label for the invocation. If you have multiple invocations of the same modifier within the same scenario phase, labels are automatically created unless you specify them.
<scenario-modifier>
Is the name of the modifier you want to invoke.
<params>
Is a comma-separated list of parameters enclosed in parentheses. The parentheses are required.

Invocation parameters

For more information on invocation parameters, see Invocation parameters.

Description

You can invoke scenario modifiers in the following contexts:

  • Within a scenario declaration outside of the do behavioral definition.
  • Within the with: block of a scenario invocation.
  • Within the body of an in scenario modifier.
  • Within a scenario modifier declaration.

Example: in

The scenario modifier lane() is invoked within the in scenario modifier.

OSC2 code: behavior modification with 'in'
extend top.main:

    in a.phase_essence.vehicle_cut_in_at_essence.drive_cut_in_vehicle_at_essence with:
        lane(leftmost: true, at: start)

    do a: sut.vehicle_cut_in_and_slow_main()

See complete example.

Example: with

The scenario modifiers position() and speed() are invoked within the with: block of a drive() scenario invocation.

Note: this example uses labels (p1, p2) to distinguish the two position() modifiers.

OSC2 code: behavior modification in 'with' block
    do parallel(overlap:equal):
        sut.car.drive() with:
            s1: speed([50..120]kph)
        car1.drive() with:
            p1: position(distance: [5..100]m, behind: sut.car, at: start)
            p2: position(distance: [5..15]m, ahead_of: sut.car, at: end)

See complete example.

For more information on invocation parameters, see Invocation parameters.

in directive

Purpose

Modify the behavior of a nested scenario.

Category

Scenario modifier

Syntax

in <scenario-path> <with-block>

Syntax parameters

<scenario-path>
Is the path to the nested scenario instance whose behavior you want to modify.
<with-block>

Is a list of one or more keep() constraints or scenario modifiers, where the members are listed on separate lines as a block or on the same line as with: and separated by semi-colons. For example, the following two examples are the same:

in ci.ga.car1 with: speed([50..75]kph); lane(1)
in ci.ga.car1 with:
    speed([50..75]kph)
    lane(1)

Note: cover() definitions are not allowed in scenario modifiers, including in.

Expressions in <with-block> can refer to values in the calling context (where it was invoked). This is done by using outer in a path expression.

Description

All modifiers inserted via the in modifier are added to the original set of modifiers. Together they influence the behavior of the scenario. If there is any contradiction between them, then an error occurs.

For information on how to use labeling to access lower-level scenarios or movements, see Automatic label computation.

Example: in

In the following example, the speed and lane constraints apply to the car1.drive movement in the get_ahead phase of the sut.cut_in scenario.

OSC2 code: behavior modification with the 'in' directive
    in label(sut).label(get_ahead.car1) with:
        position(distance:[7..10]m, ahead_of: sut.car, at: end)
        lane(1)

    do sut.cut_in()

See complete example.

Example: outer

You can use the keyword outer to constrain a nested scenario from an enclosing scenario or test. In the following example, the constrained speed is defined in a drive_attributes struct that is instantiated in the extension to top.main.

Notes

  • In this example, no speed constraints are applied to car1.drive() in the nested scenario. This avoids the possibility of constraint contradictions when it is nested within different scenarios.
  • Because the invocations of the nested and enclosing scenarios are labeled explicitly as nested and enclosing, and car1.drive() is labeled as start_up, the scenario pathname for car1.drive() is enclosing.nested.start_up.
OSC2 code: behavior modification with 'in' and enclosing/nested
import "$FTX_BASIC/exe_platforms/sumo_ssp/config/sumo_config.osc"

extend test_config:
    set map = "$FTX_PACKAGES/maps/hooder.xodr"

struct drive_attributes:
    my_speed: speed with:
        keep(it <= 70kph)

scenario sut.nested_scenario:
    car1: vehicle

    do serial:
        start_up: car1.drive()

scenario sut.enclosing_scenario:
    my_car: vehicle

    do nested: sut.nested_scenario(car1: my_car)

extend top.main:
    drv_attr: drive_attributes

    in enclosing.nested.start_up with:
        speed(outer.drv_attr.my_speed)

    do enclosing: sut.enclosing_scenario()

See complete example.

override()

Purpose

Controls the execution of another scenario or modifier.

Category

Modifier, Action

Syntax

override([overridden: ] <path to modifier>, run_mode: [best_effort|freeze|disable])

override() can be invoked as a modifier under a scenario:

extend <scenario name>:
    override(<overridden>, run_mode: <run mode>)
    do <scenario path>() with:
        override(<overridden>, run_mode: <run mode>)

override() can also be invoked as an action in a native OSC context:

def <method name> is:
    ...
    override(<overridden>, run_mode: best_effort)
on <event expression>: 
    override(<overridden>, run_mode: best_effort)

Syntax parameters

<overridden>
Is the path to the modifiers that will be affected.
run_mode
Is the desired effect: best_effort, freeze or disable.

Description

The override() modifier controls another modifier. It can be applied both declaratively and procedurally. It supports three modes that are described below: best_effort, freeze, and disabled.

best_effort

The overridden modifier affects the plan (generation), but runtime deviations from the plan are not considered an error.

Typical use case: When several scenarios and modifiers run in parallel and control different actors, each of them can interfere with the ability of another to fulfill its purpose. When used with best_effort, the override() modifier specifies that it is acceptable for the modifier or scenario to fail due to such interference, and does not require scenario execution to fail.

In the following example, the pedestrian crossing prevents the SUT vehicle from completing the 'accelerate' scenario. override() with run_mode: best_effort allows the test to continue:

OSC2 code: override() with best_effort
scenario accelerate:
    v: vehicle

    do serial:
        drv: v.drive() with:
            s_spd: speed([40..45]kph, at: start)
            e_spd: speed([50..55]kph, at: end)

extend top.main:
    sut_v: sut_vehicle

    do parallel:
        acc: accelerate(sut_v, duration: [15..20]sec)
        pedestrian_crossing: sut.crossing_person(ref_drive: acc.drv, gen_drive_path_fraction: 95)

    override(acc.e_spd, run_mode: best_effort)

See complete example.

Limitations: override() with best_effort cannot be applied to global modifiers.

freeze

All event-based actions inside the modifier will not be executed while the modifier is frozen (i.e., any on or sample directives within the modifier, and any events defined using event expressions, will be frozen when using freeze).

If used in an override declaration (i.e., not inside native code), the target modifier will be frozen from the start of the context until the end of the test run (or until an override unfreeze is executed).

Typical use case: Disabling a checker during a certain phase of the test.

In the following example, the 'speed_checker' checker is frozen for the duration of the 'accelerate' phase, and unfrozen for the 'decelerate' phase:

OSC2 code: disabling a checker during a phase
extend top.main:
    sut_v: sut_vehicle
    checker overspeed_checker is speed_above(sut_v, 50kph)

    do serial:
        drive_slowly: sut_v.drive(duration: [5..10]sec) with:
            speed([35..45]kph, at: all)
        accelerate: sut_v.drive(duration: [5..10]sec) with:
            speed([60..70]kph, at: end)
        decelerate: sut_v.drive(duration: [5..10]sec) with:
            speed([35..45]kph, at: end)
        drive_slowly_again: sut_v.drive(duration: [5..10]sec) with:
            speed([35..45]kph, at: all)
        accelerate_again: sut_v.drive(duration: [5..10]sec) with:
            speed([60..70]kph, at: end)

    # Freeze overspeed_checker for the accelerate+decelerate phases.
    # The checker will be active during accelerate_again.

    on @accelerate.start:
        override(overspeed_checker, run_mode: freeze)

    on @decelerate.end:
        override(overspeed_checker, run_mode: unfreeze)

See complete example.

Limitations:

override() with freeze:

  • Cannot be applied to scenarios.

unfreeze

Undoes a freeze override: All event-based actions inside the modifier will be executed after the modifier is unfrozen.

Limitations:

override() with unfreeze:

- Cannot be used declaratively - only in be used in native code context (in methods and on).

- Cannot be applied to scenarios.

disable

The overridden modifier is completely removed and will not affect plan generation or runtime execution.

Typical use case: When you compose new scenarios from existing ones, sometimes you need to remove behavior specified by modifiers. When used with disable, the override() modifier lets you effectively remove the specified modifier from the scenario.

In the following example, the 'spd' speed() modifier invocation is completely removed:

OSC2 code: override() with disable
extend top.main:
    sut_v: sut_vehicle

    do drive1: sut_v.drive() with:
           spd: speed([10..20]kph)
           duration([10..20]sec)

# In another file:
extend top.main:

    override(spd, run_mode: disable)

    in drive1 with:
       speed([30..40]kph)

See complete example.

Limitations:

override() with disable:

  • Can only be applied to modifiers whose scope is contained in the scope of the override's modifier.
  • Cannot be applied to global modifiers.
  • Cannot be used in native code context (in methods and on).
  • Cannot be applied to scenarios.

Disabling modifiers is not supported when the override is in a conditional inheritance subtype of the original scenario.