TEST_F(InitTrajectoryTest, WrappingSpec)
{
  // Modify trajectory message created in the fixture to wrap around
  const double wrap = 4 * M_PI;
  for (unsigned int i = 0; i < trajectory_msg.points.size(); ++i)
  {
    trajectory_msg.points[i].positions[0] += wrap; // Add wrapping value to all points
  }

  // Before first point: Return 4 segments: Last of current + bridge + full message
  const ros::Time time(curr_traj[0][0].startTime());
  std::vector<bool> angle_wraparound(1, true);
  InitJointTrajectoryOptions<Trajectory> options;
  options.current_trajectory = &curr_traj;
  options.angle_wraparound      = &angle_wraparound;

  Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
  ASSERT_EQ(points.size() + 1, trajectory[0].size());

  // Check current trajectory segment start/end times and states (only one)
  {
    const Segment& ref_segment = curr_traj[0][0];
    const Segment& segment     = trajectory[0][0];

    // Check start/end times
    {
      EXPECT_EQ(ref_segment.startTime(), segment.startTime());
      EXPECT_EQ(ref_segment.endTime(),   segment.endTime());
    }

    // Check start state
    {
      typename Segment::State ref_state, state;
      ref_segment.sample(ref_segment.startTime(), ref_state);
      segment.sample(segment.startTime(), state);
      EXPECT_EQ(ref_state.position[0],     state.position[0]);
      EXPECT_EQ(ref_state.velocity[0],     state.velocity[0]);
      EXPECT_EQ(ref_state.acceleration[0], state.acceleration[0]);
    }

    // Check end state
    {
      typename Segment::State ref_state, state;
      ref_segment.sample(ref_segment.endTime(), ref_state);
      segment.sample(segment.endTime(), state);
      EXPECT_EQ(ref_state.position[0],     state.position[0]);
      EXPECT_EQ(ref_state.velocity[0],     state.velocity[0]);
      EXPECT_EQ(ref_state.acceleration[0], state.acceleration[0]);
    }
  }

  // Check bridge trajectory segment start/end times and states (only one)
  {
    const Segment& ref_segment = curr_traj[0][0];
    const Segment& segment     = trajectory[0][1];

    const vector<JointTrajectoryPoint>& msg_points = trajectory_msg.points;

    // Segment start time should correspond to message start time
    // Segment end time should correspond to first trajectory message point
    const ros::Time msg_start_time = trajectory_msg.header.stamp;
    const typename Segment::Time start_time = msg_start_time.toSec();
    const typename Segment::Time end_time   = (msg_start_time +  msg_points[0].time_from_start).toSec();

    // Check start/end times
    {
      EXPECT_EQ(start_time, segment.startTime());
      EXPECT_EQ(end_time,   segment.endTime());
    }

    // Check start state. Corresponds to current trajectory sampled at start_time
    {
      typename Segment::State ref_state, state;
      ref_segment.sample(start_time, ref_state);
      segment.sample(segment.startTime(), state);
      EXPECT_EQ(ref_state.position[0],     state.position[0]);
      EXPECT_EQ(ref_state.velocity[0],     state.velocity[0]);
      EXPECT_EQ(ref_state.acceleration[0], state.acceleration[0]);
    }

    // Check end state. Corresponds to first trajectory message point
    {
      typename Segment::State state;
      segment.sample(segment.endTime(), state);
      EXPECT_EQ(msg_points[0].positions[0] - wrap, state.position[0]); // NOTE: Wrap used
      EXPECT_EQ(msg_points[0].velocities[0],       state.velocity[0]);
      EXPECT_EQ(msg_points[0].accelerations[0],    state.acceleration[0]);
    }
  }

  // Check all segment start/end times and states (2 segments)
  for (unsigned int traj_it = 2, msg_it = 0; traj_it < trajectory[0].size(); ++traj_it, ++msg_it)
  {
    const ros::Time msg_start_time = trajectory_msg.header.stamp;
    const vector<JointTrajectoryPoint>& msg_points = trajectory_msg.points;

    const Segment& segment = trajectory[0][traj_it];
    const JointTrajectoryPoint& p_start = msg_points[msg_it];
    const JointTrajectoryPoint& p_end   = msg_points[msg_it + 1];

    // Check start/end times
    {
      const typename Segment::Time start_time = (msg_start_time + p_start.time_from_start).toSec();
      const typename Segment::Time end_time   = (msg_start_time + p_end.time_from_start).toSec();
      EXPECT_EQ(start_time, segment.startTime());
      EXPECT_EQ(end_time,   segment.endTime());
    }

    // Check start state
    {
      typename Segment::State state;
      segment.sample(segment.startTime(), state);
      EXPECT_EQ(p_start.positions[0] - wrap, state.position[0]); // NOTE: Wrap used
      EXPECT_EQ(p_start.velocities[0],       state.velocity[0]);
      EXPECT_EQ(p_start.accelerations[0],    state.acceleration[0]);
    }

    // Check end state
    {
      typename Segment::State state;
      segment.sample(segment.endTime(), state);
      EXPECT_EQ(p_end.positions[0] - wrap, state.position[0]); // NOTE: Wrap used
      EXPECT_EQ(p_end.velocities[0],       state.velocity[0]);
      EXPECT_EQ(p_end.accelerations[0],    state.acceleration[0]);
    }
  }

  // Reference joint names size mismatch
  {
    std::vector<bool> angle_wraparound(2, true);
    InitJointTrajectoryOptions<Trajectory> options;
    options.current_trajectory = &curr_traj;
    options.angle_wraparound      = &angle_wraparound;

    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, trajectory_msg.header.stamp, options);
    EXPECT_TRUE(trajectory.empty());
  }
}
// Test logic of parsing a trajectory message. Current trajectory is specified, hence trajectory combination takes place
TEST_F(InitTrajectoryTest, InitLogicCombine)
{
  const ros::Time msg_start_time = trajectory_msg.header.stamp;
  const vector<JointTrajectoryPoint>& msg_points = trajectory_msg.points;
  InitJointTrajectoryOptions<Trajectory> options;
  options.current_trajectory = &curr_traj;

  // Empty trajectory message: Return empty trajectory
  {
    const ros::Time time = msg_start_time;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msgs::JointTrajectory(), msg_start_time, options);
    EXPECT_TRUE(trajectory.empty());
  }

  // Input time is...

  // Before first point, starting the current trajectory: Return 4 segments: Last of current + bridge + full message
  {
    const ros::Time time(curr_traj[0][0].startTime());
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_EQ(points.size() + 1, trajectory[0].size());
  }

  // Before first point: Return 4 segments: Last of current + bridge + full message
  {
    const ros::Time time = msg_start_time;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_EQ(points.size() + 1, trajectory[0].size());
  }

  // First point: Return partial trajectory, 3 segments: Last of current + bridge + last of message
  {
    const ros::Time time = msg_start_time + msg_points.begin()->time_from_start;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_EQ(points.size(), trajectory[0].size());
  }

  // Between the first and second points: Same as before
  {
    const ros::Time time = msg_start_time +
    ros::Duration((msg_points.begin()->time_from_start + (++msg_points.begin())->time_from_start).toSec() / 2.0);
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_EQ(msg_points.size(), trajectory[0].size());
  }

  // Second point: Return partial trajectory, 2 segments: Last of current + bridge (only one point of message made it)
  {
    const ros::Time time = msg_start_time + (++msg_points.begin())->time_from_start;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_EQ(msg_points.size() - 1, trajectory[0].size());
  }

  // Between the second and third points: Same as before
  {
    const ros::Time time = msg_start_time +
    ros::Duration(((++msg_points.begin())->time_from_start + (--msg_points.end())->time_from_start).toSec() / 2.0);
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_EQ(msg_points.size() - 1, trajectory[0].size());
  }

  // Last point: Return empty trajectory
  {
    const ros::Time time = msg_start_time + (--msg_points.end())->time_from_start;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_TRUE(trajectory.empty());
  }

  // After the last point: Return empty trajectory
  {
    const ros::Time time(msg_start_time + msg_points.back().time_from_start + ros::Duration(1.0));
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time, options);
    EXPECT_TRUE(trajectory.empty());
  }
}
TEST_F(InitTrajectoryTest, JointMapping)
{
  // Add an extra joint to the trajectory message created in the fixture
  trajectory_msg.points[0].positions.push_back(-2.0);
  trajectory_msg.points[0].velocities.push_back(0.0);
  trajectory_msg.points[0].accelerations.push_back(0.0);

  trajectory_msg.points[1].positions.push_back(-4.0);
  trajectory_msg.points[1].velocities.push_back(0.0);
  trajectory_msg.points[1].accelerations.push_back(0.0);

  trajectory_msg.points[2].positions.push_back(-3.0);
  trajectory_msg.points[2].velocities.push_back(0.0);
  trajectory_msg.points[2].accelerations.push_back(0.0);

  trajectory_msg.joint_names.push_back("bar_joint");

  // No reference joint names: Original message order is preserved
  {
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, trajectory_msg.header.stamp);

    // Check position values only
    const Segment& segment_foo_joint = trajectory[0][0];
    const Segment& segment_bar_joint = trajectory[1][0];
    typename Segment::State state;
    segment_foo_joint.sample(segment_foo_joint.startTime(), state);
    EXPECT_EQ(trajectory_msg.points[0].positions[0],  state.position[0]);
    segment_bar_joint.sample(segment_bar_joint.startTime(), state);
    EXPECT_EQ(trajectory_msg.points[0].positions[1],  state.position[0]);
  }

  // Reference joint names vector that reverses joint order
  {
    vector<string> joint_names; // Joints are reversed
    joint_names.push_back(trajectory_msg.joint_names[1]);
    joint_names.push_back(trajectory_msg.joint_names[0]);
    InitJointTrajectoryOptions<Trajectory> options;
    options.joint_names = &joint_names;

    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, trajectory_msg.header.stamp, options);

    // Check position values only
    const Segment& segment_bar_joint = trajectory[0][0];
    const Segment& segment_foo_joint = trajectory[1][0];

    typename Segment::State state_foo_joint;
    typename Segment::State state_bar_joint;
    segment_foo_joint.sample(segment_foo_joint.startTime(), state_foo_joint);
    segment_bar_joint.sample(segment_bar_joint.startTime(), state_bar_joint);
    EXPECT_NE(trajectory_msg.points[0].positions[0],  state_bar_joint.position[0]);
    EXPECT_NE(trajectory_msg.points[0].positions[1],  state_foo_joint.position[0]);
    EXPECT_EQ(trajectory_msg.points[0].positions[0],  state_foo_joint.position[0]);
    EXPECT_EQ(trajectory_msg.points[0].positions[1],  state_bar_joint.position[0]);
  }

  // Reference joint names size mismatch
  {
    vector<string> joint_names;
    joint_names.push_back(trajectory_msg.joint_names[0]);
    InitJointTrajectoryOptions<Trajectory> options;
    options.joint_names = &joint_names;

    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, trajectory_msg.header.stamp, options);
    EXPECT_TRUE(trajectory.empty());
  }

  // Reference joint names value mismatch
  {
    vector<string> joint_names;
    joint_names.push_back(trajectory_msg.joint_names[0]);
    joint_names.push_back("baz_joint");
    InitJointTrajectoryOptions<Trajectory> options;
    options.joint_names = &joint_names;

    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, trajectory_msg.header.stamp, options);
    EXPECT_TRUE(trajectory.empty());
  }
}
// Test logic of parsing a trajectory message. No current trajectory is specified
TEST_F(InitTrajectoryTest, InitLogic)
{
  const ros::Time msg_start_time = trajectory_msg.header.stamp;
  const vector<JointTrajectoryPoint>& msg_points = trajectory_msg.points;

  // Empty trajectory message: Return empty trajectory
  {
    const ros::Time time = msg_start_time;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msgs::JointTrajectory(), msg_start_time);
    EXPECT_TRUE(trajectory.empty());
  }

  // Input time is...

  // Before first point: Return full trajectory (2 segments)
  {
    const ros::Time time = msg_start_time;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_EQ(points.size() - 1, trajectory[0].size());
  }

  // First point: Return partial trajectory (last segment)
  {
    const ros::Time time = msg_start_time + msg_points.begin()->time_from_start;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_EQ(points.size() - 2, trajectory[0].size());
  }

  // Between the first and second points: Return partial trajectory (last segment)
  {
    const ros::Time time = msg_start_time +
    ros::Duration((msg_points.begin()->time_from_start + (++msg_points.begin())->time_from_start).toSec() / 2.0);
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_EQ(msg_points.size() - 2, trajectory[0].size());
  }

  // Second point: Return empty trajectory
  {
    const ros::Time time = msg_start_time + (++msg_points.begin())->time_from_start;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_TRUE(trajectory.empty());
  }

  // Between the second and third points: Return empty trajectory
  {
    const ros::Time time = msg_start_time +
    ros::Duration(((++msg_points.begin())->time_from_start + (--msg_points.end())->time_from_start).toSec() / 2.0);
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_TRUE(trajectory.empty());
  }

  // Last point: Return empty trajectory
  {
    const ros::Time time = msg_start_time + (--msg_points.end())->time_from_start;
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_TRUE(trajectory.empty());
  }

  // After the last point: Return empty trajectory
  {
    const ros::Time time(msg_start_time + msg_points.back().time_from_start + ros::Duration(1.0));
    Trajectory trajectory = initJointTrajectory<Trajectory>(trajectory_msg, time);
    EXPECT_TRUE(trajectory.empty());
  }
}