<commit_before>/* -*- c-basic-offset: 2 -*- */
/*
  Copyright(C) 2017-2021  Sutou Kouhei <kou@clear-code.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "mrn_query_parser.hpp"

#include <mrn_variables.hpp>

#include <vector>

extern "C" {
  /* Groonga's internal functions */
  int grn_atoi(const char *nptr, const char *end, const char **rest);
  uint grn_atoui(const char *nptr, const char *end, const char **rest);
}

#define MRN_CLASS_NAME "mrn::QueryParser"

namespace mrn {
  QueryParser::QueryParser(grn_ctx *ctx,
                           THD *thd,
                           grn_obj *expression,
                           grn_obj *default_column,
                           uint n_sections,
                           grn_obj *match_columns)
    : ctx_(ctx),
      thd_(thd),
      expression_(expression),
      default_column_(default_column),
      n_sections_(n_sections),
      match_columns_(match_columns) {
  }

  QueryParser::~QueryParser() {
  }

  grn_rc QueryParser::parse(const char *query, size_t query_length) {
    MRN_DBUG_ENTER_METHOD();

    const char *raw_query = NULL;
    size_t raw_query_length = 0;
    grn_operator default_operator = GRN_OP_OR;
    grn_expr_flags expression_flags = 0;
    parse_pragma(query,
                 query_length,
                 &raw_query,
                 &raw_query_length,
                 &default_operator,
                 &expression_flags);

    grn_obj *default_column = default_column_;
    if (match_columns_) {
      default_column = match_columns_;
    }
    grn_rc rc = grn_expr_parse(ctx_,
                               expression_,
                               raw_query,
                               raw_query_length,
                               default_column,
                               GRN_OP_MATCH,
                               default_operator,
                               expression_flags);
    if (rc != GRN_SUCCESS) {
      char error_message[MRN_MESSAGE_BUFFER_SIZE];
      snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
               "failed to parse fulltext search keyword: <%.*s>: <%s>",
               static_cast<int>(query_length),
               query,
               ctx_->errbuf);
      variables::ActionOnError action =
        variables::get_action_on_fulltext_query_error(thd_);
      switch (action) {
      case variables::ACTION_ON_ERROR_ERROR:
        my_message(ER_PARSE_ERROR, error_message, MYF(0));
        break;
      case variables::ACTION_ON_ERROR_ERROR_AND_LOG:
        my_message(ER_PARSE_ERROR, error_message, MYF(0));
        GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message);
        break;
      case variables::ACTION_ON_ERROR_IGNORE:
        break;
      case variables::ACTION_ON_ERROR_IGNORE_AND_LOG:
        GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message);
        break;
      }
    }

    DBUG_RETURN(rc);
  }

  void QueryParser::parse_pragma(const char *query,
                                 size_t query_length,
                                 const char **raw_query,
                                 size_t *raw_query_length,
                                 grn_operator *default_operator,
                                 grn_expr_flags *flags) {
    MRN_DBUG_ENTER_METHOD();

    const char *current_query = query;
    size_t current_query_length = query_length;

    *default_operator = GRN_OP_OR;

    if (current_query_length >= 4 && memcmp(current_query, "*SS ", 4) == 0) {
      *raw_query = current_query + 4;
      *raw_query_length = current_query_length - 4;
      *flags = GRN_EXPR_SYNTAX_SCRIPT;
      DBUG_VOID_RETURN;
    }

    bool weight_specified = false;
    *raw_query = query;
    *raw_query_length = query_length;
    *flags = default_expression_flags();
    if (current_query_length >= 2 && current_query[0] == '*') {
      bool parsed = false;
      bool done = false;
      current_query++;
      current_query_length--;
      while (!done) {
        size_t consumed_query_length = 0;
        switch (current_query[0]) {
        case 'D':
          if (parse_pragma_d(current_query + 1,
                             current_query_length - 1,
                             default_operator,
                             &consumed_query_length)) {
            parsed = true;
            consumed_query_length += 1;
            current_query += consumed_query_length;
            current_query_length -= consumed_query_length;
          } else {
            done = true;
          }
          break;
        case 'W':
          if (parse_pragma_w(current_query + 1,
                             current_query_length - 1,
                             &consumed_query_length)) {
            parsed = true;
            weight_specified = true;
            consumed_query_length += 1;
            current_query += consumed_query_length;
            current_query_length -= consumed_query_length;
          } else {
            done = true;
          }
          break;
        default:
          done = true;
          break;
        }
      }
      if (parsed) {
        *raw_query = current_query;
        *raw_query_length = current_query_length;
      }
    }

    // WORKAROUND: ignore the first '+' to support "+apple macintosh" pattern.
    while (*raw_query_length > 0 && (*raw_query)[0] == ' ') {
      (*raw_query)++;
      (*raw_query_length)--;
    }
    if (*raw_query_length > 0 && (*raw_query)[0] == '+') {
      (*raw_query)++;
      (*raw_query_length)--;
    }
    if (!weight_specified && match_columns_) {
      grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1);
    }

    DBUG_VOID_RETURN;
  }

  bool QueryParser::parse_pragma_w(const char *query,
                                   size_t query_length,
                                   size_t *consumed_query_length) {
    MRN_DBUG_ENTER_METHOD();

    *consumed_query_length = 0;

    grn_obj section_value_buffer;
    GRN_UINT32_INIT(&section_value_buffer, 0);

    bool target_is_vector = false;
    if (grn_obj_is_index_column(ctx_, default_column_)) {
      grn_obj source_ids;
      GRN_RECORD_INIT(&source_ids, GRN_OBJ_VECTOR, GRN_ID_NIL);
      grn_obj_get_info(ctx_, default_column_, GRN_INFO_SOURCE, &source_ids);
      if (GRN_RECORD_VECTOR_SIZE(&source_ids) == 1) {
        grn_obj *source = grn_ctx_at(ctx_, GRN_RECORD_VALUE_AT(&source_ids, 0));
        target_is_vector = grn_obj_is_vector_column(ctx_, source);
        grn_obj_unref(ctx_, source);
      }
      GRN_OBJ_FIN(ctx_, &source_ids);
    }

    std::vector<bool> specified_sections;
    if (!target_is_vector) {
      for (uint i = 0; i < n_sections_; ++i) {
        specified_sections[i] = false;
      }
    }

    uint n_weights = 0;
    while (query_length >= 1) {
      if (n_weights >= 1) {
        if (query[0] != ',') {
          break;
        }
        size_t n_used_query_length = 1;
        *consumed_query_length += n_used_query_length;
        query_length -= n_used_query_length;
        query += n_used_query_length;
        if (query_length == 0) {
          break;
        }
      }

      uint section = 0;
      if ('1' <= query[0] && query[0] <= '9') {
        const char *section_start = query;
        const char *query_end = query + query_length;
        const char *query_rest;
        section = grn_atoui(section_start, query_end, &query_rest);
        if (section_start == query_rest) {
          break;
        }
        if (!target_is_vector) {
          if (!(0 < section && section <= n_sections_)) {
            break;
          }
          specified_sections[section] = true;
        }
        section -= 1;
        size_t n_used_query_length = query_rest - query;
        *consumed_query_length += n_used_query_length;
        query_length -= n_used_query_length;
        query += n_used_query_length;
      } else {
        break;
      }

      int weight = 1;
      if (query_length >= 2 && query[0] == ':') {
        const char *weight_start = query + 1;
        const char *query_end = query + query_length;
        const char *query_rest;
        weight = grn_atoi(weight_start, query_end, &query_rest);
        if (weight_start == query_rest) {
          break;
        }
        size_t n_used_query_length = query_rest - query;
        *consumed_query_length += n_used_query_length;
        query_length -= n_used_query_length;
        query += n_used_query_length;
      }

      n_weights++;

      append_section(section,
                     &section_value_buffer,
                     weight,
                     n_weights);
    }

    if (!target_is_vector) {
      for (uint section = 0; section < n_sections_; ++section) {
        if (specified_sections[section]) {
          continue;
        }

        ++n_weights;

        int default_weight = 1;
        append_section(section,
                       &section_value_buffer,
                       default_weight,
                       n_weights);
      }
    }

    GRN_OBJ_FIN(ctx_, &section_value_buffer);

    DBUG_RETURN(n_weights > 0);
  }

  void QueryParser::append_section(uint section,
                                   grn_obj *section_value_buffer,
                                   int weight,
                                   uint n_weights) {
    MRN_DBUG_ENTER_METHOD();

    if (!match_columns_) {
      DBUG_VOID_RETURN;
    }

    grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1);
    GRN_UINT32_SET(ctx_, section_value_buffer, section);
    grn_expr_append_const(ctx_, match_columns_, section_value_buffer,
                          GRN_OP_PUSH, 1);
    grn_expr_append_op(ctx_, match_columns_, GRN_OP_GET_MEMBER, 2);

    if (weight != 1) {
      grn_expr_append_const_int(ctx_, match_columns_, weight, GRN_OP_PUSH, 1);
      grn_expr_append_op(ctx_, match_columns_, GRN_OP_STAR, 2);
    }

    if (n_weights >= 2) {
      grn_expr_append_op(ctx_, match_columns_, GRN_OP_OR, 2);
    }

    DBUG_VOID_RETURN;
  }

  bool QueryParser::parse_pragma_d(const char *query,
                                   size_t query_length,
                                   grn_operator *default_operator,
                                   size_t *consumed_query_length) {
    MRN_DBUG_ENTER_METHOD();

    bool succeeded = true;
    if (query_length >= 1 && query[0] == '+') {
      *default_operator = GRN_OP_AND;
      *consumed_query_length = 1;
    } else if (query_length >= 1 && query[0] == '-') {
      *default_operator = GRN_OP_AND_NOT;
      *consumed_query_length = 1;
    } else if (query_length >= 2 && memcmp(query, "OR", 2) == 0) {
      *default_operator = GRN_OP_OR;
      *consumed_query_length = 2;
    } else {
      succeeded = false;
    }

    DBUG_RETURN(succeeded);
  }

  grn_expr_flags QueryParser::default_expression_flags() {
    MRN_DBUG_ENTER_METHOD();

    ulonglong syntax_flags = variables::get_boolean_mode_syntax_flags(thd_);
    grn_expr_flags expression_flags = 0;
    if (syntax_flags == variables::BOOLEAN_MODE_SYNTAX_FLAG_DEFAULT) {
      expression_flags = GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_LEADING_NOT;
    } else {
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_SYNTAX_SCRIPT) {
        expression_flags |= GRN_EXPR_SYNTAX_SCRIPT;
      } else {
        expression_flags |= GRN_EXPR_SYNTAX_QUERY;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_COLUMN) {
        expression_flags |= GRN_EXPR_ALLOW_COLUMN;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_UPDATE) {
        expression_flags |= GRN_EXPR_ALLOW_UPDATE;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_LEADING_NOT) {
        expression_flags |= GRN_EXPR_ALLOW_LEADING_NOT;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_QUERY_NO_SYNTAX_ERROR) {
        expression_flags |= GRN_EXPR_QUERY_NO_SYNTAX_ERROR;
      }
    }

    DBUG_RETURN(expression_flags);
  }
}
<commit_msg>QueryParser: fix a regression crash bug<commit_after>/* -*- c-basic-offset: 2 -*- */
/*
  Copyright(C) 2017-2021  Sutou Kouhei <kou@clear-code.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "mrn_query_parser.hpp"

#include <mrn_variables.hpp>

#include <vector>

extern "C" {
  /* Groonga's internal functions */
  int grn_atoi(const char *nptr, const char *end, const char **rest);
  uint grn_atoui(const char *nptr, const char *end, const char **rest);
}

#define MRN_CLASS_NAME "mrn::QueryParser"

namespace mrn {
  QueryParser::QueryParser(grn_ctx *ctx,
                           THD *thd,
                           grn_obj *expression,
                           grn_obj *default_column,
                           uint n_sections,
                           grn_obj *match_columns)
    : ctx_(ctx),
      thd_(thd),
      expression_(expression),
      default_column_(default_column),
      n_sections_(n_sections),
      match_columns_(match_columns) {
  }

  QueryParser::~QueryParser() {
  }

  grn_rc QueryParser::parse(const char *query, size_t query_length) {
    MRN_DBUG_ENTER_METHOD();

    const char *raw_query = NULL;
    size_t raw_query_length = 0;
    grn_operator default_operator = GRN_OP_OR;
    grn_expr_flags expression_flags = 0;
    parse_pragma(query,
                 query_length,
                 &raw_query,
                 &raw_query_length,
                 &default_operator,
                 &expression_flags);

    grn_obj *default_column = default_column_;
    if (match_columns_) {
      default_column = match_columns_;
    }
    grn_rc rc = grn_expr_parse(ctx_,
                               expression_,
                               raw_query,
                               raw_query_length,
                               default_column,
                               GRN_OP_MATCH,
                               default_operator,
                               expression_flags);
    if (rc != GRN_SUCCESS) {
      char error_message[MRN_MESSAGE_BUFFER_SIZE];
      snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
               "failed to parse fulltext search keyword: <%.*s>: <%s>",
               static_cast<int>(query_length),
               query,
               ctx_->errbuf);
      variables::ActionOnError action =
        variables::get_action_on_fulltext_query_error(thd_);
      switch (action) {
      case variables::ACTION_ON_ERROR_ERROR:
        my_message(ER_PARSE_ERROR, error_message, MYF(0));
        break;
      case variables::ACTION_ON_ERROR_ERROR_AND_LOG:
        my_message(ER_PARSE_ERROR, error_message, MYF(0));
        GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message);
        break;
      case variables::ACTION_ON_ERROR_IGNORE:
        break;
      case variables::ACTION_ON_ERROR_IGNORE_AND_LOG:
        GRN_LOG(ctx_, GRN_LOG_ERROR, "%s", error_message);
        break;
      }
    }

    DBUG_RETURN(rc);
  }

  void QueryParser::parse_pragma(const char *query,
                                 size_t query_length,
                                 const char **raw_query,
                                 size_t *raw_query_length,
                                 grn_operator *default_operator,
                                 grn_expr_flags *flags) {
    MRN_DBUG_ENTER_METHOD();

    const char *current_query = query;
    size_t current_query_length = query_length;

    *default_operator = GRN_OP_OR;

    if (current_query_length >= 4 && memcmp(current_query, "*SS ", 4) == 0) {
      *raw_query = current_query + 4;
      *raw_query_length = current_query_length - 4;
      *flags = GRN_EXPR_SYNTAX_SCRIPT;
      DBUG_VOID_RETURN;
    }

    bool weight_specified = false;
    *raw_query = query;
    *raw_query_length = query_length;
    *flags = default_expression_flags();
    if (current_query_length >= 2 && current_query[0] == '*') {
      bool parsed = false;
      bool done = false;
      current_query++;
      current_query_length--;
      while (!done) {
        size_t consumed_query_length = 0;
        switch (current_query[0]) {
        case 'D':
          if (parse_pragma_d(current_query + 1,
                             current_query_length - 1,
                             default_operator,
                             &consumed_query_length)) {
            parsed = true;
            consumed_query_length += 1;
            current_query += consumed_query_length;
            current_query_length -= consumed_query_length;
          } else {
            done = true;
          }
          break;
        case 'W':
          if (parse_pragma_w(current_query + 1,
                             current_query_length - 1,
                             &consumed_query_length)) {
            parsed = true;
            weight_specified = true;
            consumed_query_length += 1;
            current_query += consumed_query_length;
            current_query_length -= consumed_query_length;
          } else {
            done = true;
          }
          break;
        default:
          done = true;
          break;
        }
      }
      if (parsed) {
        *raw_query = current_query;
        *raw_query_length = current_query_length;
      }
    }

    // WORKAROUND: ignore the first '+' to support "+apple macintosh" pattern.
    while (*raw_query_length > 0 && (*raw_query)[0] == ' ') {
      (*raw_query)++;
      (*raw_query_length)--;
    }
    if (*raw_query_length > 0 && (*raw_query)[0] == '+') {
      (*raw_query)++;
      (*raw_query_length)--;
    }
    if (!weight_specified && match_columns_) {
      grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1);
    }

    DBUG_VOID_RETURN;
  }

  bool QueryParser::parse_pragma_w(const char *query,
                                   size_t query_length,
                                   size_t *consumed_query_length) {
    MRN_DBUG_ENTER_METHOD();

    *consumed_query_length = 0;

    grn_obj section_value_buffer;
    GRN_UINT32_INIT(&section_value_buffer, 0);

    bool target_is_vector = false;
    if (grn_obj_is_index_column(ctx_, default_column_)) {
      grn_obj source_ids;
      GRN_RECORD_INIT(&source_ids, GRN_OBJ_VECTOR, GRN_ID_NIL);
      grn_obj_get_info(ctx_, default_column_, GRN_INFO_SOURCE, &source_ids);
      if (GRN_RECORD_VECTOR_SIZE(&source_ids) == 1) {
        grn_obj *source = grn_ctx_at(ctx_, GRN_RECORD_VALUE_AT(&source_ids, 0));
        target_is_vector = grn_obj_is_vector_column(ctx_, source);
        grn_obj_unref(ctx_, source);
      }
      GRN_OBJ_FIN(ctx_, &source_ids);
    }

    std::vector<bool> specified_sections;
    if (!target_is_vector) {
      for (uint i = 0; i < n_sections_; ++i) {
        specified_sections.push_back(false);
      }
    }

    uint n_weights = 0;
    while (query_length >= 1) {
      if (n_weights >= 1) {
        if (query[0] != ',') {
          break;
        }
        size_t n_used_query_length = 1;
        *consumed_query_length += n_used_query_length;
        query_length -= n_used_query_length;
        query += n_used_query_length;
        if (query_length == 0) {
          break;
        }
      }

      uint section = 0;
      if ('1' <= query[0] && query[0] <= '9') {
        const char *section_start = query;
        const char *query_end = query + query_length;
        const char *query_rest;
        section = grn_atoui(section_start, query_end, &query_rest);
        if (section_start == query_rest) {
          break;
        }
        if (!target_is_vector) {
          if (!(0 < section && section <= n_sections_)) {
            break;
          }
          specified_sections[section - 1] = true;
        }
        section -= 1;
        size_t n_used_query_length = query_rest - query;
        *consumed_query_length += n_used_query_length;
        query_length -= n_used_query_length;
        query += n_used_query_length;
      } else {
        break;
      }

      int weight = 1;
      if (query_length >= 2 && query[0] == ':') {
        const char *weight_start = query + 1;
        const char *query_end = query + query_length;
        const char *query_rest;
        weight = grn_atoi(weight_start, query_end, &query_rest);
        if (weight_start == query_rest) {
          break;
        }
        size_t n_used_query_length = query_rest - query;
        *consumed_query_length += n_used_query_length;
        query_length -= n_used_query_length;
        query += n_used_query_length;
      }

      n_weights++;

      append_section(section,
                     &section_value_buffer,
                     weight,
                     n_weights);
    }

    if (!target_is_vector) {
      for (uint section = 0; section < n_sections_; ++section) {
        if (specified_sections[section]) {
          continue;
        }

        ++n_weights;

        int default_weight = 1;
        append_section(section,
                       &section_value_buffer,
                       default_weight,
                       n_weights);
      }
    }

    GRN_OBJ_FIN(ctx_, &section_value_buffer);

    DBUG_RETURN(n_weights > 0);
  }

  void QueryParser::append_section(uint section,
                                   grn_obj *section_value_buffer,
                                   int weight,
                                   uint n_weights) {
    MRN_DBUG_ENTER_METHOD();

    if (!match_columns_) {
      DBUG_VOID_RETURN;
    }

    grn_expr_append_obj(ctx_, match_columns_, default_column_, GRN_OP_PUSH, 1);
    GRN_UINT32_SET(ctx_, section_value_buffer, section);
    grn_expr_append_const(ctx_, match_columns_, section_value_buffer,
                          GRN_OP_PUSH, 1);
    grn_expr_append_op(ctx_, match_columns_, GRN_OP_GET_MEMBER, 2);

    if (weight != 1) {
      grn_expr_append_const_int(ctx_, match_columns_, weight, GRN_OP_PUSH, 1);
      grn_expr_append_op(ctx_, match_columns_, GRN_OP_STAR, 2);
    }

    if (n_weights >= 2) {
      grn_expr_append_op(ctx_, match_columns_, GRN_OP_OR, 2);
    }

    DBUG_VOID_RETURN;
  }

  bool QueryParser::parse_pragma_d(const char *query,
                                   size_t query_length,
                                   grn_operator *default_operator,
                                   size_t *consumed_query_length) {
    MRN_DBUG_ENTER_METHOD();

    bool succeeded = true;
    if (query_length >= 1 && query[0] == '+') {
      *default_operator = GRN_OP_AND;
      *consumed_query_length = 1;
    } else if (query_length >= 1 && query[0] == '-') {
      *default_operator = GRN_OP_AND_NOT;
      *consumed_query_length = 1;
    } else if (query_length >= 2 && memcmp(query, "OR", 2) == 0) {
      *default_operator = GRN_OP_OR;
      *consumed_query_length = 2;
    } else {
      succeeded = false;
    }

    DBUG_RETURN(succeeded);
  }

  grn_expr_flags QueryParser::default_expression_flags() {
    MRN_DBUG_ENTER_METHOD();

    ulonglong syntax_flags = variables::get_boolean_mode_syntax_flags(thd_);
    grn_expr_flags expression_flags = 0;
    if (syntax_flags == variables::BOOLEAN_MODE_SYNTAX_FLAG_DEFAULT) {
      expression_flags = GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_LEADING_NOT;
    } else {
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_SYNTAX_SCRIPT) {
        expression_flags |= GRN_EXPR_SYNTAX_SCRIPT;
      } else {
        expression_flags |= GRN_EXPR_SYNTAX_QUERY;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_COLUMN) {
        expression_flags |= GRN_EXPR_ALLOW_COLUMN;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_UPDATE) {
        expression_flags |= GRN_EXPR_ALLOW_UPDATE;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_ALLOW_LEADING_NOT) {
        expression_flags |= GRN_EXPR_ALLOW_LEADING_NOT;
      }
      if (syntax_flags & variables::BOOLEAN_MODE_SYNTAX_FLAG_QUERY_NO_SYNTAX_ERROR) {
        expression_flags |= GRN_EXPR_QUERY_NO_SYNTAX_ERROR;
      }
    }

    DBUG_RETURN(expression_flags);
  }
}
<|endoftext|>


<commit_before>
#include "TextRenderer.hpp"
#include "FAST/Visualization/View.hpp"
#include <QPainter>
#include <QApplication>
#include <FAST/Data/Text.hpp>

namespace fast {

BoundingBox TextRenderer::getBoundingBox(bool transform) {
    return BoundingBox(Vector3f(6,6,6));
}

TextRenderer::TextRenderer() {
    createInputPort<Text>(0);
    mStyle = STYLE_NORMAL;
    mPosition = POSITION_CENTER;
	mFontSize = 18;
	mColor = Color::Green();
    createStringAttribute("position", "Text position", "Position of text in view (center/bottom_left/bottom_right/top_left/top_right)", "top_left");
    createIntegerAttribute("font_size", "Font size", "Font size", mFontSize);

    createShaderProgram({
        Config::getKernelSourcePath() + "/Visualization/TextRenderer/TextRenderer.vert",
        Config::getKernelSourcePath() + "/Visualization/TextRenderer/TextRenderer.frag",
    });
}

void TextRenderer::setView(View* view) {
	mView = view;
}

void TextRenderer::execute() {
    std::unique_lock<std::mutex> lock(mMutex);
    if(mStop) {
        return;
    }

    // Check if current images has not been rendered, if not wait
    while(!mHasRendered) {
        mRenderedCV.wait(lock);
    }
    // This simply gets the input data for each connection and puts it into a data structure
    for(uint inputNr = 0; inputNr < getNrOfInputConnections(); inputNr++) {
        if(hasNewInputData(inputNr)) {
            DataObject::pointer input = getInputData<DataObject>(inputNr);

            mHasRendered = false;
            mDataToRender[inputNr] = input;
        }
    }
}

void TextRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, float zNear, float zFar, bool mode2D) {
    std::lock_guard<std::mutex> lock(mMutex);

    if(!mode2D)
        throw Exception("TextRender is only implemented for 2D at the moment");

    if(mView == nullptr)
        throw Exception("TextRenderer need access to the view");

    for(auto it : mDataToRender) {
        Text::pointer input = std::static_pointer_cast<Text>(it.second);
        uint inputNr = it.first;

        // Check if a texture has already been created for this text
        if (mTexturesToRender.count(inputNr) > 0 && mTextUsed[inputNr] == input)
            continue; // If it has already been created, skip it

        if (mTexturesToRender.count(inputNr) > 0) {
            // Delete old texture
            glDeleteTextures(1, &mTexturesToRender[inputNr]);
            mTexturesToRender.erase(inputNr);
            glDeleteVertexArrays(1, &mVAO[inputNr]);
            mVAO.erase(inputNr);
        }

        Text::access access = input->getAccess(ACCESS_READ);
        std::string text = access->getData();

        // Font setup
        QColor color(mColor.getRedValue() * 255, mColor.getGreenValue() * 255, mColor.getBlueValue() * 255, 255);
        QFont font = QApplication::font();
        font.setPointSize(mFontSize);
        if (mStyle == STYLE_BOLD) {
            font.setBold(true);
        } else if (mStyle == STYLE_ITALIC) {
            font.setItalic(true);
        }
        QFontMetrics metrics(font);
        const int width = metrics.boundingRect(QString(text.c_str())).width() + 10;
        const int height = mFontSize + 5;

        // create the QImage and draw txt into it
        QImage textimg(width, height, QImage::Format_RGBA8888);
        textimg.fill(QColor(0, 0, 0, 0));
        {
            // Draw the text to the image
            QPainter painter(&textimg);
            painter.setBrush(color);
            painter.setPen(color);
            painter.setFont(font);
            painter.drawText(0, mFontSize, text.c_str());
        }

        // Make texture of QImage
        GLuint textureID;
        glGenTextures(1, &textureID);
        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                     textimg.width(), textimg.height(),
                     0, GL_RGBA, GL_UNSIGNED_BYTE, textimg.bits());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        // Draw texture on a quad
        // Delete old VAO
        if(mVAO.count(inputNr) > 0)
            glDeleteVertexArrays(1, &mVAO[inputNr]);
        GLuint VAO_ID;
        glGenVertexArrays(1, &VAO_ID);
        mVAO[inputNr] = VAO_ID;
        glBindVertexArray(VAO_ID);
        float vertices[] = {
                // vertex: x, y, z; tex coordinates: x, y
                0.0f, (float)height, 0.0f, 0.0f, 0.0f,
                (float)width, (float)height, 0.0f, 1.0f, 0.0f,
                (float)width, 0.0f, 0.0f, 1.0f, 1.0f,
                0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        };

        // Delete old VBO
        if(mVBO.count(inputNr) > 0)
            glDeleteBuffers(1, &mVBO[inputNr]);
        // Create VBO
        uint VBO;
        glGenBuffers(1, &VBO);
        mVBO[inputNr] = VBO;
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) 0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) (3 * sizeof(float)));
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        // Delete old EBO
        if(mEBO.count(inputNr) > 0)
            glDeleteBuffers(1, &mEBO[inputNr]);
        // Create EBO
        uint EBO;
        glGenBuffers(1, &EBO);
        mEBO[inputNr] = EBO;
        uint indices[] = {  // note that we start from 0!
                0, 1, 3,   // first triangle
                1, 2, 3    // second triangle
        };
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
        glBindVertexArray(0);

        mTexturesToRender[inputNr] = textureID;
        mTextUsed[inputNr] = input;
    }

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    activateShader();
    for(auto it : mDataToRender) {
        const uint inputNr = it.first;
        Affine3f transform = Affine3f::Identity();

        // Get width and height of texture
        glBindTexture(GL_TEXTURE_2D, mTexturesToRender[inputNr]);
        int width, height;
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
        const float scale = 1.0f / mView->width();
        const float scale2 = 1.0f / mView->height();
        const int padding = 15;
        Vector3f position;
        switch(mPosition) {
            case POSITION_CENTER:
                position = Vector3f(-width*scale/2.0f, -height*scale2/2.0f, 0); // Center
                break;
            case POSITION_TOP_CENTER:
                position = Vector3f(-width*scale/2.0f, 1 - (height + padding)*scale2, 0); // Top center
                break;
            case POSITION_BOTTOM_CENTER:
                position = Vector3f(-width*scale/2.0f, -1 + padding*scale2, 0); // Bottom center
                break;
            case POSITION_TOP_LEFT:
                position = Vector3f(-1 + padding*scale, 1 - (height + padding)*scale2, 0); // Top left corner
                break;
            case POSITION_TOP_RIGHT:
                position = Vector3f(1 - (width + padding)*scale, 1 - (height + padding)*scale2, 0); // Top right corner
                break;
            case POSITION_BOTTOM_LEFT:
                position = Vector3f(-1 + padding*scale, -1 + padding*scale2, 0); // Bottom left corner
                break;
            case POSITION_BOTTOM_RIGHT:
                position = Vector3f(1 - (width + padding)*scale, -1 + padding*scale2, 0); // Bottom right corner
                break;
        }
        transform.translate(position);
        transform.scale(Vector3f(scale, scale2, 0));

        uint transformLoc = glGetUniformLocation(getShaderProgram(), "transform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, transform.data());
        transformLoc = glGetUniformLocation(getShaderProgram(), "perspectiveTransform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, perspectiveMatrix.data());
        transformLoc = glGetUniformLocation(getShaderProgram(), "viewTransform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, viewingMatrix.data());

        // Actual drawing
        glBindVertexArray(mVAO[inputNr]);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
        glBindVertexArray(0);
    }
    glDisable(GL_BLEND);
    deactivateShader();
}

void TextRenderer::setFontSize(uint fontSize) {
	mFontSize = fontSize;
}

void TextRenderer::setPosition(TextRenderer::TextPosition position) {
    mPosition = position;
}

void TextRenderer::setColor(Color color) {
	mColor = color;
}

void TextRenderer::setStyle(TextStyleType textStyleType) {
	mStyle = textStyleType;
}

void TextRenderer::loadAttributes() {
    std::string position = getStringAttribute("position");
    if(position == "center") {
        setPosition(POSITION_CENTER);
    } else if(position == "top_left") {
        setPosition(POSITION_TOP_LEFT);
    } else if(position == "top_right") {
        setPosition(POSITION_TOP_RIGHT);
    } else if(position == "bottom_left") {
        setPosition(POSITION_BOTTOM_LEFT);
    } else if(position == "bottom_right") {
        setPosition(POSITION_BOTTOM_RIGHT);
    } else {
        throw Exception("Uknown position for TextRenderer: " + position);
    }
    setFontSize(getIntegerAttribute("font_size"));
}

} // end namespace fast

<commit_msg>Fixed issue with text renderer font size being affected by OS font scaling, which was not caught by font metrisc<commit_after>
#include "TextRenderer.hpp"
#include "FAST/Visualization/View.hpp"
#include <QPainter>
#include <QApplication>
#include <FAST/Data/Text.hpp>
#include <QScreen>

namespace fast {

BoundingBox TextRenderer::getBoundingBox(bool transform) {
    return BoundingBox(Vector3f(6,6,6));
}

TextRenderer::TextRenderer() {
    createInputPort<Text>(0);
    mStyle = STYLE_NORMAL;
    mPosition = POSITION_CENTER;
	mFontSize = 28;
	mColor = Color::Green();
    createStringAttribute("position", "Text position", "Position of text in view (center/bottom_left/bottom_right/top_left/top_right)", "top_left");
    createIntegerAttribute("font_size", "Font size", "Font size", mFontSize);

    createShaderProgram({
        Config::getKernelSourcePath() + "/Visualization/TextRenderer/TextRenderer.vert",
        Config::getKernelSourcePath() + "/Visualization/TextRenderer/TextRenderer.frag",
    });
}

void TextRenderer::setView(View* view) {
	mView = view;
}

void TextRenderer::execute() {
    std::unique_lock<std::mutex> lock(mMutex);
    if(mStop) {
        return;
    }

    // Check if current images has not been rendered, if not wait
    while(!mHasRendered) {
        mRenderedCV.wait(lock);
    }
    // This simply gets the input data for each connection and puts it into a data structure
    for(uint inputNr = 0; inputNr < getNrOfInputConnections(); inputNr++) {
        if(hasNewInputData(inputNr)) {
            DataObject::pointer input = getInputData<DataObject>(inputNr);

            mHasRendered = false;
            mDataToRender[inputNr] = input;
        }
    }
}

void TextRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, float zNear, float zFar, bool mode2D) {
    std::lock_guard<std::mutex> lock(mMutex);

    if(!mode2D)
        throw Exception("TextRender is only implemented for 2D at the moment");

    if(mView == nullptr)
        throw Exception("TextRenderer need access to the view");

    for(auto it : mDataToRender) {
        Text::pointer input = std::static_pointer_cast<Text>(it.second);
        uint inputNr = it.first;

        // Check if a texture has already been created for this text
        if (mTexturesToRender.count(inputNr) > 0 && mTextUsed[inputNr] == input)
            continue; // If it has already been created, skip it

        if (mTexturesToRender.count(inputNr) > 0) {
            // Delete old texture
            glDeleteTextures(1, &mTexturesToRender[inputNr]);
            mTexturesToRender.erase(inputNr);
            glDeleteVertexArrays(1, &mVAO[inputNr]);
            mVAO.erase(inputNr);
        }

        Text::access access = input->getAccess(ACCESS_READ);
        std::string text = access->getData();

        if(text.empty()) {
            continue;
        }

        // Font setup
        QColor color(mColor.getRedValue() * 255, mColor.getGreenValue() * 255, mColor.getBlueValue() * 255, 255);
        QFont font = QApplication::font();
        font.setPixelSize(mFontSize);
        if (mStyle == STYLE_BOLD) {
            font.setBold(true);
        } else if (mStyle == STYLE_ITALIC) {
            font.setItalic(true);
        }
        QFontMetrics metrics(font);
        const int width = metrics.boundingRect(QString(text.c_str())).width();
        const int height = metrics.boundingRect(QString(text.c_str())).height();

        // create the QImage and draw txt into it
        QImage textimg(width, height, QImage::Format_RGBA8888);
        textimg.fill(QColor(0, 0, 0, 0));
        {
            // Draw the text to the image
            QPainter painter(&textimg);
            painter.setBrush(color);
            painter.setPen(color);
            painter.setFont(font);
            painter.drawText(0, mFontSize, text.c_str());
        }

        // Make texture of QImage
        GLuint textureID;
        glGenTextures(1, &textureID);
        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                     textimg.width(), textimg.height(),
                     0, GL_RGBA, GL_UNSIGNED_BYTE, textimg.bits());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        // Draw texture on a quad
        // Delete old VAO
        if(mVAO.count(inputNr) > 0)
            glDeleteVertexArrays(1, &mVAO[inputNr]);
        GLuint VAO_ID;
        glGenVertexArrays(1, &VAO_ID);
        mVAO[inputNr] = VAO_ID;
        glBindVertexArray(VAO_ID);
        float vertices[] = {
                // vertex: x, y, z; tex coordinates: x, y
                0.0f, (float)height, 0.0f, 0.0f, 0.0f,
                (float)width, (float)height, 0.0f, 1.0f, 0.0f,
                (float)width, 0.0f, 0.0f, 1.0f, 1.0f,
                0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        };

        // Delete old VBO
        if(mVBO.count(inputNr) > 0)
            glDeleteBuffers(1, &mVBO[inputNr]);
        // Create VBO
        uint VBO;
        glGenBuffers(1, &VBO);
        mVBO[inputNr] = VBO;
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) 0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *) (3 * sizeof(float)));
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        // Delete old EBO
        if(mEBO.count(inputNr) > 0)
            glDeleteBuffers(1, &mEBO[inputNr]);
        // Create EBO
        uint EBO;
        glGenBuffers(1, &EBO);
        mEBO[inputNr] = EBO;
        uint indices[] = {  // note that we start from 0!
                0, 1, 3,   // first triangle
                1, 2, 3    // second triangle
        };
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
        glBindVertexArray(0);

        mTexturesToRender[inputNr] = textureID;
        mTextUsed[inputNr] = input;
    }

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    activateShader();
    for(auto it : mTexturesToRender) {
        const uint inputNr = it.first;
        Affine3f transform = Affine3f::Identity();

        // Get width and height of texture
        glBindTexture(GL_TEXTURE_2D, mTexturesToRender[inputNr]);
        int width, height;
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
        glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
        const float scale = 1.0f / mView->width();
        const float scale2 = 1.0f / mView->height();
        const int padding = 15;
        Vector3f position;
        switch(mPosition) {
            case POSITION_CENTER:
                position = Vector3f(-width*scale/2.0f, -height*scale2/2.0f, 0); // Center
                break;
            case POSITION_TOP_CENTER:
                position = Vector3f(-width*scale/2.0f, 1 - (height + padding)*scale2, 0); // Top center
                break;
            case POSITION_BOTTOM_CENTER:
                position = Vector3f(-width*scale/2.0f, -1 + padding*scale2, 0); // Bottom center
                break;
            case POSITION_TOP_LEFT:
                position = Vector3f(-1 + padding*scale, 1 - (height + padding)*scale2, 0); // Top left corner
                break;
            case POSITION_TOP_RIGHT:
                position = Vector3f(1 - (width + padding)*scale, 1 - (height + padding)*scale2, 0); // Top right corner
                break;
            case POSITION_BOTTOM_LEFT:
                position = Vector3f(-1 + padding*scale, -1 + padding*scale2, 0); // Bottom left corner
                break;
            case POSITION_BOTTOM_RIGHT:
                position = Vector3f(1 - (width + padding)*scale, -1 + padding*scale2, 0); // Bottom right corner
                break;
        }
        transform.translate(position);
        transform.scale(Vector3f(scale, scale2, 0));

        uint transformLoc = glGetUniformLocation(getShaderProgram(), "transform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, transform.data());
        transformLoc = glGetUniformLocation(getShaderProgram(), "perspectiveTransform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, perspectiveMatrix.data());
        transformLoc = glGetUniformLocation(getShaderProgram(), "viewTransform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, viewingMatrix.data());

        // Actual drawing
        glBindVertexArray(mVAO[inputNr]);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
        glBindVertexArray(0);
    }
    glDisable(GL_BLEND);
    deactivateShader();
}

void TextRenderer::setFontSize(uint fontSize) {
	mFontSize = fontSize;
}

void TextRenderer::setPosition(TextRenderer::TextPosition position) {
    mPosition = position;
}

void TextRenderer::setColor(Color color) {
	mColor = color;
}

void TextRenderer::setStyle(TextStyleType textStyleType) {
	mStyle = textStyleType;
}

void TextRenderer::loadAttributes() {
    std::string position = getStringAttribute("position");
    if(position == "center") {
        setPosition(POSITION_CENTER);
    } else if(position == "top_left") {
        setPosition(POSITION_TOP_LEFT);
    } else if(position == "top_right") {
        setPosition(POSITION_TOP_RIGHT);
    } else if(position == "bottom_left") {
        setPosition(POSITION_BOTTOM_LEFT);
    } else if(position == "bottom_right") {
        setPosition(POSITION_BOTTOM_RIGHT);
    } else {
        throw Exception("Uknown position for TextRenderer: " + position);
    }
    setFontSize(getIntegerAttribute("font_size"));
}

} // end namespace fast

<|endoftext|>


<commit_before>#include <async.h>
#include <dns.h>
#include <arpc.h>
#include <aios.h>

#include <chord_types.h>
#include <chord_prot.h>
#include <accordion_prot.h>
#include <fingers_prot.h>
#include <merkle_sync_prot.h>
#include <misc_utils.h>
#include <id_utils.h>
#include <merkle_misc.h>

#include "rpclib.h"

char *usage = "Usage: nodeq [-l] [-r] host port vnode\n";

int outstanding = 0;
bool do_reverse_lookup = false;
bool do_listkeys = false;
bool do_accordion = false;

struct node {
  int i;
  chord_node_ext y;
  str hostname;

  node (int i_, chord_node_ext n) : i (i_), y (n), hostname ("") {}
};

vec<ptr<node> > successors;
vec<ptr<node> > predecessors;
vec<ptr<node> > fingers;

void
printlist (str desc, vec<ptr<node> > &lst)
{
  if (lst.size () == 0)
    return;

  aout << "=== " << desc << "\n";
  for (size_t i = 0; i < lst.size (); i++) {
    chord_node z = make_chord_node (lst[i]->y.n);
    str h;
    if (lst[i]->hostname.len () > 0)
      h = lst[i]->hostname;
    else
      h = z.r.hostname;
    aout << i << ".\t"
	 << z.x << " "
	 << h << " "
	 << z.r.port << " "
	 << z.vnode_num << " "
	 << z.knownup << " "
	 << z.age << " "
	 << z. budget << " "
	 << lst[i]->y.a_lat << " "
	 << lst[i]->y.a_var << " "
	 << lst[i]->y.nrpc << " "
	 << z.coords[0] << " "
	 << z.coords[1] << " "
	 << z.coords[2] << " "
	 << z.e
	 << "\n";
  }
}

void
finish ()
{
  printlist ("predecessors", predecessors);
  printlist ("successors", successors);
  printlist ("fingers", fingers);
  exit (0);
}

void
dns_lookup_cb (ptr<node> nn, ptr<hostent> he, int err)
{
  if (!err)
    nn->hostname = he->h_name;

  outstanding--;
  if (outstanding == 0)
    finish ();
}

void
processreslist_cb (str desc, vec<ptr<node> > *list,
		   chord_nodelistextres *res, clnt_stat status)
{
  outstanding--;
  if (status) {
    warnx << "no " << desc << ": " << status << "\n";
    if (outstanding == 0)
      finish ();
    return;
  }

  size_t sz = res->resok->nlist.size ();
  vec<chord_node> zs;
  for (size_t i = 0; i < sz; i++) {
    ptr<node> nn = New refcounted<node> (i, res->resok->nlist[i]);
    list->push_back (nn);

    if (do_reverse_lookup) {
      outstanding++;
      struct in_addr ar;
      ar.s_addr = htonl (res->resok->nlist[i].n.machine_order_ipv4_addr);
      dns_hostbyaddr (ar, wrap (&dns_lookup_cb, nn));
    }
  }
  delete res;

  if (outstanding == 0)
    finish ();
}

void
print_successors (const chord_node &dst)
{
  ptr<chordID> ga = New refcounted<chordID> (dst.x);
  chord_nodelistextres *lst = New chord_nodelistextres ();
  doRPC (dst, chord_program_1, CHORDPROC_GETSUCC_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "successors", &successors, lst));
  outstanding++;
}

void
print_fingers (const chord_node &dst)
{
  ptr<chordID> ga = New refcounted<chordID> (dst.x);
  chord_nodelistextres *lst = New chord_nodelistextres ();
  if (do_accordion)
    doRPC (dst, accordion_program_1, ACCORDIONPROC_GETFINGERS_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "fingers", &fingers, lst));
  else
    doRPC (dst, fingers_program_1, FINGERSPROC_GETFINGERS_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "fingers", &fingers, lst));
  outstanding++;
}

void
print_predecessors (const chord_node &dst)
{
  ptr<chordID> ga = New refcounted<chordID> (dst.x);
  chord_nodelistextres *lst = New chord_nodelistextres ();
  doRPC (dst, chord_program_1, CHORDPROC_GETPRED_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "predecessors", &predecessors, lst));
  outstanding++;
}

void
getkeys_cb (const chord_node dst, ref<getkeys_arg> arg, ref<getkeys_res> res,
	    clnt_stat err)
{
  outstanding--;
  if (err) {
    warnx << "no keys: " << err << "\n";
    if (outstanding == 0)
      finish ();
    return;
  } else if (res->status != MERKLE_OK) {
    warnx << "protocol error " << res->status << "\n";
    if (outstanding == 0)
      finish ();
    return;
  }

  for (u_int i = 0; i < res->resok->keys.size (); i++) {
    const chordID &key2 = res->resok->keys[i];
    aout << key2 << "\n";
    arg->rngmin = incID (key2);
  }

  if (res->resok->morekeys) {
    doRPC (dst, merklesync_program_1, MERKLESYNC_GETKEYS,
	   arg, res,
	   wrap (getkeys_cb, dst, arg, res));
    outstanding++;
  } else {
    finish ();
  }
}

void
print_keys (const chord_node &dst)
{
  ref<getkeys_arg> arg = New refcounted<getkeys_arg> ();
  arg->ctype  = DHASH_CONTENTHASH;
  arg->rngmin = 0;
  arg->rngmax = decID (arg->rngmin);
  ref<getkeys_res> res = New refcounted<getkeys_res> ();
  doRPC (dst, merklesync_program_1, MERKLESYNC_GETKEYS,
	 arg, res,
	 wrap (getkeys_cb, dst, arg, res));
  outstanding++;
}

int
main (int argc, char *argv[])
{
  int ch;
  while ((ch = getopt (argc, argv, "rlm")) != -1)
    switch (ch) {
    case 'r':
      do_reverse_lookup = true;
      break;
    case 'l':
      do_listkeys = true;
      break;
    case 'm':
      do_accordion = true;
      break;
    default:
      fatal << usage;
      break;
    }

  argc -= optind;
  argv += optind;

  if (argc != 3)
    fatal << usage;

  chord_node dst;

  if (inet_addr (argv[0]) == INADDR_NONE) {
    // yep, this still blocks.
    struct hostent *h = gethostbyname (argv[0]);
    if (!h)
      fatal << "Invalid address or hostname: " << argv[0] << "\n";
    struct in_addr *ptr = (struct in_addr *) h->h_addr;
    dst.r.hostname = inet_ntoa (*ptr);
  } else {
    dst.r.hostname = argv[0];
  }

  dst.r.port = atoi (argv[1]);
  dst.vnode_num = atoi (argv[2]);
  dst.x = make_chordID (dst.r.hostname, dst.r.port, dst.vnode_num);

  if (do_listkeys) {
    print_keys (dst);
  } else {
    print_predecessors (dst);
    print_successors (dst);
    print_fingers (dst);
  }
  amain ();
}

<commit_msg>nodeq: exit with status 1 if there were any RPC errors.<commit_after>#include <async.h>
#include <dns.h>
#include <arpc.h>
#include <aios.h>

#include <chord_types.h>
#include <chord_prot.h>
#include <accordion_prot.h>
#include <fingers_prot.h>
#include <merkle_sync_prot.h>
#include <misc_utils.h>
#include <id_utils.h>
#include <merkle_misc.h>

#include "rpclib.h"

char *usage = "Usage: nodeq [-l] [-r] host port vnode\n";

int outstanding = 0;
int errors = 0;
bool do_reverse_lookup = false;
bool do_listkeys = false;
bool do_accordion = false;

struct node {
  int i;
  chord_node_ext y;
  str hostname;

  node (int i_, chord_node_ext n) : i (i_), y (n), hostname ("") {}
};

vec<ptr<node> > successors;
vec<ptr<node> > predecessors;
vec<ptr<node> > fingers;

void
printlist (str desc, vec<ptr<node> > &lst)
{
  if (lst.size () == 0)
    return;

  aout << "=== " << desc << "\n";
  for (size_t i = 0; i < lst.size (); i++) {
    chord_node z = make_chord_node (lst[i]->y.n);
    str h;
    if (lst[i]->hostname.len () > 0)
      h = lst[i]->hostname;
    else
      h = z.r.hostname;
    aout << i << ".\t"
	 << z.x << " "
	 << h << " "
	 << z.r.port << " "
	 << z.vnode_num << " "
	 << z.knownup << " "
	 << z.age << " "
	 << z. budget << " "
	 << lst[i]->y.a_lat << " "
	 << lst[i]->y.a_var << " "
	 << lst[i]->y.nrpc << " "
	 << z.coords[0] << " "
	 << z.coords[1] << " "
	 << z.coords[2] << " "
	 << z.e
	 << "\n";
  }
}

void
finish ()
{
  printlist ("predecessors", predecessors);
  printlist ("successors", successors);
  printlist ("fingers", fingers);
  exit (errors > 0);
}

void
dns_lookup_cb (ptr<node> nn, ptr<hostent> he, int err)
{
  if (!err)
    nn->hostname = he->h_name;

  outstanding--;
  if (outstanding == 0)
    finish ();
}

void
processreslist_cb (str desc, vec<ptr<node> > *list,
		   chord_nodelistextres *res, clnt_stat status)
{
  outstanding--;
  if (status) {
    errors++;
    warnx << "no " << desc << ": " << status << "\n";
    if (outstanding == 0)
      finish ();
    return;
  }

  size_t sz = res->resok->nlist.size ();
  vec<chord_node> zs;
  for (size_t i = 0; i < sz; i++) {
    ptr<node> nn = New refcounted<node> (i, res->resok->nlist[i]);
    list->push_back (nn);

    if (do_reverse_lookup) {
      outstanding++;
      struct in_addr ar;
      ar.s_addr = htonl (res->resok->nlist[i].n.machine_order_ipv4_addr);
      dns_hostbyaddr (ar, wrap (&dns_lookup_cb, nn));
    }
  }
  delete res;

  if (outstanding == 0)
    finish ();
}

void
print_successors (const chord_node &dst)
{
  ptr<chordID> ga = New refcounted<chordID> (dst.x);
  chord_nodelistextres *lst = New chord_nodelistextres ();
  doRPC (dst, chord_program_1, CHORDPROC_GETSUCC_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "successors", &successors, lst));
  outstanding++;
}

void
print_fingers (const chord_node &dst)
{
  ptr<chordID> ga = New refcounted<chordID> (dst.x);
  chord_nodelistextres *lst = New chord_nodelistextres ();
  if (do_accordion)
    doRPC (dst, accordion_program_1, ACCORDIONPROC_GETFINGERS_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "fingers", &fingers, lst));
  else
    doRPC (dst, fingers_program_1, FINGERSPROC_GETFINGERS_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "fingers", &fingers, lst));
  outstanding++;
}

void
print_predecessors (const chord_node &dst)
{
  ptr<chordID> ga = New refcounted<chordID> (dst.x);
  chord_nodelistextres *lst = New chord_nodelistextres ();
  doRPC (dst, chord_program_1, CHORDPROC_GETPRED_EXT,
	 ga, lst,
	 wrap (processreslist_cb, "predecessors", &predecessors, lst));
  outstanding++;
}

void
getkeys_cb (const chord_node dst, ref<getkeys_arg> arg, ref<getkeys_res> res,
	    clnt_stat err)
{
  outstanding--;
  if (err) {
    warnx << "no keys: " << err << "\n";
    if (outstanding == 0)
      finish ();
    return;
  } else if (res->status != MERKLE_OK) {
    warnx << "protocol error " << res->status << "\n";
    if (outstanding == 0)
      finish ();
    return;
  }

  for (u_int i = 0; i < res->resok->keys.size (); i++) {
    const chordID &key2 = res->resok->keys[i];
    aout << key2 << "\n";
    arg->rngmin = incID (key2);
  }

  if (res->resok->morekeys) {
    doRPC (dst, merklesync_program_1, MERKLESYNC_GETKEYS,
	   arg, res,
	   wrap (getkeys_cb, dst, arg, res));
    outstanding++;
  } else {
    finish ();
  }
}

void
print_keys (const chord_node &dst)
{
  ref<getkeys_arg> arg = New refcounted<getkeys_arg> ();
  arg->ctype  = DHASH_CONTENTHASH;
  arg->rngmin = 0;
  arg->rngmax = decID (arg->rngmin);
  ref<getkeys_res> res = New refcounted<getkeys_res> ();
  doRPC (dst, merklesync_program_1, MERKLESYNC_GETKEYS,
	 arg, res,
	 wrap (getkeys_cb, dst, arg, res));
  outstanding++;
}

int
main (int argc, char *argv[])
{
  int ch;
  while ((ch = getopt (argc, argv, "rlm")) != -1)
    switch (ch) {
    case 'r':
      do_reverse_lookup = true;
      break;
    case 'l':
      do_listkeys = true;
      break;
    case 'm':
      do_accordion = true;
      break;
    default:
      fatal << usage;
      break;
    }

  argc -= optind;
  argv += optind;

  if (argc != 3)
    fatal << usage;

  chord_node dst;

  if (inet_addr (argv[0]) == INADDR_NONE) {
    // yep, this still blocks.
    struct hostent *h = gethostbyname (argv[0]);
    if (!h)
      fatal << "Invalid address or hostname: " << argv[0] << "\n";
    struct in_addr *ptr = (struct in_addr *) h->h_addr;
    dst.r.hostname = inet_ntoa (*ptr);
  } else {
    dst.r.hostname = argv[0];
  }

  dst.r.port = atoi (argv[1]);
  dst.vnode_num = atoi (argv[2]);
  dst.x = make_chordID (dst.r.hostname, dst.r.port, dst.vnode_num);

  if (do_listkeys) {
    print_keys (dst);
  } else {
    print_predecessors (dst);
    print_successors (dst);
    print_fingers (dst);
  }
  amain ();
}

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>

#include "base/command_line.h"
#include "base/process_util.h"
#include "base/test/test_suite.h"
#include "base/threading/platform_thread.h"
#include "chrome/common/chrome_paths.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"

// To enable ATL-based code to run in this module
class ChromeFrameUnittestsModule
    : public CAtlExeModuleT<ChromeFrameUnittestsModule> {
 public:
  static HRESULT InitializeCom() {
    return CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  }
};

ChromeFrameUnittestsModule _AtlModule;

const char kNoRegistrationSwitch[] = "no-registration";

void PureCall() {
  __debugbreak();
}

int main(int argc, char **argv) {
  base::EnableTerminationOnHeapCorruption();
  base::PlatformThread::SetName("ChromeFrame tests");

  _set_purecall_handler(PureCall);

  TestSuite test_suite(argc, argv);

  SetConfigBool(kChromeFrameHeadlessMode, true);
  SetConfigBool(kChromeFrameAccessibleMode, true);

  base::ProcessHandle crash_service = chrome_frame_test::StartCrashService();
  int ret = -1;
  // If mini_installer is used to register CF, we use the switch
  // --no-registration to avoid repetitive registration.
  if (CommandLine::ForCurrentProcess()->HasSwitch(kNoRegistrationSwitch)) {
    ret = test_suite.Run();
  } else {
    // Register paths needed by the ScopedChromeFrameRegistrar.
    chrome::RegisterPathProvider();

    // This will register the chrome frame in the build directory. It currently
    // leaves that chrome frame registered once the tests are done. It must be
    // constructed AFTER the TestSuite is created since TestSuites create THE
    // AtExitManager.
    // TODO(robertshield): Make these tests restore the original registration
    // once done.
    ScopedChromeFrameRegistrar registrar;

    // Register IAccessible2 proxy stub DLL, needed for some tests.
    ScopedChromeFrameRegistrar ia2_registrar(
        chrome_frame_test::GetIAccessible2ProxyStubPath().value());

    ret = test_suite.Run();
  }

  DeleteConfigValue(kChromeFrameHeadlessMode);
  DeleteConfigValue(kChromeFrameAccessibleMode);

  if (crash_service)
    base::KillProcess(crash_service, 0, false);
}
<commit_msg>Prevent extract build failures on the ChromeFrame builders caused when the chrome frame tests executable crashes while running a test by register an exception handler and terminating the relevant processes.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>

#include "base/command_line.h"
#include "base/process_util.h"
#include "base/test/test_suite.h"
#include "base/threading/platform_thread.h"
#include "chrome/common/chrome_paths.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"

// To enable ATL-based code to run in this module
class ChromeFrameUnittestsModule
    : public CAtlExeModuleT<ChromeFrameUnittestsModule> {
 public:
  static HRESULT InitializeCom() {
    return CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  }
};

ChromeFrameUnittestsModule _AtlModule;

const char kNoRegistrationSwitch[] = "no-registration";

void PureCall() {
  __debugbreak();
}

#pragma warning(disable:4509)
int main(int argc, char **argv) {
  base::EnableTerminationOnHeapCorruption();
  base::PlatformThread::SetName("ChromeFrame tests");

  _set_purecall_handler(PureCall);

  TestSuite test_suite(argc, argv);

  SetConfigBool(kChromeFrameHeadlessMode, true);
  SetConfigBool(kChromeFrameAccessibleMode, true);

  base::ProcessHandle crash_service = chrome_frame_test::StartCrashService();
  int ret = -1;

  __try {
    // If mini_installer is used to register CF, we use the switch
    // --no-registration to avoid repetitive registration.
    if (CommandLine::ForCurrentProcess()->HasSwitch(kNoRegistrationSwitch)) {
      ret = test_suite.Run();
    } else {
      // Register paths needed by the ScopedChromeFrameRegistrar.
      chrome::RegisterPathProvider();

      // This will register the chrome frame in the build directory. It
      // currently leaves that chrome frame registered once the tests are done.
      // It must be constructed AFTER the TestSuite is created since TestSuites
      // create THE AtExitManager.
      // TODO(robertshield): Make these tests restore the original registration
      // once done.
      ScopedChromeFrameRegistrar registrar;

      // Register IAccessible2 proxy stub DLL, needed for some tests.
      ScopedChromeFrameRegistrar ia2_registrar(
          chrome_frame_test::GetIAccessible2ProxyStubPath().value());

      ret = test_suite.Run();
    }
  }

  _except(EXCEPTION_EXECUTE_HANDLER) {
    LOG(ERROR) << "ChromeFrame tests crashed";
    chrome_frame_test::KillProcesses(L"iexplore.exe", 0, false);
    chrome_frame_test::KillProcesses(L"firefox.exe", 0, false);
  }

  DeleteConfigValue(kChromeFrameHeadlessMode);
  DeleteConfigValue(kChromeFrameAccessibleMode);

  if (crash_service)
    base::KillProcess(crash_service, 0, false);
}
#pragma warning(default:4509)
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>

#include "base/command_line.h"
#include "base/process_util.h"
#include "base/test/test_suite.h"
#include "base/threading/platform_thread.h"
#include "chrome/common/chrome_paths.h"
#include "chrome_frame/crash_server_init.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"

// To enable ATL-based code to run in this module
class ChromeFrameUnittestsModule
    : public CAtlExeModuleT<ChromeFrameUnittestsModule> {
 public:
  static HRESULT InitializeCom() {
    return CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  }
};

ChromeFrameUnittestsModule _AtlModule;

const char kNoRegistrationSwitch[] = "no-registration";

void PureCall() {
  __debugbreak();
}

int main(int argc, char **argv) {
  base::EnableTerminationOnHeapCorruption();
  base::PlatformThread::SetName("ChromeFrame tests");

  _set_purecall_handler(PureCall);

  base::TestSuite test_suite(argc, argv);

  SetConfigBool(kChromeFrameHeadlessMode, true);
  SetConfigBool(kChromeFrameAccessibleMode, true);

  base::ProcessHandle crash_service = chrome_frame_test::StartCrashService();

  google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
      InitializeCrashReporting(HEADLESS));

  int ret = -1;
  // If mini_installer is used to register CF, we use the switch
  // --no-registration to avoid repetitive registration.
  if (CommandLine::ForCurrentProcess()->HasSwitch(kNoRegistrationSwitch)) {
    ret = test_suite.Run();
  } else {
    // This will register the chrome frame in the build directory. It currently
    // leaves that chrome frame registered once the tests are done. It must be
    // constructed AFTER the TestSuite is created since TestSuites create THE
    // AtExitManager.
    // TODO(robertshield): Make these tests restore the original registration
    // once done.
    ScopedChromeFrameRegistrar registrar(chrome_frame_test::GetTestBedType());

    // Register IAccessible2 proxy stub DLL, needed for some tests.
    ScopedChromeFrameRegistrar ia2_registrar(
        chrome_frame_test::GetIAccessible2ProxyStubPath().value(),
        ScopedChromeFrameRegistrar::SYSTEM_LEVEL);
    ret = test_suite.Run();
  }

  DeleteConfigValue(kChromeFrameHeadlessMode);
  DeleteConfigValue(kChromeFrameAccessibleMode);

  if (crash_service)
    base::KillProcess(crash_service, 0, false);
  return ret;
}
<commit_msg>Allow crash reporting for GCF integration tests to be suppressed via command line.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>

#include "base/command_line.h"
#include "base/process_util.h"
#include "base/test/test_suite.h"
#include "base/threading/platform_thread.h"
#include "chrome/common/chrome_paths.h"
#include "chrome_frame/crash_server_init.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/chrome_frame_ui_test_utils.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"

// To enable ATL-based code to run in this module
class ChromeFrameUnittestsModule
    : public CAtlExeModuleT<ChromeFrameUnittestsModule> {
 public:
  static HRESULT InitializeCom() {
    return CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
  }
};

ChromeFrameUnittestsModule _AtlModule;

const char kNoCrashService[] = "no-crash-service";
const char kNoRegistrationSwitch[] = "no-registration";

void PureCall() {
  __debugbreak();
}

int main(int argc, char **argv) {
  base::EnableTerminationOnHeapCorruption();
  base::PlatformThread::SetName("ChromeFrame tests");

  _set_purecall_handler(PureCall);

  base::TestSuite test_suite(argc, argv);

  SetConfigBool(kChromeFrameHeadlessMode, true);
  SetConfigBool(kChromeFrameAccessibleMode, true);

  base::ProcessHandle crash_service = NULL;
  google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad;

  if (!CommandLine::ForCurrentProcess()->HasSwitch(kNoCrashService)) {
    crash_service = chrome_frame_test::StartCrashService();

    breakpad.reset(InitializeCrashReporting(HEADLESS));
  }

  int ret = -1;
  // If mini_installer is used to register CF, we use the switch
  // --no-registration to avoid repetitive registration.
  if (CommandLine::ForCurrentProcess()->HasSwitch(kNoRegistrationSwitch)) {
    ret = test_suite.Run();
  } else {
    // This will register the chrome frame in the build directory. It currently
    // leaves that chrome frame registered once the tests are done. It must be
    // constructed AFTER the TestSuite is created since TestSuites create THE
    // AtExitManager.
    // TODO(robertshield): Make these tests restore the original registration
    // once done.
    ScopedChromeFrameRegistrar registrar(chrome_frame_test::GetTestBedType());

    // Register IAccessible2 proxy stub DLL, needed for some tests.
    ScopedChromeFrameRegistrar ia2_registrar(
        chrome_frame_test::GetIAccessible2ProxyStubPath().value(),
        ScopedChromeFrameRegistrar::SYSTEM_LEVEL);
    ret = test_suite.Run();
  }

  DeleteConfigValue(kChromeFrameHeadlessMode);
  DeleteConfigValue(kChromeFrameAccessibleMode);

  if (crash_service)
    base::KillProcess(crash_service, 0, false);
  return ret;
}
<|endoftext|>


<commit_before>#include "Registry.h"

Registry::Registry(HKEY hkey, const std::wstring& key)
: hkey_(hkey), key_(key), subkey_(nullptr)
{
  if (key_.empty()) {
    throw std::logic_error("key cannot be empty");
  }
}

Registry::~Registry()
{
  if (subkey_) {
    RegCloseKey(subkey_);
    subkey_ = nullptr;
  }
}

bool Registry::open() const
{
  if (subkey_) {
    RegCloseKey(subkey_);
    subkey_ = nullptr;
  }
  return ::RegOpenKeyEx(hkey_, key_.c_str(), 0, KEY_ALL_ACCESS, &subkey_) ==
    ERROR_SUCCESS;
}

bool Registry::create() const
{
  if (subkey_) {
    RegCloseKey(subkey_);
    subkey_ = nullptr;
  }
  DWORD dispo;
  return ::RegCreateKeyEx(
    hkey_,
    key_.c_str(),
    0,
    nullptr,
    REG_OPTION_NON_VOLATILE,
    KEY_ALL_ACCESS,
    nullptr,
    &subkey_,
    &dispo
    ) == ERROR_SUCCESS;
}

bool Registry::remove() const
{
  return RegDeleteTree(hkey_, key_.c_str()) == ERROR_SUCCESS;
}

bool Registry::unset(const std::wstring& name) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  if (!exists(name)) {
    return true;
  }
  return RegDeleteKeyValue(subkey_, nullptr, name.c_str()) == ERROR_SUCCESS;
}

bool Registry::exists(const std::wstring& name) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = 0;
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_ANY,
    nullptr,
    0,
    &size);
  return rv == ERROR_SUCCESS || rv == ERROR_MORE_DATA;
}

bool Registry::get(const std::wstring& name, std::wstring& data) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = 0;
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_REG_SZ | RRF_RT_REG_BINARY | RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND,
    nullptr,
    nullptr,
    &size
    );
  if (rv != ERROR_SUCCESS) {
    return false;
  }
  if (size == 0) {
    return true;
  }
  if (size % 2) {
    // Cannot convert.
    return false;
  }
  auto buf = std::make_unique<wchar_t[]>(size / sizeof(wchar_t));
  rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND,
    nullptr,
    buf.get(),
    &size
    );
  if (rv != ERROR_SUCCESS) {
    return false;
  }
  data = std::wstring(buf.get(), (size - 1) / sizeof(wchar_t));
  return true;
}

bool Registry::get(const std::wstring& name, uint32_t& data) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = 0;
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_DWORD,
    nullptr,
    &data,
    &size
    );
  return rv == ERROR_SUCCESS;
}

bool Registry::get(const std::wstring& name, uint64_t& data) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = 0;
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_QWORD,
    nullptr,
    &data,
    &size
    );
  return rv == ERROR_SUCCESS;
}

bool Registry::set(const std::wstring& name, const std::wstring& data, DWORD type) const
{
  if (!subkey_) {
    return false;
  }
  return RegSetValueEx(
    subkey_,
    name.c_str(),
    0,
    type,
    (BYTE*)data.c_str(),
    (data.length() + 1) * sizeof(wchar_t)
    ) == ERROR_SUCCESS;
}

bool Registry::set(const std::wstring& name, const uint32_t data, DWORD type) const
{
  if (!subkey_) {
    return false;
  }
  return ::RegSetValueEx(
    subkey_,
    name.c_str(),
    0,
    type,
    (BYTE*)&data,
    sizeof(uint32_t)
    ) == ERROR_SUCCESS;
}

bool Registry::set(const std::wstring& name, const uint64_t data, DWORD type) const
{
  if (!subkey_) {
    return false;
  }
  return ::RegSetValueEx(
    subkey_,
    name.c_str(),
    0,
    type,
    (BYTE*)&data,
    sizeof(uint64_t)
    ) == ERROR_SUCCESS;
}
<commit_msg>Fix Registry numeric getters<commit_after>#include "Registry.h"

Registry::Registry(HKEY hkey, const std::wstring& key)
: hkey_(hkey), key_(key), subkey_(nullptr)
{
  if (key_.empty()) {
    throw std::logic_error("key cannot be empty");
  }
}

Registry::~Registry()
{
  if (subkey_) {
    RegCloseKey(subkey_);
    subkey_ = nullptr;
  }
}

bool Registry::open() const
{
  if (subkey_) {
    RegCloseKey(subkey_);
    subkey_ = nullptr;
  }
  return ::RegOpenKeyEx(hkey_, key_.c_str(), 0, KEY_ALL_ACCESS, &subkey_) ==
    ERROR_SUCCESS;
}

bool Registry::create() const
{
  if (subkey_) {
    RegCloseKey(subkey_);
    subkey_ = nullptr;
  }
  DWORD dispo;
  return ::RegCreateKeyEx(
    hkey_,
    key_.c_str(),
    0,
    nullptr,
    REG_OPTION_NON_VOLATILE,
    KEY_ALL_ACCESS,
    nullptr,
    &subkey_,
    &dispo
    ) == ERROR_SUCCESS;
}

bool Registry::remove() const
{
  return RegDeleteTree(hkey_, key_.c_str()) == ERROR_SUCCESS;
}

bool Registry::unset(const std::wstring& name) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  if (!exists(name)) {
    return true;
  }
  return RegDeleteKeyValue(subkey_, nullptr, name.c_str()) == ERROR_SUCCESS;
}

bool Registry::exists(const std::wstring& name) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = 0;
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_ANY,
    nullptr,
    0,
    &size);
  return rv == ERROR_SUCCESS || rv == ERROR_MORE_DATA;
}

bool Registry::get(const std::wstring& name, std::wstring& data) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = 0;
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_REG_SZ | RRF_RT_REG_BINARY | RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND,
    nullptr,
    nullptr,
    &size
    );
  if (rv != ERROR_SUCCESS) {
    return false;
  }
  if (size == 0) {
    return true;
  }
  if (size % 2) {
    // Cannot convert.
    return false;
  }
  auto buf = std::make_unique<wchar_t[]>(size / sizeof(wchar_t));
  rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND,
    nullptr,
    buf.get(),
    &size
    );
  if (rv != ERROR_SUCCESS) {
    return false;
  }
  data = std::wstring(buf.get(), (size - 1) / sizeof(wchar_t));
  return true;
}

bool Registry::get(const std::wstring& name, uint32_t& data) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = sizeof(uint32_t);
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_DWORD,
    nullptr,
    &data,
    &size
    );
  return rv == ERROR_SUCCESS;
}

bool Registry::get(const std::wstring& name, uint64_t& data) const
{
  if (!subkey_) {
    throw std::logic_error("key not open");
  }
  DWORD size = sizeof(uint64_t);
  auto rv = RegGetValue(
    subkey_,
    nullptr,
    name.c_str(),
    RRF_RT_QWORD,
    nullptr,
    &data,
    &size
    );
  return rv == ERROR_SUCCESS;
}

bool Registry::set(const std::wstring& name, const std::wstring& data, DWORD type) const
{
  if (!subkey_) {
    return false;
  }
  return RegSetValueEx(
    subkey_,
    name.c_str(),
    0,
    type,
    (BYTE*)data.c_str(),
    (data.length() + 1) * sizeof(wchar_t)
    ) == ERROR_SUCCESS;
}

bool Registry::set(const std::wstring& name, const uint32_t data, DWORD type) const
{
  if (!subkey_) {
    return false;
  }
  return ::RegSetValueEx(
    subkey_,
    name.c_str(),
    0,
    type,
    (BYTE*)&data,
    sizeof(uint32_t)
    ) == ERROR_SUCCESS;
}

bool Registry::set(const std::wstring& name, const uint64_t data, DWORD type) const
{
  if (!subkey_) {
    return false;
  }
  return ::RegSetValueEx(
    subkey_,
    name.c_str(),
    0,
    type,
    (BYTE*)&data,
    sizeof(uint64_t)
    ) == ERROR_SUCCESS;
}
<|endoftext|>


<commit_before>#include "Device.h"
#include "DeviceStream.h"
#include "cudafuncs.h"
#include <algorithm>

using namespace sqaod_cuda;

Device::Device(int devNo) {
    devNo_ = -1;
    if (devNo != -1)
        initialize(devNo);
}

Device::~Device() {
    if (devNo_ != -1)
        finalize();
}

void Device::initialize(int devNo){
    devNo_ = devNo;
    throwOnError(cudaSetDevice(devNo));
    memStore_.initialize();
    devObjAllocator_.set(&memStore_);
    defaultDeviceStream_.set(NULL, memStore_);
    devConstScalarsFP64_.initialize(devObjAllocator_, defaultDeviceStream_);
    devConstScalarsFP32_.initialize(devObjAllocator_, defaultDeviceStream_);
}

void Device::finalize() {
    synchronize();
    for (Streams::iterator it = streams_.begin(); it != streams_.end(); ++it)
        delete *it;
    defaultDeviceStream_.finalize();
    streams_.clear();
    // devObjAllocator_.finalize();
    memStore_.finalize();
    devNo_ = -1;
}

void Device::useManagedMemory(bool use) {
    memStore_.useManagedMemory(use);
}

DeviceStream *Device::newStream() {
    cudaStream_t stream;
    throwOnError(cudaStreamCreate(&stream));
    DeviceStream *deviceStream = new DeviceStream(stream, memStore_);
    streams_.pushBack(deviceStream);
    return deviceStream;
}

DeviceStream *Device::defaultStream() {
    return &defaultDeviceStream_;
}

void Device::releaseStream(DeviceStream *stream) {
    stream->releaseTempObjects();
    Streams::iterator it = std::find(streams_.begin(), streams_.end(), stream);
    assert(it != streams_.end());
    streams_.erase(it);
    if (stream != &defaultDeviceStream_)
        delete stream;
}

/* sync on device */
void Device::synchronize() {
    throwOnError(cudaDeviceSynchronize());
    for (Streams::iterator it = streams_.begin(); it != streams_.end(); ++it) {
        (*it)->releaseTempObjects();
    }
    defaultDeviceStream_.releaseTempObjects();
}
<commit_msg>Adding a check to multiple initialize() call.<commit_after>#include "Device.h"
#include "DeviceStream.h"
#include "cudafuncs.h"
#include <algorithm>

using namespace sqaod_cuda;

Device::Device(int devNo) {
    devNo_ = -1;
    if (devNo != -1)
        initialize(devNo);
}

Device::~Device() {
    if (devNo_ != -1)
        finalize();
}

void Device::initialize(int devNo){
    if (devNo_ != -1) /* already initialized */
        throwErrorIf(devNo_ != devNo, "Trying to initialize Device that is already initialized.");

    devNo_ = devNo;
    throwOnError(cudaSetDevice(devNo));
    memStore_.initialize();
    devObjAllocator_.set(&memStore_);
    defaultDeviceStream_.set(NULL, memStore_);
    devConstScalarsFP64_.initialize(devObjAllocator_, defaultDeviceStream_);
    devConstScalarsFP32_.initialize(devObjAllocator_, defaultDeviceStream_);
}

void Device::finalize() {
    synchronize();
    for (Streams::iterator it = streams_.begin(); it != streams_.end(); ++it)
        delete *it;
    defaultDeviceStream_.finalize();
    streams_.clear();
    // devObjAllocator_.finalize();
    memStore_.finalize();
    devNo_ = -1;
}

void Device::useManagedMemory(bool use) {
    memStore_.useManagedMemory(use);
}

DeviceStream *Device::newStream() {
    cudaStream_t stream;
    throwOnError(cudaStreamCreate(&stream));
    DeviceStream *deviceStream = new DeviceStream(stream, memStore_);
    streams_.pushBack(deviceStream);
    return deviceStream;
}

DeviceStream *Device::defaultStream() {
    return &defaultDeviceStream_;
}

void Device::releaseStream(DeviceStream *stream) {
    stream->releaseTempObjects();
    Streams::iterator it = std::find(streams_.begin(), streams_.end(), stream);
    assert(it != streams_.end());
    streams_.erase(it);
    if (stream != &defaultDeviceStream_)
        delete stream;
}

/* sync on device */
void Device::synchronize() {
    throwOnError(cudaDeviceSynchronize());
    for (Streams::iterator it = streams_.begin(); it != streams_.end(); ++it) {
        (*it)->releaseTempObjects();
    }
    defaultDeviceStream_.releaseTempObjects();
}
<|endoftext|>


<commit_before>/**
 * @file   XmlModel.hpp
 * @brief  XmlModel class prototype.
 * @author zer0
 * @date   2017-04-21
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_DOM_XMLMODEL_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_DOM_XMLMODEL_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>
#include <libtbag/filesystem/Path.hpp>
#include <libtbag/3rd/tinyxml2/tinyxml2.h>

#include <map>
#include <string>
#include <memory>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace dom {

/**
 * XmlModel class prototype.
 *
 * @author zer0
 * @date   2017-04-21
 */
class TBAG_API XmlModel
{
public:
    using Document = tinyxml2::XMLDocument;
    using Element  = tinyxml2::XMLElement;
    using Node     = tinyxml2::XMLNode;

    using Path   = filesystem::Path;
    using String = std::string;
    using Size   = std::size_t;

public:
    enum class Scope
    {
        WORK,
        EXE,
        HOME,
        GLOBAL,
    };

public:
    struct NodeInterface
    {
        using Document = XmlModel::Document;
        using Element  = XmlModel::Element;
        using Node     = XmlModel::Node;
        using String   = XmlModel::String;

        virtual String name() const = 0;

        virtual void setup() = 0;
        virtual void teardown() = 0;

        virtual void load(Element const & element) = 0;
        virtual void save(Element & element) const = 0;
    };

public:
    using SharedModel = std::shared_ptr<NodeInterface>;
    using WeakModel   = std::weak_ptr<NodeInterface>;
    using ModelMap    = std::map<String, SharedModel>;
    using ModelPair   = ModelMap::value_type;

private:
    ModelMap _models;

public:
    XmlModel();
    XmlModel(XmlModel const & obj);
    XmlModel(XmlModel && obj);
    virtual ~XmlModel();

public:
    XmlModel & operator =(XmlModel const & obj);
    XmlModel & operator =(XmlModel && obj);

public:
    inline void clear() TBAG_NOEXCEPT_EXPR(TBAG_NOEXCEPT_EXPR(_models.clear()))
    { _models.clear(); }
    inline Size size() const TBAG_NOEXCEPT_EXPR(TBAG_NOEXCEPT_EXPR(_models.size()))
    { return _models.size(); }
    inline bool empty() const TBAG_NOEXCEPT_EXPR(TBAG_NOEXCEPT_EXPR(_models.empty()))
    { return _models.empty(); }

public:
    bool add(SharedModel model);
    bool remove(String const & name);

public:
    WeakModel get(String const & name);

public:
    template <typename Up>
    Up * getPointer()
    {
        if (auto shared = get(Up().name()).lock()) {
            return static_cast<Up*>(shared.get());
        }
        return nullptr;
    }

public:
    virtual String getRootName() const;
    virtual String getFileName() const;

    virtual Path getWorkDir() const;
    virtual Path getExeDir() const;
    virtual Path getHomeDir() const;
    virtual Path getGlobalDir() const;

public:
    Path getFilePath(Scope scope) const;
    Path findExistsFilePathOfNearest() const;

public:
    void teardown();
    void setup();

public:
    bool save() const;
    bool save(Scope scope) const;
    bool save(Path const & path) const;

public:
    bool load();
    bool load(Scope scope);
    bool load(Path const & path);

public:
    bool loadOrDefaultSave();
    bool loadOrDefaultSave(Scope scope);
    bool loadOrDefaultSave(Path const & path);
};

} // namespace dom

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_DOM_XMLMODEL_HPP__

<commit_msg>Trivial commit.<commit_after>/**
 * @file   XmlModel.hpp
 * @brief  XmlModel class prototype.
 * @author zer0
 * @date   2017-04-21
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_DOM_XMLMODEL_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_DOM_XMLMODEL_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>
#include <libtbag/filesystem/Path.hpp>
#include <libtbag/3rd/tinyxml2/tinyxml2.h>
#include <libtbag/Type.hpp>

#include <map>
#include <string>
#include <memory>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace dom {

/**
 * XmlModel class prototype.
 *
 * @author zer0
 * @date   2017-04-21
 */
class TBAG_API XmlModel
{
public:
    using Document = tinyxml2::XMLDocument;
    using Element  = tinyxml2::XMLElement;
    using Node     = tinyxml2::XMLNode;

    using Path   = filesystem::Path;
    using String = std::string;
    using Size   = std::size_t;

public:
    enum class Scope
    {
        WORK,
        EXE,
        HOME,
        GLOBAL,
    };

public:
    struct NodeInterface
    {
        using Document = XmlModel::Document;
        using Element  = XmlModel::Element;
        using Node     = XmlModel::Node;
        using String   = XmlModel::String;

        virtual String name() const = 0;

        virtual void setup() = 0;
        virtual void teardown() = 0;

        virtual void load(Element const & element) = 0;
        virtual void save(Element & element) const = 0;
    };

public:
    using SharedModel = std::shared_ptr<NodeInterface>;
    using WeakModel   = std::weak_ptr<NodeInterface>;
    using ModelMap    = std::map<String, SharedModel>;
    using ModelPair   = ModelMap::value_type;

private:
    ModelMap _models;

public:
    XmlModel();
    XmlModel(XmlModel const & obj);
    XmlModel(XmlModel && obj);
    virtual ~XmlModel();

public:
    XmlModel & operator =(XmlModel const & obj);
    XmlModel & operator =(XmlModel && obj);

public:
    inline void clear() TBAG_NOEXCEPT_EXPR(TBAG_NOEXCEPT_EXPR(_models.clear()))
    { _models.clear(); }
    inline Size size() const TBAG_NOEXCEPT_EXPR(TBAG_NOEXCEPT_EXPR(_models.size()))
    { return _models.size(); }
    inline bool empty() const TBAG_NOEXCEPT_EXPR(TBAG_NOEXCEPT_EXPR(_models.empty()))
    { return _models.empty(); }

public:
    bool add(SharedModel model);
    bool remove(String const & name);

public:
    WeakModel get(String const & name);

public:
    template <typename Up>
    Up * getPointer()
    {
        STATIC_ASSERT_CHECK_IS_BASE_OF(NodeInterface, Up);
        if (auto shared = get(Up().name()).lock()) {
            return static_cast<Up*>(shared.get());
        }
        return nullptr;
    }

public:
    virtual String getRootName() const;
    virtual String getFileName() const;

    virtual Path getWorkDir() const;
    virtual Path getExeDir() const;
    virtual Path getHomeDir() const;
    virtual Path getGlobalDir() const;

public:
    Path getFilePath(Scope scope) const;
    Path findExistsFilePathOfNearest() const;

public:
    void teardown();
    void setup();

public:
    bool save() const;
    bool save(Scope scope) const;
    bool save(Path const & path) const;

public:
    bool load();
    bool load(Scope scope);
    bool load(Path const & path);

public:
    bool loadOrDefaultSave();
    bool loadOrDefaultSave(Scope scope);
    bool loadOrDefaultSave(Path const & path);
};

} // namespace dom

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_DOM_XMLMODEL_HPP__

<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/audio/test/audio_bwe_integration_test.h"

#include "webrtc/common_audio/wav_file.h"
#include "webrtc/rtc_base/ptr_util.h"
#include "webrtc/system_wrappers/include/sleep.h"
#include "webrtc/test/field_trial.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"

namespace webrtc {
namespace test {

namespace {
// Wait a second between stopping sending and stopping receiving audio.
constexpr int kExtraProcessTimeMs = 1000;
}  // namespace

AudioBweTest::AudioBweTest() : EndToEndTest(CallTest::kDefaultTimeoutMs) {}

size_t AudioBweTest::GetNumVideoStreams() const {
  return 0;
}
size_t AudioBweTest::GetNumAudioStreams() const {
  return 1;
}
size_t AudioBweTest::GetNumFlexfecStreams() const {
  return 0;
}

std::unique_ptr<test::FakeAudioDevice::Capturer>
AudioBweTest::CreateCapturer() {
  return test::FakeAudioDevice::CreateWavFileReader(AudioInputFile());
}

void AudioBweTest::OnFakeAudioDevicesCreated(
    test::FakeAudioDevice* send_audio_device,
    test::FakeAudioDevice* recv_audio_device) {
  send_audio_device_ = send_audio_device;
}

test::PacketTransport* AudioBweTest::CreateSendTransport(Call* sender_call) {
  return new test::PacketTransport(
      sender_call, this, test::PacketTransport::kSender,
      test::CallTest::payload_type_map_, GetNetworkPipeConfig());
}

test::PacketTransport* AudioBweTest::CreateReceiveTransport() {
  return new test::PacketTransport(
      nullptr, this, test::PacketTransport::kReceiver,
      test::CallTest::payload_type_map_, GetNetworkPipeConfig());
}

void AudioBweTest::PerformTest() {
  send_audio_device_->WaitForRecordingEnd();
  SleepMs(GetNetworkPipeConfig().queue_delay_ms + kExtraProcessTimeMs);
}

class StatsPollTask : public rtc::QueuedTask {
 public:
  explicit StatsPollTask(Call* sender_call) : sender_call_(sender_call) {}

 private:
  bool Run() override {
    RTC_CHECK(sender_call_);
    Call::Stats call_stats = sender_call_->GetStats();
    EXPECT_GT(call_stats.send_bandwidth_bps, 25000);
    rtc::TaskQueue::Current()->PostDelayedTask(
        std::unique_ptr<QueuedTask>(this), 100);
    return false;
  }
  Call* sender_call_;
};

class NoBandwidthDropAfterDtx : public AudioBweTest {
 public:
  NoBandwidthDropAfterDtx()
      : sender_call_(nullptr), stats_poller_("stats poller task queue") {}

  void ModifyAudioConfigs(
      AudioSendStream::Config* send_config,
      std::vector<AudioReceiveStream::Config>* receive_configs) override {
    send_config->send_codec_spec =
        rtc::Optional<AudioSendStream::Config::SendCodecSpec>(
            {test::CallTest::kAudioSendPayloadType,
             {"OPUS",
              48000,
              2,
              {{"ptime", "60"}, {"usedtx", "1"}, {"stereo", "1"}}}});

    send_config->min_bitrate_bps = 6000;
    send_config->max_bitrate_bps = 100000;
    send_config->rtp.extensions.push_back(
        RtpExtension(RtpExtension::kTransportSequenceNumberUri,
                     kTransportSequenceNumberExtensionId));
    for (AudioReceiveStream::Config& recv_config : *receive_configs) {
      recv_config.rtp.transport_cc = true;
      recv_config.rtp.extensions = send_config->rtp.extensions;
      recv_config.rtp.remote_ssrc = send_config->rtp.ssrc;
    }
  }

  std::string AudioInputFile() override {
    return test::ResourcePath("voice_engine/audio_dtx16", "wav");
  }

  FakeNetworkPipe::Config GetNetworkPipeConfig() override {
    FakeNetworkPipe::Config pipe_config;
    pipe_config.link_capacity_kbps = 50;
    pipe_config.queue_length_packets = 1500;
    pipe_config.queue_delay_ms = 300;
    return pipe_config;
  }

  void OnCallsCreated(Call* sender_call, Call* receiver_call) override {
    sender_call_ = sender_call;
  }

  void PerformTest() override {
    stats_poller_.PostDelayedTask(
        std::unique_ptr<rtc::QueuedTask>(new StatsPollTask(sender_call_)), 100);
    sender_call_->OnTransportOverheadChanged(webrtc::MediaType::AUDIO, 0);
    AudioBweTest::PerformTest();
  }

 private:
  Call* sender_call_;
  rtc::TaskQueue stats_poller_;
};

using AudioBweIntegrationTest = CallTest;

// TODO(tschumim): This test is flaky when run on android. Re-enable the test
// for android when the issue is fixed.
#if defined(WEBRTC_ANDROID)
#define MAYBE_NoBandwidthDropAfterDtx DISABLED_NoBandwidthDropAfterDtx
#else
#define MAYBE_NoBandwidthDropAfterDtx NoBandwidthDropAfterDtx
#endif
TEST_F(AudioBweIntegrationTest, MAYBE_NoBandwidthDropAfterDtx) {
  webrtc::test::ScopedFieldTrials override_field_trials(
      "WebRTC-Audio-SendSideBwe/Enabled/"
      "WebRTC-SendSideBwe-WithOverhead/Enabled/");
  NoBandwidthDropAfterDtx test;
  RunBaseTest(&test);
}

}  // namespace test
}  // namespace webrtc
<commit_msg>Disable flaky NoBandwidthDropAfterDtx test.<commit_after>/*
 *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/audio/test/audio_bwe_integration_test.h"

#include "webrtc/common_audio/wav_file.h"
#include "webrtc/rtc_base/ptr_util.h"
#include "webrtc/system_wrappers/include/sleep.h"
#include "webrtc/test/field_trial.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"

namespace webrtc {
namespace test {

namespace {
// Wait a second between stopping sending and stopping receiving audio.
constexpr int kExtraProcessTimeMs = 1000;
}  // namespace

AudioBweTest::AudioBweTest() : EndToEndTest(CallTest::kDefaultTimeoutMs) {}

size_t AudioBweTest::GetNumVideoStreams() const {
  return 0;
}
size_t AudioBweTest::GetNumAudioStreams() const {
  return 1;
}
size_t AudioBweTest::GetNumFlexfecStreams() const {
  return 0;
}

std::unique_ptr<test::FakeAudioDevice::Capturer>
AudioBweTest::CreateCapturer() {
  return test::FakeAudioDevice::CreateWavFileReader(AudioInputFile());
}

void AudioBweTest::OnFakeAudioDevicesCreated(
    test::FakeAudioDevice* send_audio_device,
    test::FakeAudioDevice* recv_audio_device) {
  send_audio_device_ = send_audio_device;
}

test::PacketTransport* AudioBweTest::CreateSendTransport(Call* sender_call) {
  return new test::PacketTransport(
      sender_call, this, test::PacketTransport::kSender,
      test::CallTest::payload_type_map_, GetNetworkPipeConfig());
}

test::PacketTransport* AudioBweTest::CreateReceiveTransport() {
  return new test::PacketTransport(
      nullptr, this, test::PacketTransport::kReceiver,
      test::CallTest::payload_type_map_, GetNetworkPipeConfig());
}

void AudioBweTest::PerformTest() {
  send_audio_device_->WaitForRecordingEnd();
  SleepMs(GetNetworkPipeConfig().queue_delay_ms + kExtraProcessTimeMs);
}

class StatsPollTask : public rtc::QueuedTask {
 public:
  explicit StatsPollTask(Call* sender_call) : sender_call_(sender_call) {}

 private:
  bool Run() override {
    RTC_CHECK(sender_call_);
    Call::Stats call_stats = sender_call_->GetStats();
    EXPECT_GT(call_stats.send_bandwidth_bps, 25000);
    rtc::TaskQueue::Current()->PostDelayedTask(
        std::unique_ptr<QueuedTask>(this), 100);
    return false;
  }
  Call* sender_call_;
};

class NoBandwidthDropAfterDtx : public AudioBweTest {
 public:
  NoBandwidthDropAfterDtx()
      : sender_call_(nullptr), stats_poller_("stats poller task queue") {}

  void ModifyAudioConfigs(
      AudioSendStream::Config* send_config,
      std::vector<AudioReceiveStream::Config>* receive_configs) override {
    send_config->send_codec_spec =
        rtc::Optional<AudioSendStream::Config::SendCodecSpec>(
            {test::CallTest::kAudioSendPayloadType,
             {"OPUS",
              48000,
              2,
              {{"ptime", "60"}, {"usedtx", "1"}, {"stereo", "1"}}}});

    send_config->min_bitrate_bps = 6000;
    send_config->max_bitrate_bps = 100000;
    send_config->rtp.extensions.push_back(
        RtpExtension(RtpExtension::kTransportSequenceNumberUri,
                     kTransportSequenceNumberExtensionId));
    for (AudioReceiveStream::Config& recv_config : *receive_configs) {
      recv_config.rtp.transport_cc = true;
      recv_config.rtp.extensions = send_config->rtp.extensions;
      recv_config.rtp.remote_ssrc = send_config->rtp.ssrc;
    }
  }

  std::string AudioInputFile() override {
    return test::ResourcePath("voice_engine/audio_dtx16", "wav");
  }

  FakeNetworkPipe::Config GetNetworkPipeConfig() override {
    FakeNetworkPipe::Config pipe_config;
    pipe_config.link_capacity_kbps = 50;
    pipe_config.queue_length_packets = 1500;
    pipe_config.queue_delay_ms = 300;
    return pipe_config;
  }

  void OnCallsCreated(Call* sender_call, Call* receiver_call) override {
    sender_call_ = sender_call;
  }

  void PerformTest() override {
    stats_poller_.PostDelayedTask(
        std::unique_ptr<rtc::QueuedTask>(new StatsPollTask(sender_call_)), 100);
    sender_call_->OnTransportOverheadChanged(webrtc::MediaType::AUDIO, 0);
    AudioBweTest::PerformTest();
  }

 private:
  Call* sender_call_;
  rtc::TaskQueue stats_poller_;
};

using AudioBweIntegrationTest = CallTest;

// TODO(tschumim): This test is flaky when run on android and mac. Re-enable the
// test for when the issue is fixed.
TEST_F(AudioBweIntegrationTest, DISABLED_NoBandwidthDropAfterDtx) {
  webrtc::test::ScopedFieldTrials override_field_trials(
      "WebRTC-Audio-SendSideBwe/Enabled/"
      "WebRTC-SendSideBwe-WithOverhead/Enabled/");
  NoBandwidthDropAfterDtx test;
  RunBaseTest(&test);
}

}  // namespace test
}  // namespace webrtc
<|endoftext|>