Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
SpeedActionTest.cpp 26.23 KiB
/********************************************************************************
 * Copyright (c) 2021-2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
 * Copyright (c) 2023 Ansys, Inc.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 ********************************************************************************/

#include <MantleAPI/Test/test_utils.h>
#include <gtest/gtest.h>

#include "Conversion/OscToMantle/ConvertScenarioSpeedActionTarget.h"
#include "Storyboard/MotionControlAction/SpeedAction_impl.h"
#include "gmock/gmock.h"

using namespace mantle_api;
using namespace units::literals;

using testing::_;
using testing::Return;
using testing::SizeIs;

namespace OpenScenarioEngine::v1_2
{
class SpeedActionWithJerkLimitsTestFixture : public testing::Test
{
protected:
  void Init(const Vec3<units::velocity::meters_per_second_t>& initial_velocity,
            units::velocity::meters_per_second_t target_speed,
            units::acceleration::meters_per_second_squared_t expected_acceleration,
            std::optional<units::jerk::meters_per_second_cubed_t> max_acceleration_rate = std::nullopt,
            std::optional<units::jerk::meters_per_second_cubed_t> max_deceleration_rate = std::nullopt)
  {
    vehicle_properties_.performance = {
        .max_acceleration_rate = max_acceleration_rate.value_or(units::jerk::meters_per_second_cubed_t(std::numeric_limits<double>::infinity())),
        .max_deceleration_rate = max_deceleration_rate.value_or(units::jerk::meters_per_second_cubed_t(std::numeric_limits<double>::infinity()))};

    EXPECT_CALL(*mock_environment_, UpdateControlStrategies(_, _)).WillOnce(testing::SaveArg<1>(&control_strategies_));
    EXPECT_CALL(mock_vehicle_, GetPropertiesImpl()).WillOnce(Return(&vehicle_properties_));
    EXPECT_CALL(mock_vehicle_, GetVelocity()).WillOnce(Return(initial_velocity));

    const mantle_api::TransitionDynamics transition_dynamics{Dimension::kRate, Shape::kLinear, expected_acceleration()};
    const TransitionDynamics speed_action_dynamics{transition_dynamics, FollowingMode::kFollow};

    const SpeedActionBase::Values values{std::vector<std::string>{"Vehicle1"},
                                         speed_action_dynamics,
                                         [target_speed]() { return target_speed; }};
    const SpeedActionBase::Interfaces interfaces{mock_environment_};

    speed_action_ = std::make_unique<SpeedAction>(values, interfaces);
  }

  void SetAndCheckControlStrategies()
  {
    EXPECT_NO_THROW(speed_action_->SetControlStrategy());
    EXPECT_THAT(control_strategies_, SizeIs(1));
    EXPECT_EQ(control_strategies_.front()->type, ControlStrategyType::kFollowVelocitySpline);
  }

  std::unique_ptr<SpeedAction> speed_action_{nullptr};
  std::shared_ptr<MockEnvironment> mock_environment_{std::make_shared<MockEnvironment>()};
  MockVehicle& mock_vehicle_{dynamic_cast<MockVehicle&>(mock_environment_->GetEntityRepository().GetHost())};
  VehicleProperties vehicle_properties_{};
  std::vector<std::shared_ptr<ControlStrategy>> control_strategies_{};
};

TEST(SpeedAction, GivenMatchingControlStrategyGoalIsReached_ReturnTrue)
{
  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  ON_CALL(*mockEnvironment,
          HasControlStrategyGoalBeenReached(_, mantle_api::ControlStrategyType::kFollowVelocitySpline))
      .WillByDefault(Return(true));
  TransitionDynamics speedActionDynamics;
  speedActionDynamics.transitionDynamics.shape = mantle_api::Shape::kLinear;

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           []() -> units::velocity::meters_per_second_t { return 0_mps; }},
                          {mockEnvironment});

  ASSERT_TRUE(speedAction.HasControlStrategyGoalBeenReached("Vehicle1"));
}

TEST(SpeedAction, GivenUnmatchingControlStrategyGoalIsReached_ReturnsFalse)
{
  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  ON_CALL(*mockEnvironment,
          HasControlStrategyGoalBeenReached(_, mantle_api::ControlStrategyType::kUndefined))
      .WillByDefault(Return(false));

  TransitionDynamics speedActionDynamics;
  speedActionDynamics.transitionDynamics.shape = mantle_api::Shape::kLinear;

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           []() -> units::velocity::meters_per_second_t { return 0_mps; }},
                          {mockEnvironment});

  ASSERT_FALSE(speedAction.HasControlStrategyGoalBeenReached("Vehicle1"));
}

TEST(SpeedAction, GivenkStepShapeForSpeedActionDynamics_ReturnTrue)
{
  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  ON_CALL(*mockEnvironment,
          HasControlStrategyGoalBeenReached(_, mantle_api::ControlStrategyType::kUndefined))
      .WillByDefault(Return(true));
  TransitionDynamics speedActionDynamics;
  speedActionDynamics.transitionDynamics.shape = mantle_api::Shape::kStep;

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           []() -> units::velocity::meters_per_second_t { return 0_mps; }},
                          {mockEnvironment});

  ASSERT_TRUE(speedAction.HasControlStrategyGoalBeenReached("Vehicle1"));
}

TEST(SpeedAction, GivenkUndefinedShapeForSpeedActionDynamics_ReturnFalse)
{
  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  ON_CALL(*mockEnvironment,
          HasControlStrategyGoalBeenReached(_, mantle_api::ControlStrategyType::kUndefined))
      .WillByDefault(Return(false));

  TransitionDynamics speedActionDynamics;
  speedActionDynamics.transitionDynamics.shape = mantle_api::Shape::kUndefined;

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           []() -> units::velocity::meters_per_second_t { return 0_mps; }},
                          {mockEnvironment});

  ASSERT_FALSE(speedAction.HasControlStrategyGoalBeenReached("Vehicle1"));
}

TEST(SpeedAction, GivenIsStepShapeForSpeedActionDynamics_SetControlStrategy)
{
  TransitionDynamics speedActionDynamics;
  speedActionDynamics.transitionDynamics.shape = mantle_api::Shape::kStep;

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           []() -> units::velocity::meters_per_second_t { return 0_mps; }},
                          {std::make_shared<mantle_api::MockEnvironment>()});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
}

TEST(SpeedAction, GivenLinearShapeAndkUndefinedDimensionForSpeedActionDynamics_ThrowError)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kUndefined, mantle_api::Shape::kLinear, {}};
  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           []() -> units::velocity::meters_per_second_t { return 123_mps; }},
                          {std::make_shared<mantle_api::MockEnvironment>()});

  EXPECT_THROW(speedAction.SetControlStrategy(), std::runtime_error);
}

TEST(SpeedAction, GivenLinearShapeAndkTimeDimensionForSpeedActionDynamics_SetControlStrategy)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kTime, mantle_api::Shape::kLinear, 3.6};
  SpeedActionTarget speedActionTarget = 1.2_mps;

  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  std::vector<std::shared_ptr<mantle_api::ControlStrategy>> control_strategies{};
  EXPECT_CALL(*mockEnvironment,
              UpdateControlStrategies(_, _))
      .WillOnce(testing::SaveArg<1>(&control_strategies));

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           [t = speedActionTarget]() -> units::velocity::meters_per_second_t { return t; }},
                          {mockEnvironment});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
  EXPECT_THAT(control_strategies, testing::SizeIs(1));
  EXPECT_EQ(control_strategies.front()->type, mantle_api::ControlStrategyType::kFollowVelocitySpline);
  auto control_strategy = dynamic_cast<mantle_api::FollowVelocitySplineControlStrategy*>(control_strategies[0].get());
  EXPECT_EQ(control_strategy->default_value, speedActionTarget);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(1));
  EXPECT_EQ(control_strategy->velocity_splines[0].start_time, mantle_api::Time(0));
  EXPECT_EQ(control_strategy->velocity_splines[0].end_time, mantle_api::Time(3600));
}

TEST(SpeedAction, GivenLinearShapeAndkRateDimensionForSpeedActionDynamics_SetControlStrategy)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kRate, mantle_api::Shape::kLinear, 1.2};
  SpeedActionTarget speedActionTarget = 1.2_mps;

  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  std::vector<std::shared_ptr<mantle_api::ControlStrategy>> control_strategies{};
  EXPECT_CALL(*mockEnvironment,
              UpdateControlStrategies(_, _))
      .WillOnce(testing::SaveArg<1>(&control_strategies));

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           [t = speedActionTarget]() -> units::velocity::meters_per_second_t { return t; }},
                          {mockEnvironment});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
  EXPECT_THAT(control_strategies, testing::SizeIs(1));
  EXPECT_EQ(control_strategies.front()->type, mantle_api::ControlStrategyType::kFollowVelocitySpline);
  auto control_strategy = dynamic_cast<mantle_api::FollowVelocitySplineControlStrategy*>(control_strategies[0].get());
  EXPECT_EQ(control_strategy->default_value, speedActionTarget);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(1));
  EXPECT_EQ(control_strategy->velocity_splines[0].start_time, mantle_api::Time(0));
  EXPECT_EQ(control_strategy->velocity_splines[0].end_time, mantle_api::Time(1000));
}

TEST(SpeedAction, GivenLinearShapeAndkDistanceDimensionForSpeedActionDynamics_SetControlStrategy)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kDistance, mantle_api::Shape::kLinear, 0.6};
  SpeedActionTarget speedActionTarget = 1.2_mps;

  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  std::vector<std::shared_ptr<mantle_api::ControlStrategy>> control_strategies{};
  EXPECT_CALL(*mockEnvironment,
              UpdateControlStrategies(_, _))
      .WillOnce(testing::SaveArg<1>(&control_strategies));

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           [t = speedActionTarget]() -> units::velocity::meters_per_second_t { return t; }},
                          {mockEnvironment});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
  EXPECT_THAT(control_strategies, testing::SizeIs(1));
  EXPECT_EQ(control_strategies.front()->type, mantle_api::ControlStrategyType::kFollowVelocitySpline);
  auto control_strategy = dynamic_cast<mantle_api::FollowVelocitySplineControlStrategy*>(control_strategies[0].get());
  EXPECT_EQ(control_strategy->default_value, speedActionTarget);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(1));
  EXPECT_EQ(control_strategy->velocity_splines[0].start_time, mantle_api::Time(0));
  EXPECT_EQ(control_strategy->velocity_splines[0].end_time, mantle_api::Time(1000));
}

TEST(SpeedAction, WithCubicShapeAndRate_SetsControlStrategy)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kRate, mantle_api::Shape::kCubic, 1.2};
  SpeedActionTarget speedActionTarget = 1.2_mps;

  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  std::vector<std::shared_ptr<mantle_api::ControlStrategy>> control_strategies{};
  EXPECT_CALL(*mockEnvironment, UpdateControlStrategies(_, _))
      .WillOnce(testing::SaveArg<1>(&control_strategies));

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           [t = speedActionTarget]() -> units::velocity::meters_per_second_t { return t; }},
                          {mockEnvironment});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
  EXPECT_THAT(control_strategies, SizeIs(1));
  EXPECT_EQ(control_strategies.front()->type, mantle_api::ControlStrategyType::kFollowVelocitySpline);
  auto control_strategy = dynamic_cast<mantle_api::FollowVelocitySplineControlStrategy*>(control_strategies[0].get());
  EXPECT_EQ(control_strategy->default_value, speedActionTarget);
  EXPECT_THAT(control_strategy->velocity_splines, SizeIs(1));
  EXPECT_EQ(control_strategy->velocity_splines[0].start_time, mantle_api::Time(0));
  EXPECT_EQ(control_strategy->velocity_splines[0].end_time, mantle_api::Time(1000));
}

TEST(SpeedAction, WithCubicShapeAndTime_SetsControlStrategy)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kTime, mantle_api::Shape::kCubic, 1.2};
  SpeedActionTarget speedActionTarget = 1.2_mps;

  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  std::vector<std::shared_ptr<mantle_api::ControlStrategy>> control_strategies{};
  EXPECT_CALL(*mockEnvironment, UpdateControlStrategies(_, _))
      .WillOnce(testing::SaveArg<1>(&control_strategies));

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           [t = speedActionTarget]() -> units::velocity::meters_per_second_t { return t; }},
                          {mockEnvironment});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
  EXPECT_THAT(control_strategies, SizeIs(1));
  EXPECT_EQ(control_strategies.front()->type, mantle_api::ControlStrategyType::kFollowVelocitySpline);
  auto control_strategy = dynamic_cast<mantle_api::FollowVelocitySplineControlStrategy*>(control_strategies[0].get());
  EXPECT_EQ(control_strategy->default_value, speedActionTarget);
  EXPECT_THAT(control_strategy->velocity_splines, SizeIs(1));
  EXPECT_EQ(control_strategy->velocity_splines[0].start_time, mantle_api::Time(0));
  EXPECT_EQ(control_strategy->velocity_splines[0].end_time, mantle_api::Time(1200));
}

TEST(SpeedAction, WithCubicShapeAndDistance_SetsControlStrategy)
{
  TransitionDynamics speedActionDynamics{mantle_api::Dimension::kDistance, mantle_api::Shape::kCubic, 0.6};
  SpeedActionTarget speedActionTarget = 1.2_mps;

  auto mockEnvironment = std::make_shared<mantle_api::MockEnvironment>();
  std::vector<std::shared_ptr<mantle_api::ControlStrategy>> control_strategies{};
  EXPECT_CALL(*mockEnvironment,
              UpdateControlStrategies(_, _))
      .WillOnce(testing::SaveArg<1>(&control_strategies));

  SpeedAction speedAction({std::vector<std::string>{"Vehicle1"},
                           speedActionDynamics,
                           [t = speedActionTarget]() -> units::velocity::meters_per_second_t { return t; }},
                          {mockEnvironment});

  EXPECT_NO_THROW(speedAction.SetControlStrategy());
  EXPECT_THAT(control_strategies, testing::SizeIs(1));
  EXPECT_EQ(control_strategies.front()->type, mantle_api::ControlStrategyType::kFollowVelocitySpline);
  auto control_strategy = dynamic_cast<mantle_api::FollowVelocitySplineControlStrategy*>(control_strategies[0].get());
  EXPECT_EQ(control_strategy->default_value, speedActionTarget);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(1));
  EXPECT_EQ(control_strategy->velocity_splines[0].start_time, mantle_api::Time(0));
  EXPECT_EQ(control_strategy->velocity_splines[0].end_time, mantle_api::Time(1000));
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenDecreasingSpeedAndJerkLimits_WhenExpectedRatesAreTooHigh_ThenSplineHasAccelerationAndDecelerationJerkLimits)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{123.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{0.0_mps};
  const auto acceleration{-123.0_mps_sq};
  const auto max_acceleration_rate{30.0_mps_cu};
  const auto max_deceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, max_acceleration_rate, max_deceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(3));
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[0].polynomial), -0.5 * max_deceleration_rate);
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[1].polynomial), acceleration);
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[2].polynomial), 0.5 * max_acceleration_rate);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenDecreasingSpeedAndJerkLimitsAndPositiveSpeedTarget_WhenExpectedRateAreTooHigh_ThenSplineHasAccelerationAndDecelerationJerkLimitsAndInitialSpeed)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{456.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{123.0_mps};
  const auto acceleration{-333.0_mps_sq};
  const auto max_acceleration_rate{30.0_mps_cu};
  const auto max_deceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, max_acceleration_rate, max_deceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(3));
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[0].polynomial), -0.5 * max_deceleration_rate);
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[1].polynomial), acceleration);
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[2].polynomial), 0.5 * max_acceleration_rate);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenDecreasingSpeedAndAccelerationRateLimit_WhenExpectedAccelerationRateIsTooHigh_ThenSplineHasAccelerationRateLimit)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{123.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{0.0_mps};
  const auto acceleration{-123.0_mps_sq};
  const auto max_acceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, max_acceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(2));
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[0].polynomial), acceleration);
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[1].polynomial), 0.5 * max_acceleration_rate);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenDecreasingSpeedAndDecelerationRateLimit_WhenExpectedDecelerationRateIsTooHigh_ThenSplineHasDecelerationRateLimit)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{123.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{0.0_mps};
  const auto acceleration{-123.0_mps_sq};
  const auto max_deceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, std::nullopt, max_deceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(2));
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[0].polynomial), -0.5 * max_deceleration_rate);
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[1].polynomial), acceleration);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenDecreasingSpeedAndNoJerkLimits_WhenExpectedDecelerationRateIsTooHigh_ThenSplineHasNoJerkLimits)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{123.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{0.0_mps};
  const auto acceleration{-123.0_mps_sq};

  Init(initial_velocity, target_speed, acceleration);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(1));
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[0].polynomial), acceleration);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenIncreasingSpeedAndJerkLimits_WhenExpectedRatesAreTooHigh_ThenSplineHasAccelerationAndDecelerationJerkLimits)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{0.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{123.0_mps};
  const auto acceleration{123.0_mps_sq};
  const auto max_acceleration_rate{30.0_mps_cu};
  const auto max_deceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, max_acceleration_rate, max_deceleration_rate);
  SetAndCheckControlStrategies();
  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(3));
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[0].polynomial), 0.5 * max_deceleration_rate);
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[1].polynomial), acceleration);
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[2].polynomial), -0.5 * max_acceleration_rate);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenIncreasingSpeedAndJerkLimitsAndInitialSpeed_WhenExpectedRateAreTooHigh_ThenSplineHasAccelerationAndDecelerationJerkLimitsAndInitialSpeed)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{123.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{456.0_mps};
  const auto acceleration{333.0_mps_sq};
  const auto max_acceleration_rate{30.0_mps_cu};
  const auto max_deceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, max_acceleration_rate, max_deceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(3));
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[0].polynomial), 0.5 * max_deceleration_rate);
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[1].polynomial), acceleration);
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[2].polynomial), -0.5 * max_acceleration_rate);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenIncreasingSpeedAndAccelerationRateLimit_WhenExpectedAccelerationRateIsTooHigh_ThenSplineHasAccelerationRateLimit)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{0.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{123.0_mps};
  const auto acceleration{123.0_mps_sq};
  const auto max_acceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, max_acceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(2));
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[0].polynomial), 0.5 * max_acceleration_rate);
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[1].polynomial), acceleration);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenIncreasingSpeedAndDecelerationRateLimit_WhenExpectedDecelerationRateIsTooHigh_ThenSplineHasDecelerationRateLimit)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{0.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{123.0_mps};
  const auto acceleration{123.0_mps_sq};
  const auto max_deceleration_rate{30.0_mps_cu};

  Init(initial_velocity, target_speed, acceleration, std::nullopt, max_deceleration_rate);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(2));
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[0].polynomial), acceleration);
  EXPECT_EQ(std::get<1>(control_strategy->velocity_splines[1].polynomial), -0.5 * max_deceleration_rate);
}

TEST_F(SpeedActionWithJerkLimitsTestFixture, GivenIncreasingSpeedAndNoJerkLimits_WhenExpectedAccelerationRateIsTooHigh_ThenSplineHasNoJerkLimits)
{
  const Vec3<units::velocity::meters_per_second_t> initial_velocity{0.0_mps, 0.0_mps, 0.0_mps};
  const auto target_speed{123.0_mps};
  const auto acceleration{123.0_mps_sq};

  Init(initial_velocity, target_speed, acceleration);
  SetAndCheckControlStrategies();

  auto control_strategy{dynamic_cast<FollowVelocitySplineControlStrategy*>(control_strategies_.front().get())};
  EXPECT_EQ(control_strategy->default_value, target_speed);
  EXPECT_THAT(control_strategy->velocity_splines, testing::SizeIs(1));
  EXPECT_EQ(std::get<3>(control_strategy->velocity_splines[0].polynomial), initial_velocity.Length());
  EXPECT_EQ(std::get<2>(control_strategy->velocity_splines[0].polynomial), acceleration);
}

}  // namespace OpenScenarioEngine::v1_2