<commit_before>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * 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.
 *****************************************************************************/

/**
 * @file constraint_checker.cc
 **/

#include "modules/planning/constraint_checker/constraint_checker.h"

#include "modules/common/log.h"
#include "modules/planning/common/planning_gflags.h"

namespace apollo {
namespace planning {

namespace {
template <typename T>
bool WithinRange(const T v, const T lower, const T upper) {
  return lower <= v && v <= upper;
}
}

bool ConstraintChecker::ValidTrajectory(
    const DiscretizedTrajectory& trajectory) {
  // TODO(all): this is wrong; must be set to trajectory time range;
  const double kMaxCheckRelativeTime = 2.0;
  for (const auto& p : trajectory.trajectory_points()) {
    double t = p.relative_time();
    if (t > kMaxCheckRelativeTime) {
      break;
    }
    double lon_v = p.v();
    if (!WithinRange(lon_v, FLAGS_speed_lower_bound, FLAGS_speed_upper_bound)) {
      AERROR << "Velocity at relative time " << t
             << " exceeds bound, value: " << lon_v << ", bound ["
             << FLAGS_speed_lower_bound << ", " << FLAGS_speed_upper_bound
             << "].";
      return false;
    }

    double lon_a = p.a();
    if (!WithinRange(lon_a, FLAGS_longitudinal_acceleration_lower_bound,
                     FLAGS_longitudinal_acceleration_upper_bound)) {
      AERROR << "Longitudinal acceleration at relative time " << t
             << " exceeds bound, value: " << lon_a << ", bound ["
             << FLAGS_longitudinal_acceleration_lower_bound << ", "
             << FLAGS_longitudinal_acceleration_upper_bound << "].";
      return false;
    }

    double kappa = p.path_point().kappa();
    if (!WithinRange(kappa, -FLAGS_kappa_bound, FLAGS_kappa_bound)) {
      AERROR << "Kappa at relative time " << t
             << " exceeds bound, value: " << kappa << ", bound ["
             << -FLAGS_kappa_bound << ", " << FLAGS_kappa_bound << "].";
      return false;
    }
  }

  for (std::size_t i = 1; i < trajectory.NumOfPoints(); ++i) {
    const auto& p0 = trajectory.TrajectoryPointAt(i - 1);
    const auto& p1 = trajectory.TrajectoryPointAt(i);

    if (p1.relative_time() > kMaxCheckRelativeTime) {
      break;
    }

    double t = p0.relative_time();

    double dt = p1.relative_time() - p0.relative_time();
    double d_lon_a = p1.a() - p0.a();
    double lon_jerk = d_lon_a / dt;
    if (!WithinRange(lon_jerk, FLAGS_longitudinal_jerk_lower_bound,
                     FLAGS_longitudinal_jerk_upper_bound)) {
      AERROR << "Longitudinal jerk at relative time " << t
             << " exceeds bound, value: " << lon_jerk << ", bound ["
             << FLAGS_longitudinal_jerk_lower_bound << ", "
             << FLAGS_longitudinal_jerk_upper_bound << "].";
      return false;
    }

    double d_lat_a = p1.v() * p1.v() * p1.path_point().kappa() -
                     p0.v() * p0.v() * p0.path_point().kappa();

    // TODO(all): add lateral acceleration check
    double lat_jerk = d_lat_a / dt;
    if (!WithinRange(lat_jerk, -FLAGS_lateral_jerk_bound,
                     FLAGS_lateral_jerk_bound)) {
      AERROR << "Lateral jerk at relative time " << t
             << " exceeds bound, value: " << lat_jerk << ", bound ["
             << -FLAGS_lateral_jerk_bound << ", " << FLAGS_lateral_jerk_bound
             << "].";
      return false;
    }
  }

  return true;
}

}  // namespace planning
}  // namespace apollo
<commit_msg>Planning: [lattice] make some AERROR to ADEBUG in constraint_checker.cc<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * 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.
 *****************************************************************************/

/**
 * @file constraint_checker.cc
 **/

#include "modules/planning/constraint_checker/constraint_checker.h"

#include "modules/common/log.h"
#include "modules/planning/common/planning_gflags.h"

namespace apollo {
namespace planning {

namespace {
template <typename T>
bool WithinRange(const T v, const T lower, const T upper) {
  return lower <= v && v <= upper;
}
}

bool ConstraintChecker::ValidTrajectory(
    const DiscretizedTrajectory& trajectory) {
  // TODO(all): this is wrong; must be set to trajectory time range;
  const double kMaxCheckRelativeTime = 2.0;
  for (const auto& p : trajectory.trajectory_points()) {
    double t = p.relative_time();
    if (t > kMaxCheckRelativeTime) {
      break;
    }
    double lon_v = p.v();
    if (!WithinRange(lon_v, FLAGS_speed_lower_bound, FLAGS_speed_upper_bound)) {
      ADEBUG << "Velocity at relative time " << t
             << " exceeds bound, value: " << lon_v << ", bound ["
             << FLAGS_speed_lower_bound << ", " << FLAGS_speed_upper_bound
             << "].";
      return false;
    }

    double lon_a = p.a();
    if (!WithinRange(lon_a, FLAGS_longitudinal_acceleration_lower_bound,
                     FLAGS_longitudinal_acceleration_upper_bound)) {
      ADEBUG << "Longitudinal acceleration at relative time " << t
             << " exceeds bound, value: " << lon_a << ", bound ["
             << FLAGS_longitudinal_acceleration_lower_bound << ", "
             << FLAGS_longitudinal_acceleration_upper_bound << "].";
      return false;
    }

    double kappa = p.path_point().kappa();
    if (!WithinRange(kappa, -FLAGS_kappa_bound, FLAGS_kappa_bound)) {
      ADEBUG << "Kappa at relative time " << t
             << " exceeds bound, value: " << kappa << ", bound ["
             << -FLAGS_kappa_bound << ", " << FLAGS_kappa_bound << "].";
      return false;
    }
  }

  for (std::size_t i = 1; i < trajectory.NumOfPoints(); ++i) {
    const auto& p0 = trajectory.TrajectoryPointAt(i - 1);
    const auto& p1 = trajectory.TrajectoryPointAt(i);

    if (p1.relative_time() > kMaxCheckRelativeTime) {
      break;
    }

    double t = p0.relative_time();

    double dt = p1.relative_time() - p0.relative_time();
    double d_lon_a = p1.a() - p0.a();
    double lon_jerk = d_lon_a / dt;
    if (!WithinRange(lon_jerk, FLAGS_longitudinal_jerk_lower_bound,
                     FLAGS_longitudinal_jerk_upper_bound)) {
      ADEBUG << "Longitudinal jerk at relative time " << t
             << " exceeds bound, value: " << lon_jerk << ", bound ["
             << FLAGS_longitudinal_jerk_lower_bound << ", "
             << FLAGS_longitudinal_jerk_upper_bound << "].";
      return false;
    }

    double d_lat_a = p1.v() * p1.v() * p1.path_point().kappa() -
                     p0.v() * p0.v() * p0.path_point().kappa();

    // TODO(all): add lateral acceleration check
    double lat_jerk = d_lat_a / dt;
    if (!WithinRange(lat_jerk, -FLAGS_lateral_jerk_bound,
                     FLAGS_lateral_jerk_bound)) {
      ADEBUG << "Lateral jerk at relative time " << t
             << " exceeds bound, value: " << lat_jerk << ", bound ["
             << -FLAGS_lateral_jerk_bound << ", " << FLAGS_lateral_jerk_bound
             << "].";
      return false;
    }
  }

  return true;
}

}  // namespace planning
}  // namespace apollo
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * 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.
 *****************************************************************************/

/**
 * @file
 **/

#include "modules/planning/planner/open_space/open_space_planner.h"

#include <fstream>
#include <utility>

#include "cyber/common/log.h"
#include "cyber/task/task.h"
#include "modules/common/util/string_tokenizer.h"
#include "modules/planning/common/planning_gflags.h"

namespace apollo {
namespace planning {

using apollo::common::ErrorCode;
using apollo::common::Status;
using apollo::common::TrajectoryPoint;
using apollo::common::math::Box2d;
using apollo::common::math::Vec2d;

Status OpenSpacePlanner::Init(const PlanningConfig& planning_confgs) {
  AINFO << "In OpenSpacePlanner::Init()";

  // TODO(QiL): integrate open_space planner into task config when refactor done
  CHECK(common::util::GetProtoFromFile(FLAGS_planner_open_space_config_filename,
                                       &planner_open_space_config_))
      << "Failed to load open space config file "
      << FLAGS_planner_open_space_config_filename;

  current_trajectory_index_ = 0;
  // initialize open space trajectory generator;
  open_space_trajectory_generator_.reset(new OpenSpaceTrajectoryGenerator());

  open_space_trajectory_generator_->Init(planner_open_space_config_);

  if (FLAGS_enable_open_space_planner_thread) {
    task_future_ =
        cyber::Async(&OpenSpacePlanner::GenerateTrajectoryThread, this);
  }

  return Status::OK();
}

apollo::common::Status OpenSpacePlanner::Plan(
    const common::TrajectoryPoint& planning_init_point, Frame* frame) {
  // 1. Build Predicition Environments.
  predicted_bounding_rectangles_.clear();
  if (FLAGS_enable_open_space_planner_thread) {
    std::lock_guard<std::mutex> lock(open_space_mutex_);
    BuildPredictedEnvironment(frame->obstacles());

    // 2. Update Vehicle information and obstacles information from frame.
    // TODO(QiL, Jinyun): Refactor this to be more compact
    vehicle_state_ = frame->vehicle_state();
    rotate_angle_ = frame->origin_heading();
    translate_origin_ = frame->origin_point();
    end_pose_ = frame->open_space_end_pose();
    obstacles_num_ = frame->obstacles_num();
    obstacles_edges_num_ = frame->obstacles_edges_num();
    obstacles_A_ = frame->obstacles_A();
    obstacles_b_ = frame->obstacles_b();
    obstalce_list_ = frame->openspace_warmstart_obstacles();
    XYbounds_ = frame->ROI_xy_boundary();
    // 3. Check if trajectory updated, if so, update internal
    // trajectory_partition_;
    if (trajectory_updated_) {
      open_space_trajectory_generator_->UpdateTrajectory(
          &trajectory_partition_);
      AINFO << "Trajectory caculation updated, new results : "
            << trajectory_partition_.ShortDebugString();
    }
    // TODO(Jiaxuan): Choose the current_trajectory in trajectory_partition_
    // If the vehicle each the end point of current trajectory and stop
    // Then move to the next Trajectory
    current_trajectory_ =
        trajectory_partition_.adc_trajectory(current_trajectory_index_);

    TrajectoryPoint end_point = current_trajectory_.trajectory_point(
        current_trajectory_.trajectory_point_size() - 1);

    if (vehicle_state_.linear_velocity() <= 1e-3 &&
        std::sqrt((vehicle_state_.x() - end_point.path_point().x()) *
                      (vehicle_state_.x() - end_point.path_point().x()) +
                  (vehicle_state_.y() - end_point.path_point().y()) *
                      (vehicle_state_.y() - end_point.path_point().y())) <
            planner_open_space_config_.max_position_error_to_end_point() &&
        std::abs(vehicle_state_.heading() - end_point.path_point().theta()) <
            planner_open_space_config_.max_theta_error_to_end_point() &&
        (current_trajectory_index_ <
         trajectory_partition_.adc_trajectory_size() - 1)) {
      current_trajectory_index_ += 1;
    }

    // 4. Collision check for updated trajectory, if pass, update frame, else,
    // return error status

    if (IsCollisionFreeTrajectory(current_trajectory_)) {
      frame->mutable_trajectory()->CopyFrom(current_trajectory_);
      return Status::OK();
    } else {
      // If collision happens, return wrong planning status and estop
      // trajectory would be sent in std planning
      return Status(ErrorCode::PLANNING_ERROR, "Collision Check failed");
    }
  } else {
    // Single thread logic

    vehicle_state_ = frame->vehicle_state();
    rotate_angle_ = frame->origin_heading();
    translate_origin_ = frame->origin_point();
    end_pose_ = frame->open_space_end_pose();
    obstacles_num_ = frame->obstacles_num();
    obstacles_edges_num_ = frame->obstacles_edges_num();
    obstacles_A_ = frame->obstacles_A();
    obstacles_b_ = frame->obstacles_b();
    obstalce_list_ = frame->openspace_warmstart_obstacles();
    XYbounds_ = frame->ROI_xy_boundary();

    // 2. Generate Trajectory;
    Status status = open_space_trajectory_generator_->Plan(
        vehicle_state_, XYbounds_, rotate_angle_, translate_origin_, end_pose_,
        obstacles_num_, obstacles_edges_num_, obstacles_A_, obstacles_b_,
        obstalce_list_);

    // 3. If status is OK, update vehicle trajectory;
    if (status == Status::OK()) {
      open_space_trajectory_generator_->UpdateTrajectory(
          &trajectory_partition_);
      AINFO << "Trajectory caculation updated, new results : "
            << trajectory_partition_.ShortDebugString();
    } else {
      return Status(ErrorCode::PLANNING_ERROR,
                    "Planning failed to generate open space trajectory");
    }

    current_trajectory_ =
        trajectory_partition_.adc_trajectory(current_trajectory_index_);

    TrajectoryPoint end_point = current_trajectory_.trajectory_point(
        current_trajectory_.trajectory_point_size() - 1);

    if (vehicle_state_.linear_velocity() <= 1e-3 &&
        std::sqrt((vehicle_state_.x() - end_point.path_point().x()) *
                      (vehicle_state_.x() - end_point.path_point().x()) +
                  (vehicle_state_.y() - end_point.path_point().y()) *
                      (vehicle_state_.y() - end_point.path_point().y())) <
            planner_open_space_config_.max_position_error_to_end_point() &&
        std::abs(vehicle_state_.heading() - end_point.path_point().theta()) <
            planner_open_space_config_.max_theta_error_to_end_point() &&
        (current_trajectory_index_ <
         trajectory_partition_.adc_trajectory_size() - 1)) {
      current_trajectory_index_ += 1;
    }

    // 4. Collision check for updated trajectory, if pass, update frame, else,
    // return error status

    if (IsCollisionFreeTrajectory(current_trajectory_)) {
      frame->mutable_trajectory()->CopyFrom(current_trajectory_);
      return Status::OK();
    } else {
      // If collision happens, return wrong planning status and estop
      // trajectory would be sent in std planning
      return Status(ErrorCode::PLANNING_ERROR, "Collision Check failed");
    }
  }
}

void OpenSpacePlanner::GenerateTrajectoryThread() {
  while (!is_stop_) {
    {
      std::lock_guard<std::mutex> lock(open_space_mutex_);
      trajectory_updated_ = false;
      if (open_space_trajectory_generator_->Plan(
              vehicle_state_, XYbounds_, rotate_angle_, translate_origin_,
              end_pose_, obstacles_num_, obstacles_edges_num_, obstacles_A_,
              obstacles_b_, obstalce_list_) == Status::OK()) {
        trajectory_updated_ = true;
      }
    }
  }
}

void OpenSpacePlanner::Stop() {
  is_stop_ = true;
  if (FLAGS_enable_open_space_planner_thread) {
    task_future_.get();
  }
}

bool OpenSpacePlanner::IsCollisionFreeTrajectory(
    const ADCTrajectory& adc_trajectory) {
  CHECK_LE(adc_trajectory.trajectory_point().size(),
           predicted_bounding_rectangles_.size());
  const auto& vehicle_config =
      common::VehicleConfigHelper::Instance()->GetConfig();
  double ego_length = vehicle_config.vehicle_param().length();
  double ego_width = vehicle_config.vehicle_param().width();

  for (int i = 0; i < adc_trajectory.trajectory_point().size(); ++i) {
    const auto& trajectory_point = adc_trajectory.trajectory_point(i);
    double ego_theta = trajectory_point.path_point().theta();
    Box2d ego_box(
        {trajectory_point.path_point().x(), trajectory_point.path_point().y()},
        ego_theta, ego_length, ego_width);
    double shift_distance =
        ego_length / 2.0 - vehicle_config.vehicle_param().back_edge_to_center();
    Vec2d shift_vec{shift_distance * std::cos(ego_theta),
                    shift_distance * std::sin(ego_theta)};
    ego_box.Shift(shift_vec);

    for (const auto& obstacle_box : predicted_bounding_rectangles_[i]) {
      if (ego_box.HasOverlap(obstacle_box)) {
        return true;
      }
    }
  }
  return false;
}

void OpenSpacePlanner::BuildPredictedEnvironment(
    const std::vector<const Obstacle*>& obstacles) {
  CHECK(predicted_bounding_rectangles_.empty());
  double relative_time = 0.0;
  while (relative_time < FLAGS_trajectory_time_length) {
    std::vector<Box2d> predicted_env;
    for (const Obstacle* obstacle : obstacles) {
      TrajectoryPoint point = obstacle->GetPointAtTime(relative_time);
      Box2d box = obstacle->GetBoundingBox(point);
      predicted_env.push_back(std::move(box));
    }
    predicted_bounding_rectangles_.push_back(std::move(predicted_env));
    relative_time += FLAGS_trajectory_time_resolution;
  }
}

}  // namespace planning
}  // namespace apollo
<commit_msg>Planning : fix collision check logic in open space planner<commit_after>/******************************************************************************
 * Copyright 2018 The Apollo Authors. All Rights Reserved.
 *
 * 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.
 *****************************************************************************/

/**
 * @file
 **/

#include "modules/planning/planner/open_space/open_space_planner.h"

#include <fstream>
#include <utility>

#include "cyber/common/log.h"
#include "cyber/task/task.h"
#include "modules/common/util/string_tokenizer.h"
#include "modules/planning/common/planning_gflags.h"

namespace apollo {
namespace planning {

using apollo::common::ErrorCode;
using apollo::common::Status;
using apollo::common::TrajectoryPoint;
using apollo::common::math::Box2d;
using apollo::common::math::Vec2d;

Status OpenSpacePlanner::Init(const PlanningConfig& planning_confgs) {
  AINFO << "In OpenSpacePlanner::Init()";

  // TODO(QiL): integrate open_space planner into task config when refactor done
  CHECK(common::util::GetProtoFromFile(FLAGS_planner_open_space_config_filename,
                                       &planner_open_space_config_))
      << "Failed to load open space config file "
      << FLAGS_planner_open_space_config_filename;

  current_trajectory_index_ = 0;
  // initialize open space trajectory generator;
  open_space_trajectory_generator_.reset(new OpenSpaceTrajectoryGenerator());

  open_space_trajectory_generator_->Init(planner_open_space_config_);

  if (FLAGS_enable_open_space_planner_thread) {
    task_future_ =
        cyber::Async(&OpenSpacePlanner::GenerateTrajectoryThread, this);
  }

  return Status::OK();
}

apollo::common::Status OpenSpacePlanner::Plan(
    const common::TrajectoryPoint& planning_init_point, Frame* frame) {
  // 1. Build Predicition Environments.
  predicted_bounding_rectangles_.clear();
  if (FLAGS_enable_open_space_planner_thread) {
    std::lock_guard<std::mutex> lock(open_space_mutex_);
    BuildPredictedEnvironment(frame->obstacles());

    // 2. Update Vehicle information and obstacles information from frame.
    // TODO(QiL, Jinyun): Refactor this to be more compact
    vehicle_state_ = frame->vehicle_state();
    rotate_angle_ = frame->origin_heading();
    translate_origin_ = frame->origin_point();
    end_pose_ = frame->open_space_end_pose();
    obstacles_num_ = frame->obstacles_num();
    obstacles_edges_num_ = frame->obstacles_edges_num();
    obstacles_A_ = frame->obstacles_A();
    obstacles_b_ = frame->obstacles_b();
    obstalce_list_ = frame->openspace_warmstart_obstacles();
    XYbounds_ = frame->ROI_xy_boundary();
    // 3. Check if trajectory updated, if so, update internal
    // trajectory_partition_;
    if (trajectory_updated_) {
      open_space_trajectory_generator_->UpdateTrajectory(
          &trajectory_partition_);
      AINFO << "Trajectory caculation updated, new results : "
            << trajectory_partition_.ShortDebugString();
    }
    // TODO(Jiaxuan): Choose the current_trajectory in trajectory_partition_
    // If the vehicle each the end point of current trajectory and stop
    // Then move to the next Trajectory
    current_trajectory_ =
        trajectory_partition_.adc_trajectory(current_trajectory_index_);

    TrajectoryPoint end_point = current_trajectory_.trajectory_point(
        current_trajectory_.trajectory_point_size() - 1);

    if (vehicle_state_.linear_velocity() <= 1e-3 &&
        std::sqrt((vehicle_state_.x() - end_point.path_point().x()) *
                      (vehicle_state_.x() - end_point.path_point().x()) +
                  (vehicle_state_.y() - end_point.path_point().y()) *
                      (vehicle_state_.y() - end_point.path_point().y())) <
            planner_open_space_config_.max_position_error_to_end_point() &&
        std::abs(vehicle_state_.heading() - end_point.path_point().theta()) <
            planner_open_space_config_.max_theta_error_to_end_point() &&
        (current_trajectory_index_ <
         trajectory_partition_.adc_trajectory_size() - 1)) {
      current_trajectory_index_ += 1;
    }

    // 4. Collision check for updated trajectory, if pass, update frame, else,
    // return error status

    if (IsCollisionFreeTrajectory(current_trajectory_)) {
      frame->mutable_trajectory()->CopyFrom(current_trajectory_);
      return Status::OK();
    } else {
      // If collision happens, return wrong planning status and estop
      // trajectory would be sent in std planning
      return Status(ErrorCode::PLANNING_ERROR, "Collision Check failed");
    }
  } else {
    // Single thread logic

    vehicle_state_ = frame->vehicle_state();
    rotate_angle_ = frame->origin_heading();
    translate_origin_ = frame->origin_point();
    end_pose_ = frame->open_space_end_pose();
    obstacles_num_ = frame->obstacles_num();
    obstacles_edges_num_ = frame->obstacles_edges_num();
    obstacles_A_ = frame->obstacles_A();
    obstacles_b_ = frame->obstacles_b();
    obstalce_list_ = frame->openspace_warmstart_obstacles();
    XYbounds_ = frame->ROI_xy_boundary();

    // 2. Generate Trajectory;
    Status status = open_space_trajectory_generator_->Plan(
        vehicle_state_, XYbounds_, rotate_angle_, translate_origin_, end_pose_,
        obstacles_num_, obstacles_edges_num_, obstacles_A_, obstacles_b_,
        obstalce_list_);

    // 3. If status is OK, update vehicle trajectory;
    if (status == Status::OK()) {
      open_space_trajectory_generator_->UpdateTrajectory(
          &trajectory_partition_);
      AINFO << "Trajectory caculation updated, new results : "
            << trajectory_partition_.ShortDebugString();
    } else {
      return Status(ErrorCode::PLANNING_ERROR,
                    "Planning failed to generate open space trajectory");
    }

    current_trajectory_ =
        trajectory_partition_.adc_trajectory(current_trajectory_index_);

    TrajectoryPoint end_point = current_trajectory_.trajectory_point(
        current_trajectory_.trajectory_point_size() - 1);

    if (vehicle_state_.linear_velocity() <= 1e-3 &&
        std::sqrt((vehicle_state_.x() - end_point.path_point().x()) *
                      (vehicle_state_.x() - end_point.path_point().x()) +
                  (vehicle_state_.y() - end_point.path_point().y()) *
                      (vehicle_state_.y() - end_point.path_point().y())) <
            planner_open_space_config_.max_position_error_to_end_point() &&
        std::abs(vehicle_state_.heading() - end_point.path_point().theta()) <
            planner_open_space_config_.max_theta_error_to_end_point() &&
        (current_trajectory_index_ <
         trajectory_partition_.adc_trajectory_size() - 1)) {
      current_trajectory_index_ += 1;
    }

    // 4. Collision check for updated trajectory, if pass, update frame, else,
    // return error status

    if (IsCollisionFreeTrajectory(current_trajectory_)) {
      frame->mutable_trajectory()->CopyFrom(current_trajectory_);
      return Status::OK();
    } else {
      // If collision happens, return wrong planning status and estop
      // trajectory would be sent in std planning
      return Status(ErrorCode::PLANNING_ERROR, "Collision Check failed");
    }
  }
}

void OpenSpacePlanner::GenerateTrajectoryThread() {
  while (!is_stop_) {
    {
      std::lock_guard<std::mutex> lock(open_space_mutex_);
      trajectory_updated_ = false;
      if (open_space_trajectory_generator_->Plan(
              vehicle_state_, XYbounds_, rotate_angle_, translate_origin_,
              end_pose_, obstacles_num_, obstacles_edges_num_, obstacles_A_,
              obstacles_b_, obstalce_list_) == Status::OK()) {
        trajectory_updated_ = true;
      }
    }
  }
}

void OpenSpacePlanner::Stop() {
  is_stop_ = true;
  if (FLAGS_enable_open_space_planner_thread) {
    task_future_.get();
  }
}

bool OpenSpacePlanner::IsCollisionFreeTrajectory(
    const ADCTrajectory& adc_trajectory) {
  CHECK_LE(adc_trajectory.trajectory_point().size(),
           predicted_bounding_rectangles_.size());
  const auto& vehicle_config =
      common::VehicleConfigHelper::Instance()->GetConfig();
  double ego_length = vehicle_config.vehicle_param().length();
  double ego_width = vehicle_config.vehicle_param().width();

  for (int i = 0; i < adc_trajectory.trajectory_point().size(); ++i) {
    const auto& trajectory_point = adc_trajectory.trajectory_point(i);
    double ego_theta = trajectory_point.path_point().theta();
    Box2d ego_box(
        {trajectory_point.path_point().x(), trajectory_point.path_point().y()},
        ego_theta, ego_length, ego_width);
    double shift_distance =
        ego_length / 2.0 - vehicle_config.vehicle_param().back_edge_to_center();
    Vec2d shift_vec{shift_distance * std::cos(ego_theta),
                    shift_distance * std::sin(ego_theta)};
    ego_box.Shift(shift_vec);

    for (const auto& obstacle_box : predicted_bounding_rectangles_[i]) {
      if (ego_box.HasOverlap(obstacle_box)) {
        return false;
      }
    }
  }
  return true;
}

void OpenSpacePlanner::BuildPredictedEnvironment(
    const std::vector<const Obstacle*>& obstacles) {
  CHECK(predicted_bounding_rectangles_.empty());
  double relative_time = 0.0;
  while (relative_time < FLAGS_trajectory_time_length) {
    std::vector<Box2d> predicted_env;
    for (const Obstacle* obstacle : obstacles) {
      TrajectoryPoint point = obstacle->GetPointAtTime(relative_time);
      Box2d box = obstacle->GetBoundingBox(point);
      predicted_env.push_back(std::move(box));
    }
    predicted_bounding_rectangles_.push_back(std::move(predicted_env));
    relative_time += FLAGS_trajectory_time_resolution;
  }
}

}  // namespace planning
}  // namespace apollo
<|endoftext|>


<commit_before>#include "umundo/core.h"
#include <iostream>

using namespace umundo;

bool testRecursiveMutex() {
	Mutex mutex;
	UMUNDO_LOCK(mutex);
	if(!mutex.tryLock()) {
		LOG_ERR("tryLock should be possible from within the same thread");
		assert(false);
	}
	UMUNDO_LOCK(mutex);
	// can we unlock it as well?
	UMUNDO_UNLOCK(mutex);
	UMUNDO_UNLOCK(mutex);
	return true;
}

static Mutex testMutex;
bool testThreads() {
	class Thread1 : public Thread {
		void run() {
			if(testMutex.tryLock()) {
				LOG_ERR("tryLock should return false with a mutex locked in another thread");
				assert(false);
			}
			UMUNDO_LOCK(testMutex); // blocks
			Thread::sleepMs(50);
			UMUNDO_UNLOCK(testMutex);
			Thread::sleepMs(100);
		}
	};

	/**
	 * tl=tryLock, l=lock, u=unlock b=block, sN=sleep(N)
	 * thread1:        start tl l  l s50      u
	 *    main: start l s50       u s20  tl    l join
	 *    t->          0         50     70   100
	 */

	UMUNDO_LOCK(testMutex);
	Thread1 thread1;
	thread1.start();
	Thread::sleepMs(50); // thread1 will trylock and block on lock
	UMUNDO_UNLOCK(testMutex);  // unlock
	Thread::sleepMs(20); // yield cpu and sleep
	// thread1 sleeps with lock on mutex
	if(testMutex.tryLock()) {
		LOG_ERR("tryLock should return false with a mutex locked in another thread");
		assert(false);
	}
	UMUNDO_LOCK(testMutex);    // thread1 will unlock and sleep
	thread1.join();      // join with thread1
	if(thread1.isStarted()) {
		LOG_ERR("thread still running after join");
		assert(false);
	}
	return true;
}

static Monitor testMonitor;
static int passedMonitor = 0;
bool testMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testMonitor.wait();
			Thread::sleepMs(10); // avoid clash with other threads
			passedMonitor++;
		}
	};

	TestThread thread1(0);
	TestThread thread2(5);
	TestThread thread3(10);

	for (int i = 0; i < 10; i++) {
		passedMonitor = 0;

		// all will block on monitor
		thread1.start();
		thread2.start();
		thread3.start();
		Thread::sleepMs(5); // give threads a chance to run into wait
		if(passedMonitor != 0) {
			LOG_ERR("%d threads already passed the monitor", passedMonitor);
			assert(false);
		}
		UMUNDO_SIGNAL(testMonitor); // signal a single thread
		Thread::sleepMs(15); // thread will increase passedMonitor
		if(passedMonitor != 1) {
			LOG_ERR("Expected 1 threads to pass the monitor, but %d did", passedMonitor);
			assert(false);
		}
		UMUNDO_BROADCAST(testMonitor); // signal all other threads
		Thread::sleepMs(15);

		if (thread1.isStarted() || thread2.isStarted() || thread3.isStarted()) {
			LOG_ERR("Threads ran to completion but still insist on being started");
			assert(false);
		}
	}
	return true;
}

static Monitor testTimedMonitor;
static int passedTimedMonitor = 0;
bool testTimedMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testTimedMonitor.wait(_ms);
			passedTimedMonitor++;
		}
	};

	TestThread thread1(15);
	TestThread thread2(0); // waits forever
	TestThread thread3(0); // waits forever
	TestThread thread4(0); // waits forever
	TestThread thread5(0); // waits forever

	for (int i = 0; i < 50; i++) {
		// test waiting for a given time
		passedTimedMonitor = 0;
		thread1.start(); // wait for 15ms at mutex before resuming
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 0); // thread1 should not have passed
		Thread::sleepMs(25);
		assert(passedTimedMonitor == 1); // thread1 should have passed
		assert(!thread1.isStarted());

		// test signalling a set of threads
		passedTimedMonitor = 0;
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		Thread::sleepMs(5);
		testTimedMonitor.signal(2); // signal 2 threads
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 2);
		testTimedMonitor.signal(1); // signal another thread
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 3);
		testTimedMonitor.broadcast(); // signal last thread
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 4);
		assert(!thread2.isStarted() && !thread3.isStarted() && !thread4.isStarted() && !thread5.isStarted());

		// test timed and unlimited waiting
		passedTimedMonitor = 0;
		thread1.start();
		thread2.start(); // with another thread
		thread3.start(); // with another thread
		Thread::sleepMs(5);
		testTimedMonitor.signal(); // explicit signal
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 1);
		// wo do not know which thread passed
		assert(!thread1.isStarted() || !thread2.isStarted() || !thread3.isStarted());
		if (thread1.isStarted()) {
			// thread1 is still running, just wait
			Thread::sleepMs(10);
			assert(passedTimedMonitor == 2);
		}
		testTimedMonitor.broadcast(); // explicit signal
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 3);
		assert(!thread1.isStarted() && !thread2.isStarted() && !thread3.isStarted());

		// test signalling prior to waiting
		passedTimedMonitor = 0;
		testTimedMonitor.signal();
		thread1.start();
		Thread::sleepMs(5);
		thread2.start();
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 1);
		assert(!thread1.isStarted());
		assert(thread2.isStarted());
		testTimedMonitor.signal();
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 2);

		assert(!thread1.isStarted());
		assert(!thread2.isStarted());
		assert(!thread3.isStarted());

	}
	return true;
}

class FooTracer : public Traceable, public Thread {
	void run() {
		while(isStarted()) {
			trace("This is foo");
			Thread::sleepMs(20);
		}
	}
};

bool testTracing() {
	FooTracer* tr1 = new FooTracer();
	FooTracer* tr2 = new FooTracer();
	FooTracer* tr3 = new FooTracer();

	tr1->setTraceFile("trace.txt");
	tr2->setTraceFile("trace.txt");
	tr3->setTraceFile("trace.txt");

	tr1->start();
	tr2->start();
	tr3->start();

	Thread::sleepMs(100);
	delete tr1;
	delete tr2;
	delete tr3;

	return true;
}

int main(int argc, char** argv) {
	if(!testTracing())
		return EXIT_FAILURE;
	if(!testRecursiveMutex())
		return EXIT_FAILURE;
	if(!testThreads())
		return EXIT_FAILURE;
	if(!testMonitors())
		return EXIT_FAILURE;
	if(!testTimedMonitors())
		return EXIT_FAILURE;
}
<commit_msg>Adapted timeouts<commit_after>#include "umundo/core.h"
#include <iostream>

using namespace umundo;

bool testRecursiveMutex() {
	Mutex mutex;
	UMUNDO_LOCK(mutex);
	if(!mutex.tryLock()) {
		LOG_ERR("tryLock should be possible from within the same thread");
		assert(false);
	}
	UMUNDO_LOCK(mutex);
	// can we unlock it as well?
	UMUNDO_UNLOCK(mutex);
	UMUNDO_UNLOCK(mutex);
	return true;
}

static Mutex testMutex;
bool testThreads() {
	class Thread1 : public Thread {
		void run() {
			if(testMutex.tryLock()) {
				LOG_ERR("tryLock should return false with a mutex locked in another thread");
				assert(false);
			}
			UMUNDO_LOCK(testMutex); // blocks
			Thread::sleepMs(50);
			UMUNDO_UNLOCK(testMutex);
			Thread::sleepMs(100);
		}
	};

	/**
	 * tl=tryLock, l=lock, u=unlock b=block, sN=sleep(N)
	 * thread1:        start tl l  l s50      u
	 *    main: start l s50       u s20  tl    l join
	 *    t->          0         50     70   100
	 */

	UMUNDO_LOCK(testMutex);
	Thread1 thread1;
	thread1.start();
	Thread::sleepMs(50); // thread1 will trylock and block on lock
	UMUNDO_UNLOCK(testMutex);  // unlock
	Thread::sleepMs(20); // yield cpu and sleep
	// thread1 sleeps with lock on mutex
	if(testMutex.tryLock()) {
		LOG_ERR("tryLock should return false with a mutex locked in another thread");
		assert(false);
	}
	UMUNDO_LOCK(testMutex);    // thread1 will unlock and sleep
	thread1.join();      // join with thread1
	if(thread1.isStarted()) {
		LOG_ERR("thread still running after join");
		assert(false);
	}
	return true;
}

static Monitor testMonitor;
static int passedMonitor = 0;
bool testMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testMonitor.wait();
			Thread::sleepMs(10); // avoid clash with other threads
			passedMonitor++;
		}
	};

	TestThread thread1(0);
	TestThread thread2(5);
	TestThread thread3(10);

	for (int i = 0; i < 10; i++) {
		passedMonitor = 0;

		// all will block on monitor
		thread1.start();
		thread2.start();
		thread3.start();
		Thread::sleepMs(5); // give threads a chance to run into wait
		if(passedMonitor != 0) {
			LOG_ERR("%d threads already passed the monitor", passedMonitor);
			assert(false);
		}
		UMUNDO_SIGNAL(testMonitor); // signal a single thread
		Thread::sleepMs(15); // thread will increase passedMonitor
		if(passedMonitor != 1) {
			LOG_ERR("Expected 1 threads to pass the monitor, but %d did", passedMonitor);
			assert(false);
		}
		UMUNDO_BROADCAST(testMonitor); // signal all other threads
		Thread::sleepMs(15);

		if (thread1.isStarted() || thread2.isStarted() || thread3.isStarted()) {
			LOG_ERR("Threads ran to completion but still insist on being started");
			assert(false);
		}
	}
	return true;
}

static Monitor testTimedMonitor;
static int passedTimedMonitor = 0;
bool testTimedMonitors() {
	struct TestThread : public Thread {
		int _ms;
		TestThread(int ms) : _ms(ms) {}
		void run() {
			testTimedMonitor.wait(_ms);
			passedTimedMonitor++;
		}
	};

	TestThread thread1(15);
	TestThread thread2(0); // waits forever
	TestThread thread3(0); // waits forever
	TestThread thread4(0); // waits forever
	TestThread thread5(0); // waits forever

	for (int i = 0; i < 50; i++) {
		// test waiting for a given time
		passedTimedMonitor = 0;
		thread1.start(); // wait for 15ms at mutex before resuming
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 0); // thread1 should not have passed
		Thread::sleepMs(25);
		assert(passedTimedMonitor == 1); // thread1 should have passed
		assert(!thread1.isStarted());

		// test signalling a set of threads
		passedTimedMonitor = 0;
		thread2.start();
		thread3.start();
		thread4.start();
		thread5.start();
		Thread::sleepMs(5);
		testTimedMonitor.signal(2); // signal 2 threads
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 2);
		testTimedMonitor.signal(1); // signal another thread
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 3);
		testTimedMonitor.broadcast(); // signal last thread
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 4);
		assert(!thread2.isStarted() && !thread3.isStarted() && !thread4.isStarted() && !thread5.isStarted());

		// test timed and unlimited waiting
		passedTimedMonitor = 0;
		thread1.start();
		thread2.start(); // with another thread
		thread3.start(); // with another thread
		Thread::sleepMs(5);
		testTimedMonitor.signal(); // explicit signal
		Thread::sleepMs(10);
		assert(passedTimedMonitor == 1);
		// wo do not know which thread passed
		assert(!thread1.isStarted() || !thread2.isStarted() || !thread3.isStarted());
		if (thread1.isStarted()) {
			// thread1 is still running, just wait
			Thread::sleepMs(10);
			assert(passedTimedMonitor == 2);
		}
		testTimedMonitor.broadcast(); // explicit signal
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 3);
		assert(!thread1.isStarted() && !thread2.isStarted() && !thread3.isStarted());

		// test signalling prior to waiting
		passedTimedMonitor = 0;
		testTimedMonitor.signal();
		thread1.start();
		Thread::sleepMs(5);
		thread2.start();
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 1);
		assert(!thread1.isStarted());
		assert(thread2.isStarted());
		testTimedMonitor.signal();
		Thread::sleepMs(5);
		assert(passedTimedMonitor == 2);

		assert(!thread1.isStarted());
		assert(!thread2.isStarted());
		assert(!thread3.isStarted());

	}
	return true;
}

class FooTracer : public Traceable, public Thread {
	void run() {
		while(isStarted()) {
			trace("This is foo");
			Thread::sleepMs(20);
		}
	}
};

bool testTracing() {
	FooTracer* tr1 = new FooTracer();
	FooTracer* tr2 = new FooTracer();
	FooTracer* tr3 = new FooTracer();

	tr1->setTraceFile("trace.txt");
	tr2->setTraceFile("trace.txt");
	tr3->setTraceFile("trace.txt");

	tr1->start();
	tr2->start();
	tr3->start();

	Thread::sleepMs(100);
	delete tr1;
	delete tr2;
	delete tr3;

	return true;
}

int main(int argc, char** argv) {
	if(!testTracing())
		return EXIT_FAILURE;
	if(!testRecursiveMutex())
		return EXIT_FAILURE;
	if(!testThreads())
		return EXIT_FAILURE;
	if(!testMonitors())
		return EXIT_FAILURE;
	if(!testTimedMonitors())
		return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2011, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: Manuel Holtgrewe <manuel.holtgrewe@fu-berlin.de>
// ==========================================================================

#include <seqan/basic.h>
#include <seqan/file.h>

#include "test_bam_alignment_record.h"
#include "test_bam_header_record.h"
#include "test_bam_io_context.h"
#include "test_bam_sam_conversion.h"
#include "test_bam_tags_dict.h"
#include "test_read_sam.h"
#include "test_write_sam.h"
#if SEQAN_HAS_ZLIB
#include "test_read_bam.h"
#include "test_write_bam.h"
#include "test_bam_index.h"
#endif  // #if SEQAN_HAS_ZLIB
#include "test_bam_stream.h"

SEQAN_BEGIN_TESTSUITE(test_bam_io)
{
    // Test BamAlignmentRecord class.
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_class);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_multiple);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_unmapped);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_next_unmapped);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_rc);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_next_rc);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_first);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_last);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_secondary);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_qc_no_pass);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_duplicate);

    // Test BamHeader and BamHeaderRecord classes.
    SEQAN_CALL_TEST(test_bam_io_bam_header_class);
    SEQAN_CALL_TEST(test_bam_io_bam_header_typedefs);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_class);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_typedefs);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_find_tag_key);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_get_tag_value);

    // Test BamIoContext.
    SEQAN_CALL_TEST(test_bam_io_bam_io_context_standalone);
    SEQAN_CALL_TEST(test_bam_io_bam_io_context_fragment_store);

    // Test BAM<->SAM tag conversion.
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_two_tags);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_A);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_c);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_C);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_s);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_S);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_i);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_I);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_f);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Z);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_H);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bc);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_BC);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bs);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_BS);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bi);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_BI);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bf);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_two_tags);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_A);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_i);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_f);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Z);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_H);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bc);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_BC);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bs);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_BS);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bi);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_BI);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bf);

    // Test BamTagsDict.
    SEQAN_CALL_TEST(test_bam_tags_dict_get_type_size);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_type);
    SEQAN_CALL_TEST(test_bam_tags_dict_length);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_A);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_c);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_C);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_s);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_S);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_i);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_I);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_A);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_c);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_C);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_s);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_S);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_i);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_I);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_f);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Z);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_H);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bc);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_BC);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bs);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_BS);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bi);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_BI);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bf);
    SEQAN_CALL_TEST(test_bam_tags_dict_erase_tag);
    SEQAN_CALL_TEST(test_bam_tags_dict_set_tag_value);

    // Test SAM I/O.
    SEQAN_CALL_TEST(test_bam_io_sam_read_header);
    SEQAN_CALL_TEST(test_bam_io_sam_read_alignment);
    SEQAN_CALL_TEST(test_bam_io_sam_write_header);
    SEQAN_CALL_TEST(test_bam_io_sam_write_alignment);

    // Test BAM I/O.
#if SEQAN_HAS_ZLIB
    SEQAN_CALL_TEST(test_bam_io_bam_read_header);
    SEQAN_CALL_TEST(test_bam_io_bam_read_alignment);
    SEQAN_CALL_TEST(test_bam_io_bam_write_header);
    SEQAN_CALL_TEST(test_bam_io_bam_write_alignment);

    // Test BAM indices.
    SEQAN_CALL_TEST(test_bam_io_bam_index_bai);
#endif  // #if SEQAN_HAS_ZLIB

    // Test BamStream class.
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_file_size);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_read_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_read_records);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_write_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_write_records);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_file_size);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_read_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_read_records);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_write_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_write_records);

    exit(1);
}
SEQAN_END_TESTSUITE
<commit_msg>[FIX] Fixing error in test.<commit_after>// ==========================================================================
//                 SeqAn - The Library for Sequence Analysis
// ==========================================================================
// Copyright (c) 2006-2011, Knut Reinert, FU Berlin
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Knut Reinert or the FU Berlin nor the names of
//       its contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
//
// ==========================================================================
// Author: Manuel Holtgrewe <manuel.holtgrewe@fu-berlin.de>
// ==========================================================================

#include <seqan/basic.h>
#include <seqan/file.h>

#include "test_bam_alignment_record.h"
#include "test_bam_header_record.h"
#include "test_bam_io_context.h"
#include "test_bam_sam_conversion.h"
#include "test_bam_tags_dict.h"
#include "test_read_sam.h"
#include "test_write_sam.h"
#if SEQAN_HAS_ZLIB
#include "test_read_bam.h"
#include "test_write_bam.h"
#include "test_bam_index.h"
#endif  // #if SEQAN_HAS_ZLIB
#include "test_bam_stream.h"

SEQAN_BEGIN_TESTSUITE(test_bam_io)
{
    // Test BamAlignmentRecord class.
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_class);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_multiple);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_unmapped);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_next_unmapped);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_rc);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_next_rc);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_first);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_last);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_secondary);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_qc_no_pass);
    SEQAN_CALL_TEST(test_bam_io_bam_alignment_record_has_flag_duplicate);

    // Test BamHeader and BamHeaderRecord classes.
    SEQAN_CALL_TEST(test_bam_io_bam_header_class);
    SEQAN_CALL_TEST(test_bam_io_bam_header_typedefs);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_class);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_typedefs);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_find_tag_key);
    SEQAN_CALL_TEST(test_bam_io_bam_header_record_get_tag_value);

    // Test BamIoContext.
    SEQAN_CALL_TEST(test_bam_io_bam_io_context_standalone);
    SEQAN_CALL_TEST(test_bam_io_bam_io_context_fragment_store);

    // Test BAM<->SAM tag conversion.
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_two_tags);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_A);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_c);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_C);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_s);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_S);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_i);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_I);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_f);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Z);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_H);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bc);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_BC);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bs);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_BS);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bi);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_BI);
    SEQAN_CALL_TEST(test_assign_tags_bam_to_sam_type_Bf);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_two_tags);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_A);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_i);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_f);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Z);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_H);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bc);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_BC);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bs);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_BS);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bi);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_BI);
    SEQAN_CALL_TEST(test_assign_tags_sam_to_bam_type_Bf);

    // Test BamTagsDict.
    SEQAN_CALL_TEST(test_bam_tags_dict_get_type_size);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_type);
    SEQAN_CALL_TEST(test_bam_tags_dict_length);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_A);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_c);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_C);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_s);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_S);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_i);
    SEQAN_CALL_TEST(test_bam_tags_dict_extract_value_type_I);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_A);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_c);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_C);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_s);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_S);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_i);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_I);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_f);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Z);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_H);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bc);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_BC);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bs);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_BS);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bi);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_BI);
    SEQAN_CALL_TEST(test_bam_tags_dict_get_value_type_Bf);
    SEQAN_CALL_TEST(test_bam_tags_dict_erase_tag);
    SEQAN_CALL_TEST(test_bam_tags_dict_set_tag_value);

    // Test SAM I/O.
    SEQAN_CALL_TEST(test_bam_io_sam_read_header);
    SEQAN_CALL_TEST(test_bam_io_sam_read_alignment);
    SEQAN_CALL_TEST(test_bam_io_sam_write_header);
    SEQAN_CALL_TEST(test_bam_io_sam_write_alignment);

    // Test BAM I/O.
#if SEQAN_HAS_ZLIB
    SEQAN_CALL_TEST(test_bam_io_bam_read_header);
    SEQAN_CALL_TEST(test_bam_io_bam_read_alignment);
    SEQAN_CALL_TEST(test_bam_io_bam_write_header);
    SEQAN_CALL_TEST(test_bam_io_bam_write_alignment);

    // Test BAM indices.
    SEQAN_CALL_TEST(test_bam_io_bam_index_bai);
#endif  // #if SEQAN_HAS_ZLIB

    // Test BamStream class.
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_file_size);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_read_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_read_records);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_write_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_sam_write_records);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_file_size);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_read_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_read_records);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_write_header);
    SEQAN_CALL_TEST(test_bam_io_bam_stream_bam_write_records);
}
SEQAN_END_TESTSUITE
<|endoftext|>


<commit_before>#include <array>
#include <cmath>
#include "Basics/MathTools.h"
#include "const-brick-iterator.h"

namespace tuvok {

// converts a 3D index ('loc') into a 1D index.
static uint64_t to1d(const std::array<uint64_t,3>& loc,
                     const std::array<uint64_t,3>& size) {
  return loc[2]*size[1]*size[0] + loc[1]*size[0] + loc[0];
}

// just converts a 3-element array into a UINTVECTOR3.
static UINTVECTOR3 va(const std::array<uint32_t,3>& a) {
  return UINTVECTOR3(a[0], a[1], a[2]);
}

static unsigned ghost() { return 4; }

/// @param l the current location, in brick coords
/// @param bsz size of the bricks
/// @param voxels number of voxels
/// @returns the number of voxels 'l' has.  this is normally 'bsz', but can be
///          smaller when the 'l' abuts the side of a dimension
static std::array<uint32_t,3> nvoxels(const std::array<uint64_t,3> l,
                                      const std::array<unsigned,3> bsz,
                                      const std::array<uint64_t,3> voxels) {
  // the brick starts at 'v' and goes to 'v+bricksize'.  But 'v+bricksize'
  // might exceed beyond the bounds of the domain ('voxels'), and therefore
  // it should shrink.
  std::array<uint64_t,3> v = {{
    l[0] * bsz[0], l[1] * bsz[1], l[2] * bsz[2]
  }};
  const std::array<uint32_t,3> difference = {{
    static_cast<uint32_t>(voxels[0] - v[0]),
    static_cast<uint32_t>(voxels[1] - v[1]),
    static_cast<uint32_t>(voxels[2] - v[2])
  }};
  // get a uint32 version so we don't have to cast w/ e.g. min below.
  const std::array<uint32_t,3> bs = {{
    static_cast<uint32_t>(bsz[0]),
    static_cast<uint32_t>(bsz[1]),
    static_cast<uint32_t>(bsz[2]),
  }};
  std::array<uint32_t,3> nvox = {{
    difference[0] ? std::min(bs[0], difference[0]) : bs[0],
    difference[1] ? std::min(bs[1], difference[1]) : bs[1],
    difference[2] ? std::min(bs[2], difference[2]) : bs[2],
  }};

  return nvox;
}

// The dimension with the largest ratio of size-to-bricksize
#define DIM \
    (vox[0] / bsize[0] > vox[1] / bsize[1] ? \
      (vox[0] / bsize[0] > vox[2] / bsize[2] ? 0 : 2) \
    : (vox[1] / bsize[1] > vox[2] / bsize[2] ? 1 : 2))
const_brick_iterator::const_brick_iterator(
  const std::array<uint64_t,3>& vox,
  const std::array<unsigned,3>& bricksize,
  const std::array<std::array<float,3>,2>& exts
) : bsize(bricksize),
    // a bit intense for an initializer list, but it needs to be here so this
    // can be const.
    MaxLODs(static_cast<size_t>(ceil(double(vox[DIM]) / bricksize[DIM]))),
    voxels(vox), LOD(0), extents(exts)
#undef DIM
{
  location[0] = location[1] = location[2] = 1ULL;
}

/// gives the brick layout for a given decomposition. i.e. the number of bricks
/// in each dimension
static std::array<uint64_t,3> layout(const std::array<uint64_t,3> voxels,
                                     const std::array<unsigned,3> bsize) {
  std::array<uint64_t,3> tmp = {{
    static_cast<uint64_t>(ceil(static_cast<float>(voxels[0]) / bsize[0])),
    static_cast<uint64_t>(ceil(static_cast<float>(voxels[1]) / bsize[1])),
    static_cast<uint64_t>(ceil(static_cast<float>(voxels[2]) / bsize[2])),
  }};
  return tmp;
}

const_brick_iterator& const_brick_iterator::advance() {
  // the layout for this level
  const std::array<uint64_t,3> ly = layout(this->voxels, this->bsize);

  // increment the x coord. if it goes beyond the end, then switch to
  // Y... and so on.
  this->location[0]++;
  if(this->location[0] > ly[0]) {
    this->location[0] = 1;
    this->location[1]++;
  }
  if(this->location[1] > ly[1]) {
    this->location[1] = 1;
    this->location[2]++;
  }
  if(this->location[2] > ly[2]) {
    this->location[2] = 1;
    this->LOD++;
    // we also have to decrease the number of voxels for the new level
    if(this->voxels[0] > this->bsize[0]) { this->voxels[0] = voxels[0] /= 2; }
    if(this->voxels[1] > this->bsize[1]) { this->voxels[1] = voxels[1] /= 2; }
    if(this->voxels[2] > this->bsize[2]) { this->voxels[2] = voxels[2] /= 2; }
  }
  if(this->LOD >= this->MaxLODs) {
    // invalidate the iterator.
    this->voxels[0] = this->voxels[1] = this->voxels[2] = 0ULL;
    this->location[0] = this->location[1] = this->location[2] = 0ULL;
  }
  return *this;
}

const_brick_iterator& const_brick_iterator::operator++() {
  return this->advance();
}

const std::pair<BrickKey, BrickMD> const_brick_iterator::dereference() const {
  const size_t timestep = 0; // unsupported/unimplemented.
  std::array<uint64_t,3> loc_sub1 = {{
    this->location[0] - 1, this->location[1] - 1, this->location[2] - 1
  }};
  uint64_t index = to1d(
    loc_sub1, layout(this->voxels, this->bsize)
  );
  BrickKey bk(timestep, this->LOD, index);

  FLOATVECTOR3 exts, center;
  { // what percentage of the way through the volume are we?
    const std::array<uint64_t,3> vlow = {{
      loc_sub1[0] * this->bsize[0],
      loc_sub1[1] * this->bsize[1],
      loc_sub1[2] * this->bsize[2]
    }};
    // the high coord is the low coord + the number of voxels in the brick
    const std::array<uint32_t,3> bsz = nvoxels(this->location, this->bsize,
                                               this->voxels);
    const std::array<uint64_t,3> vhigh = {{
      vlow[0]+bsz[0], vlow[1]+bsz[1], vlow[2]+bsz[2]
    }};
    // where the center of this brick would be, in voxels.  note this is FP:
    // the 'center' could be a half-voxel in, if the brick has an odd number of
    // voxels.
    const std::array<float,3> vox_center = {{
      ((vhigh[0]-vlow[0])/2.0f) + vlow[0],
      ((vhigh[1]-vlow[1])/2.0f) + vlow[1],
      ((vhigh[2]-vlow[2])/2.0f) + vlow[2],
    }};
    const std::array<float,3> voxelsf = {{ // for type purposes.
      static_cast<float>(this->voxels[0]), static_cast<float>(this->voxels[1]),
      static_cast<float>(this->voxels[2])
    }};
    assert(vox_center[0] < voxelsf[0]);
    assert(vox_center[1] < voxelsf[1]);
    assert(vox_center[2] < voxelsf[2]);
    // we know the center in voxels, and we know the width of the domain in
    // world space.  interpolate to get the center in world space.
    center = FLOATVECTOR3(
      MathTools::lerp(vox_center[0], 0.0f,voxelsf[0], this->extents[0][0],
                                                      this->extents[1][0]),
      MathTools::lerp(vox_center[1], 0.0f,voxelsf[1], this->extents[0][1],
                                                      this->extents[1][1]),
      MathTools::lerp(vox_center[2], 0.0f,voxelsf[2], this->extents[0][2],
                                                      this->extents[1][2])
    );
    assert(extents[0][0] <= center[0] && center[0] <= extents[1][0]);
    assert(extents[0][1] <= center[1] && center[1] <= extents[1][1]);
    assert(extents[0][2] <= center[2] && center[2] <= extents[1][2]);
    const uint64_t zero = 0ULL; // for type purposes.
    const FLOATVECTOR3 wlow(
      MathTools::lerp(vlow[0], zero,this->voxels[0],
                      this->extents[0][0],this->extents[1][0]),
      MathTools::lerp(vlow[1], zero,this->voxels[1],
                      this->extents[0][1],this->extents[1][1]),
      MathTools::lerp(vlow[2], zero,this->voxels[2],
                      this->extents[0][2],this->extents[1][2])
    );
    const FLOATVECTOR3 whigh(
      MathTools::lerp(vhigh[0], zero,this->voxels[0],
                      this->extents[0][0],this->extents[1][0]),
      MathTools::lerp(vhigh[1], zero,this->voxels[1],
                      this->extents[0][1],this->extents[1][1]),
      MathTools::lerp(vhigh[2], zero,this->voxels[2],
                      this->extents[0][2],this->extents[1][2])
    );
    exts = FLOATVECTOR3(whigh[0]-wlow[0], whigh[1]-wlow[1], whigh[2]-wlow[2]);
    assert(exts[0] <= (this->extents[1][0]-this->extents[0][0]));
    assert(exts[1] <= (this->extents[1][1]-this->extents[0][1]));
    assert(exts[2] <= (this->extents[1][2]-this->extents[0][2]));
  }

  UINTVECTOR3 nvox = va(nvoxels(this->location, this->bsize, this->voxels));
  nvox += ghost(); // add in the ghost data on each brick!
  BrickMD md = { center, exts, nvox };
  return std::make_pair(bk, md);
}
const std::pair<BrickKey, BrickMD> const_brick_iterator::operator*() const {
  return this->dereference();
}

bool const_brick_iterator::equals(const const_brick_iterator& iter) const {
  return this->location == iter.location;
}
bool const_brick_iterator::operator==(const const_brick_iterator& that) const {
  return this->equals(that);
}
bool const_brick_iterator::operator!=(const const_brick_iterator& that) const {
  return !this->equals(that);
}

const_brick_iterator begin(const std::array<uint64_t,3>& voxels,
                           const std::array<unsigned,3>& bricksize,
                           const std::array<std::array<float,3>,2>& extents) {
  return const_brick_iterator(voxels, bricksize, extents);
}
const_brick_iterator end() { return const_brick_iterator(); }

}
/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2013 IVDA Group

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/
<commit_msg>Use 0-based location when computing voxels!<commit_after>#include <array>
#include <cmath>
#include "Basics/MathTools.h"
#include "const-brick-iterator.h"

namespace tuvok {

// converts a 3D index ('loc') into a 1D index.
static uint64_t to1d(const std::array<uint64_t,3>& loc,
                     const std::array<uint64_t,3>& size) {
  return loc[2]*size[1]*size[0] + loc[1]*size[0] + loc[0];
}

// just converts a 3-element array into a UINTVECTOR3.
static UINTVECTOR3 va(const std::array<uint32_t,3>& a) {
  return UINTVECTOR3(a[0], a[1], a[2]);
}

static unsigned ghost() { return 4; }

/// @param l the current location, in brick coords
/// @param bsz size of the bricks
/// @param voxels number of voxels
/// @returns the number of voxels 'l' has.  this is normally 'bsz', but can be
///          smaller when the 'l' abuts the side of a dimension
static std::array<uint32_t,3> nvoxels(const std::array<uint64_t,3> l,
                                      const std::array<unsigned,3> bsz,
                                      const std::array<uint64_t,3> voxels) {
  // the brick starts at 'v' and goes to 'v+bricksize'.  But 'v+bricksize'
  // might exceed beyond the bounds of the domain ('voxels'), and therefore
  // it should shrink.
  std::array<uint64_t,3> v = {{
    l[0] * bsz[0], l[1] * bsz[1], l[2] * bsz[2]
  }};
  const std::array<uint32_t,3> difference = {{
    static_cast<uint32_t>(voxels[0] - v[0]),
    static_cast<uint32_t>(voxels[1] - v[1]),
    static_cast<uint32_t>(voxels[2] - v[2])
  }};
  // get a uint32 version so we don't have to cast w/ e.g. min below.
  const std::array<uint32_t,3> bs = {{
    static_cast<uint32_t>(bsz[0]),
    static_cast<uint32_t>(bsz[1]),
    static_cast<uint32_t>(bsz[2]),
  }};
  std::array<uint32_t,3> nvox = {{
    difference[0] ? std::min(bs[0], difference[0]) : bs[0],
    difference[1] ? std::min(bs[1], difference[1]) : bs[1],
    difference[2] ? std::min(bs[2], difference[2]) : bs[2],
  }};

  return nvox;
}

// The dimension with the largest ratio of size-to-bricksize
#define DIM \
    (vox[0] / bsize[0] > vox[1] / bsize[1] ? \
      (vox[0] / bsize[0] > vox[2] / bsize[2] ? 0 : 2) \
    : (vox[1] / bsize[1] > vox[2] / bsize[2] ? 1 : 2))
const_brick_iterator::const_brick_iterator(
  const std::array<uint64_t,3>& vox,
  const std::array<unsigned,3>& bricksize,
  const std::array<std::array<float,3>,2>& exts
) : bsize(bricksize),
    // a bit intense for an initializer list, but it needs to be here so this
    // can be const.
    MaxLODs(static_cast<size_t>(ceil(double(vox[DIM]) / bricksize[DIM]))),
    voxels(vox), LOD(0), extents(exts)
#undef DIM
{
  location[0] = location[1] = location[2] = 1ULL;
}

/// gives the brick layout for a given decomposition. i.e. the number of bricks
/// in each dimension
static std::array<uint64_t,3> layout(const std::array<uint64_t,3> voxels,
                                     const std::array<unsigned,3> bsize) {
  std::array<uint64_t,3> tmp = {{
    static_cast<uint64_t>(ceil(static_cast<float>(voxels[0]) / bsize[0])),
    static_cast<uint64_t>(ceil(static_cast<float>(voxels[1]) / bsize[1])),
    static_cast<uint64_t>(ceil(static_cast<float>(voxels[2]) / bsize[2])),
  }};
  return tmp;
}

const_brick_iterator& const_brick_iterator::advance() {
  // the layout for this level
  const std::array<uint64_t,3> ly = layout(this->voxels, this->bsize);

  // increment the x coord. if it goes beyond the end, then switch to
  // Y... and so on.
  this->location[0]++;
  if(this->location[0] > ly[0]) {
    this->location[0] = 1;
    this->location[1]++;
  }
  if(this->location[1] > ly[1]) {
    this->location[1] = 1;
    this->location[2]++;
  }
  if(this->location[2] > ly[2]) {
    this->location[2] = 1;
    this->LOD++;
    // we also have to decrease the number of voxels for the new level
    if(this->voxels[0] > this->bsize[0]) { this->voxels[0] = voxels[0] /= 2; }
    if(this->voxels[1] > this->bsize[1]) { this->voxels[1] = voxels[1] /= 2; }
    if(this->voxels[2] > this->bsize[2]) { this->voxels[2] = voxels[2] /= 2; }
  }
  if(this->LOD >= this->MaxLODs) {
    // invalidate the iterator.
    this->voxels[0] = this->voxels[1] = this->voxels[2] = 0ULL;
    this->location[0] = this->location[1] = this->location[2] = 0ULL;
  }
  return *this;
}

const_brick_iterator& const_brick_iterator::operator++() {
  return this->advance();
}

const std::pair<BrickKey, BrickMD> const_brick_iterator::dereference() const {
  const size_t timestep = 0; // unsupported/unimplemented.
  std::array<uint64_t,3> loc_sub1 = {{
    this->location[0] - 1, this->location[1] - 1, this->location[2] - 1
  }};
  uint64_t index = to1d(
    loc_sub1, layout(this->voxels, this->bsize)
  );
  BrickKey bk(timestep, this->LOD, index);

  FLOATVECTOR3 exts, center;
  { // what percentage of the way through the volume are we?
    const std::array<uint64_t,3> vlow = {{
      loc_sub1[0] * this->bsize[0],
      loc_sub1[1] * this->bsize[1],
      loc_sub1[2] * this->bsize[2]
    }};
    // the high coord is the low coord + the number of voxels in the brick
    const std::array<uint32_t,3> bsz = nvoxels(loc_sub1, this->bsize,
                                               this->voxels);
    const std::array<uint64_t,3> vhigh = {{
      vlow[0]+bsz[0], vlow[1]+bsz[1], vlow[2]+bsz[2]
    }};
    // where the center of this brick would be, in voxels.  note this is FP:
    // the 'center' could be a half-voxel in, if the brick has an odd number of
    // voxels.
    const std::array<float,3> vox_center = {{
      ((vhigh[0]-vlow[0])/2.0f) + vlow[0],
      ((vhigh[1]-vlow[1])/2.0f) + vlow[1],
      ((vhigh[2]-vlow[2])/2.0f) + vlow[2],
    }};
    const std::array<float,3> voxelsf = {{ // for type purposes.
      static_cast<float>(this->voxels[0]), static_cast<float>(this->voxels[1]),
      static_cast<float>(this->voxels[2])
    }};
    assert(vox_center[0] < voxelsf[0]);
    assert(vox_center[1] < voxelsf[1]);
    assert(vox_center[2] < voxelsf[2]);
    // we know the center in voxels, and we know the width of the domain in
    // world space.  interpolate to get the center in world space.
    center = FLOATVECTOR3(
      MathTools::lerp(vox_center[0], 0.0f,voxelsf[0], this->extents[0][0],
                                                      this->extents[1][0]),
      MathTools::lerp(vox_center[1], 0.0f,voxelsf[1], this->extents[0][1],
                                                      this->extents[1][1]),
      MathTools::lerp(vox_center[2], 0.0f,voxelsf[2], this->extents[0][2],
                                                      this->extents[1][2])
    );
    assert(extents[0][0] <= center[0] && center[0] <= extents[1][0]);
    assert(extents[0][1] <= center[1] && center[1] <= extents[1][1]);
    assert(extents[0][2] <= center[2] && center[2] <= extents[1][2]);
    const uint64_t zero = 0ULL; // for type purposes.
    const FLOATVECTOR3 wlow(
      MathTools::lerp(vlow[0], zero,this->voxels[0],
                      this->extents[0][0],this->extents[1][0]),
      MathTools::lerp(vlow[1], zero,this->voxels[1],
                      this->extents[0][1],this->extents[1][1]),
      MathTools::lerp(vlow[2], zero,this->voxels[2],
                      this->extents[0][2],this->extents[1][2])
    );
    const FLOATVECTOR3 whigh(
      MathTools::lerp(vhigh[0], zero,this->voxels[0],
                      this->extents[0][0],this->extents[1][0]),
      MathTools::lerp(vhigh[1], zero,this->voxels[1],
                      this->extents[0][1],this->extents[1][1]),
      MathTools::lerp(vhigh[2], zero,this->voxels[2],
                      this->extents[0][2],this->extents[1][2])
    );
    exts = FLOATVECTOR3(whigh[0]-wlow[0], whigh[1]-wlow[1], whigh[2]-wlow[2]);
    assert(exts[0] <= (this->extents[1][0]-this->extents[0][0]));
    assert(exts[1] <= (this->extents[1][1]-this->extents[0][1]));
    assert(exts[2] <= (this->extents[1][2]-this->extents[0][2]));
  }

  UINTVECTOR3 nvox = va(nvoxels(loc_sub1, this->bsize, this->voxels));
  nvox += ghost(); // add in the ghost data on each brick!
  BrickMD md = { center, exts, nvox };
  return std::make_pair(bk, md);
}
const std::pair<BrickKey, BrickMD> const_brick_iterator::operator*() const {
  return this->dereference();
}

bool const_brick_iterator::equals(const const_brick_iterator& iter) const {
  return this->location == iter.location;
}
bool const_brick_iterator::operator==(const const_brick_iterator& that) const {
  return this->equals(that);
}
bool const_brick_iterator::operator!=(const const_brick_iterator& that) const {
  return !this->equals(that);
}

const_brick_iterator begin(const std::array<uint64_t,3>& voxels,
                           const std::array<unsigned,3>& bricksize,
                           const std::array<std::array<float,3>,2>& extents) {
  return const_brick_iterator(voxels, bricksize, extents);
}
const_brick_iterator end() { return const_brick_iterator(); }

}
/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2013 IVDA Group

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/
<|endoftext|>


<commit_before>// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && %deflake %run %t | FileCheck %s
// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && %deflake %run %t | FileCheck %s
#include "test.h"

volatile int X;
volatile int N;
void (*volatile F)();

static void foo() {
  if (--N == 0)
    X = 42;
  else
    F();
}

void *Thread(void *p) {
#ifdef ORDER1
  barrier_wait(&barrier);
#endif
  F();
#ifdef ORDER2
  barrier_wait(&barrier);
#endif
  return 0;
}

static size_t RoundUp(size_t n, size_t to) {
  return ((n + to - 1) / to) * to;
}

int main() {
  barrier_init(&barrier, 2);
  N = 50000;
  F = foo;
  pthread_t t;
  pthread_attr_t a;
  pthread_attr_init(&a);
  size_t stack_size = N * 256 + (1 << 20);
  stack_size = RoundUp(stack_size, 0x10000);  // round the stack size to 64k
  int ret = pthread_attr_setstacksize(&a, stack_size);
  if (ret) abort();
  pthread_create(&t, &a, Thread, 0);
#ifdef ORDER2
  barrier_wait(&barrier);
#endif
  X = 43;
#ifdef ORDER1
  barrier_wait(&barrier);
#endif

  pthread_join(t, 0);
}

// CHECK: WARNING: ThreadSanitizer: data race
// CHECK:    #100 foo
// We must output suffucuently large stack (at least 100 frames)

<commit_msg>Fixup whitespace.<commit_after>// RUN: %clangxx_tsan -O1 %s -o %t -DORDER1 && %deflake %run %t | FileCheck %s
// RUN: %clangxx_tsan -O1 %s -o %t -DORDER2 && %deflake %run %t | FileCheck %s
#include "test.h"

volatile int X;
volatile int N;
void (*volatile F)();

static void foo() {
  if (--N == 0)
    X = 42;
  else
    F();
}

void *Thread(void *p) {
#ifdef ORDER1
  barrier_wait(&barrier);
#endif
  F();
#ifdef ORDER2
  barrier_wait(&barrier);
#endif
  return 0;
}

static size_t RoundUp(size_t n, size_t to) {
  return ((n + to - 1) / to) * to;
}

int main() {
  barrier_init(&barrier, 2);
  N = 50000;
  F = foo;
  pthread_t t;
  pthread_attr_t a;
  pthread_attr_init(&a);
  size_t stack_size = N * 256 + (1 << 20);
  stack_size = RoundUp(stack_size, 0x10000);  // round the stack size to 64k
  int ret = pthread_attr_setstacksize(&a, stack_size);
  if (ret) abort();
  pthread_create(&t, &a, Thread, 0);
#ifdef ORDER2
  barrier_wait(&barrier);
#endif
  X = 43;
#ifdef ORDER1
  barrier_wait(&barrier);
#endif

  pthread_join(t, 0);
}

// CHECK: WARNING: ThreadSanitizer: data race
// CHECK:    #100 foo
// We must output suffucuently large stack (at least 100 frames)

<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

#include <unistd.h>

#include <string>

#include <process/future.hpp>
#include <process/owned.hpp>

#include <stout/gtest.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include <stout/uuid.hpp>

#include <mesos/mesos.hpp>

#ifdef __linux__
#include "linux/ns.hpp"
#endif

#include "slave/containerizer/fetcher.hpp"

#include "slave/containerizer/mesos/containerizer.hpp"

#include "tests/mesos.hpp"

using std::string;

using process::Future;
using process::Owned;

using mesos::internal::slave::Fetcher;
using mesos::internal::slave::MesosContainerizer;

using mesos::slave::ContainerTermination;

namespace mesos {
namespace internal {
namespace tests {

#ifdef __linux__
class NamespacesIsolatorTest : public MesosTest
{
public:
  virtual void SetUp()
  {
    MesosTest::SetUp();

    directory = os::getcwd(); // We're inside a temporary sandbox.
    containerId.set_value(UUID::random().toString());
  }

  Try<Owned<MesosContainerizer>> createContainerizer(const string& isolation)
  {
    slave::Flags flags = CreateSlaveFlags();
    flags.isolation = isolation;

    fetcher.reset(new Fetcher(flags));

    Try<MesosContainerizer*> _containerizer =
      MesosContainerizer::create(flags, false, fetcher.get());

    if (_containerizer.isError()) {
      return Error(_containerizer.error());
    }

    return Owned<MesosContainerizer>(_containerizer.get());
  }

  // Read a uint64_t value from the given path.
  Try<uint64_t> readValue(const string& path)
  {
    Try<string> value = os::read(path);

    if (value.isError()) {
      return Error("Failed to read '" + path + "': " + value.error());
    }

    return numify<uint64_t>(strings::trim(value.get()));
  }

  string directory;
  Owned<Fetcher> fetcher;
  ContainerID containerId;
};

TEST_F(NamespacesIsolatorTest, ROOT_PidNamespace)
{
  Try<Owned<MesosContainerizer>> containerizer =
    createContainerizer("filesystem/linux,namespaces/pid");
  ASSERT_SOME(containerizer);

  // Write the command's pid namespace inode and init name to files.
  const string command =
    "stat -c %i /proc/self/ns/pid > ns && (cat /proc/1/comm > init)";

  process::Future<bool> launch = containerizer.get()->launch(
      containerId,
      createContainerConfig(
          None(),
          createExecutorInfo("executor", command),
          directory),
      std::map<string, string>(),
      None());

  AWAIT_READY(launch);
  ASSERT_TRUE(launch.get());

  // Wait on the container.
  Future<Option<ContainerTermination>> wait =
    containerizer.get()->wait(containerId);

  AWAIT_READY(wait);
  ASSERT_SOME(wait.get());

  // Check the executor exited correctly.
  EXPECT_TRUE(wait->get().has_status());
  EXPECT_EQ(0, wait->get().status());

  // Check that the command was run in a different pid namespace.
  Try<ino_t> testPidNamespace = ns::getns(::getpid(), "pid");
  ASSERT_SOME(testPidNamespace);

  Try<string> containerPidNamespace = os::read(path::join(directory, "ns"));
  ASSERT_SOME(containerPidNamespace);

  EXPECT_NE(stringify(testPidNamespace.get()),
            strings::trim(containerPidNamespace.get()));

  // Check that the word 'mesos' is the part of the name for the
  // container's 'init' process. This verifies that /proc has been
  // correctly mounted for the container.
  Try<string> init = os::read(path::join(directory, "init"));
  ASSERT_SOME(init);

  EXPECT_TRUE(strings::contains(init.get(), "mesos"));
}

// The IPC namespace has its own copy of the svipc(7) tunables. We verify
// that we are correctly entering the IPC namespace by verifying that we
// can set shmmax some different value than that of the host namespace.
TEST_F(NamespacesIsolatorTest, ROOT_IPCNamespace)
{
  Try<Owned<MesosContainerizer>> containerizer =
    createContainerizer("namespaces/ipc");
  ASSERT_SOME(containerizer);

  // Value we will set the child namespace shmmax to.
  uint64_t shmmaxValue = static_cast<uint64_t>(::getpid());

  Try<uint64_t> hostShmmax = readValue("/proc/sys/kernel/shmmax");
  ASSERT_SOME(hostShmmax);

  // Verify that the host namespace shmmax is different.
  ASSERT_NE(hostShmmax.get(), shmmaxValue);

  const string command =
    "stat -c %i /proc/self/ns/ipc > ns;"
    "echo " + stringify(shmmaxValue) + " > /proc/sys/kernel/shmmax;"
    "cp /proc/sys/kernel/shmmax shmmax";

  process::Future<bool> launch = containerizer.get()->launch(
      containerId,
      createContainerConfig(
          None(),
          createExecutorInfo("executor", command),
          directory),
      std::map<string, string>(),
      None());

  AWAIT_READY(launch);
  ASSERT_TRUE(launch.get());

  // Wait on the container.
  Future<Option<ContainerTermination>> wait =
    containerizer.get()->wait(containerId);

  AWAIT_READY(wait);
  ASSERT_SOME(wait.get());

  // Check the executor exited correctly.
  EXPECT_TRUE(wait->get().has_status());
  EXPECT_EQ(0, wait->get().status());

  // Check that the command was run in a different IPC namespace.
  Try<ino_t> testIPCNamespace = ns::getns(::getpid(), "ipc");
  ASSERT_SOME(testIPCNamespace);

  Try<string> containerIPCNamespace = os::read(path::join(directory, "ns"));
  ASSERT_SOME(containerIPCNamespace);

  EXPECT_NE(stringify(testIPCNamespace.get()),
            strings::trim(containerIPCNamespace.get()));

  // Check that we modified the IPC shmmax of the namespace, not the host.
  Try<uint64_t> childShmmax = readValue("shmmax");
  ASSERT_SOME(childShmmax);

  // Verify that we didn't modify shmmax in the host namespace.
  ASSERT_EQ(hostShmmax.get(), readValue("/proc/sys/kernel/shmmax").get());

  EXPECT_NE(hostShmmax.get(), childShmmax.get());
  EXPECT_EQ(shmmaxValue, childShmmax.get());
}
#endif // __linux__

} // namespace tests {
} // namespace internal {
} // namespace mesos {
<commit_msg>Fixed a bug in the test `NamespacesIsolatorTest`.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

#include <unistd.h>

#include <string>

#include <process/future.hpp>
#include <process/owned.hpp>

#include <stout/gtest.hpp>
#include <stout/os.hpp>
#include <stout/path.hpp>
#include <stout/stringify.hpp>
#include <stout/strings.hpp>
#include <stout/uuid.hpp>

#include <mesos/mesos.hpp>

#ifdef __linux__
#include "linux/ns.hpp"
#endif

#include "slave/containerizer/fetcher.hpp"

#include "slave/containerizer/mesos/containerizer.hpp"

#include "tests/mesos.hpp"

using std::string;

using process::Future;
using process::Owned;

using mesos::internal::slave::Fetcher;
using mesos::internal::slave::MesosContainerizer;

using mesos::slave::ContainerTermination;

namespace mesos {
namespace internal {
namespace tests {

#ifdef __linux__
class NamespacesIsolatorTest : public MesosTest
{
public:
  virtual void SetUp()
  {
    MesosTest::SetUp();

    directory = os::getcwd(); // We're inside a temporary sandbox.
    containerId.set_value(UUID::random().toString());
  }

  Try<Owned<MesosContainerizer>> createContainerizer(const string& isolation)
  {
    slave::Flags flags = CreateSlaveFlags();
    flags.isolation = isolation;

    fetcher.reset(new Fetcher(flags));

    Try<MesosContainerizer*> _containerizer =
      MesosContainerizer::create(flags, false, fetcher.get());

    if (_containerizer.isError()) {
      return Error(_containerizer.error());
    }

    return Owned<MesosContainerizer>(_containerizer.get());
  }

  // Read a uint64_t value from the given path.
  Try<uint64_t> readValue(const string& path)
  {
    Try<string> value = os::read(path);

    if (value.isError()) {
      return Error("Failed to read '" + path + "': " + value.error());
    }

    return numify<uint64_t>(strings::trim(value.get()));
  }

  string directory;
  Owned<Fetcher> fetcher;
  ContainerID containerId;
};

TEST_F(NamespacesIsolatorTest, ROOT_PidNamespace)
{
  Try<Owned<MesosContainerizer>> containerizer =
    createContainerizer("filesystem/linux,namespaces/pid");
  ASSERT_SOME(containerizer);

  // Write the command's pid namespace inode and init name to files.
  const string command =
    "stat -Lc %i /proc/self/ns/pid > ns && (cat /proc/1/comm > init)";

  process::Future<bool> launch = containerizer.get()->launch(
      containerId,
      createContainerConfig(
          None(),
          createExecutorInfo("executor", command),
          directory),
      std::map<string, string>(),
      None());

  AWAIT_READY(launch);
  ASSERT_TRUE(launch.get());

  // Wait on the container.
  Future<Option<ContainerTermination>> wait =
    containerizer.get()->wait(containerId);

  AWAIT_READY(wait);
  ASSERT_SOME(wait.get());

  // Check the executor exited correctly.
  EXPECT_TRUE(wait->get().has_status());
  EXPECT_EQ(0, wait->get().status());

  // Check that the command was run in a different pid namespace.
  Try<ino_t> testPidNamespace = ns::getns(::getpid(), "pid");
  ASSERT_SOME(testPidNamespace);

  Try<string> containerPidNamespace = os::read(path::join(directory, "ns"));
  ASSERT_SOME(containerPidNamespace);

  EXPECT_NE(stringify(testPidNamespace.get()),
            strings::trim(containerPidNamespace.get()));

  // Check that the word 'mesos' is the part of the name for the
  // container's 'init' process. This verifies that /proc has been
  // correctly mounted for the container.
  Try<string> init = os::read(path::join(directory, "init"));
  ASSERT_SOME(init);

  EXPECT_TRUE(strings::contains(init.get(), "mesos"));
}

// The IPC namespace has its own copy of the svipc(7) tunables. We verify
// that we are correctly entering the IPC namespace by verifying that we
// can set shmmax some different value than that of the host namespace.
TEST_F(NamespacesIsolatorTest, ROOT_IPCNamespace)
{
  Try<Owned<MesosContainerizer>> containerizer =
    createContainerizer("namespaces/ipc");
  ASSERT_SOME(containerizer);

  // Value we will set the child namespace shmmax to.
  uint64_t shmmaxValue = static_cast<uint64_t>(::getpid());

  Try<uint64_t> hostShmmax = readValue("/proc/sys/kernel/shmmax");
  ASSERT_SOME(hostShmmax);

  // Verify that the host namespace shmmax is different.
  ASSERT_NE(hostShmmax.get(), shmmaxValue);

  const string command =
    "stat -Lc %i /proc/self/ns/ipc > ns;"
    "echo " + stringify(shmmaxValue) + " > /proc/sys/kernel/shmmax;"
    "cp /proc/sys/kernel/shmmax shmmax";

  process::Future<bool> launch = containerizer.get()->launch(
      containerId,
      createContainerConfig(
          None(),
          createExecutorInfo("executor", command),
          directory),
      std::map<string, string>(),
      None());

  AWAIT_READY(launch);
  ASSERT_TRUE(launch.get());

  // Wait on the container.
  Future<Option<ContainerTermination>> wait =
    containerizer.get()->wait(containerId);

  AWAIT_READY(wait);
  ASSERT_SOME(wait.get());

  // Check the executor exited correctly.
  EXPECT_TRUE(wait->get().has_status());
  EXPECT_EQ(0, wait->get().status());

  // Check that the command was run in a different IPC namespace.
  Try<ino_t> testIPCNamespace = ns::getns(::getpid(), "ipc");
  ASSERT_SOME(testIPCNamespace);

  Try<string> containerIPCNamespace = os::read(path::join(directory, "ns"));
  ASSERT_SOME(containerIPCNamespace);

  EXPECT_NE(stringify(testIPCNamespace.get()),
            strings::trim(containerIPCNamespace.get()));

  // Check that we modified the IPC shmmax of the namespace, not the host.
  Try<uint64_t> childShmmax = readValue("shmmax");
  ASSERT_SOME(childShmmax);

  // Verify that we didn't modify shmmax in the host namespace.
  ASSERT_EQ(hostShmmax.get(), readValue("/proc/sys/kernel/shmmax").get());

  EXPECT_NE(hostShmmax.get(), childShmmax.get());
  EXPECT_EQ(shmmaxValue, childShmmax.get());
}
#endif // __linux__

} // namespace tests {
} // namespace internal {
} // namespace mesos {
<|endoftext|>