<commit_before>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "DedupBlocksPass.h"

#include <boost/optional.hpp>
#include <iterator>
#include <unordered_map>
#include <unordered_set>

#include "ControlFlow.h"
#include "DexClass.h"
#include "DexOutput.h"
#include "DexUtil.h"
#include "IRCode.h"
#include "Transform.h"
#include "Walkers.h"

/*
 * This pass removes blocks that are duplicates in a method.
 *
 * If a method has multiple blocks with the same code and the same successors,
 * delete all but one of the blocks. Naming one of them the canonical block.
 *
 * Then, reroute all the predecessors of all the blocks to that canonical block.
 */

namespace {

class DedupBlocksImpl {
 public:
  DedupBlocksImpl(const std::vector<DexClass*>& scope,
                  PassManager& mgr,
                  const DedupBlocksPass::Config& config)
      : m_scope(scope), m_mgr(mgr), m_config(config) {}

  void run() {
    walk_methods(m_scope, [this](DexMethod* method) {
      if (m_config.method_black_list.count(method) != 0) {
        return;
      }

      IRCode* code = method->get_code();
      if (code == nullptr) {
        return;
      }
      code->build_cfg();

      duplicates_t dups = collect_duplicates(code);
      if (dups.size() > 0) {
        record_stats(dups);
        deduplicate(dups, method);
      }
    });
    report_stats();
  }

 private:
  using hash_t = uint64_t;
  using duplicates_t = std::unordered_map<hash_t, std::unordered_set<Block*>>;
  const char* METRIC_BLOCKS_REMOVED = "blocks_removed";
  const char* METRIC_ELIGIBLE_BLOCKS = "eligible_blocks";
  const std::vector<DexClass*>& m_scope;
  PassManager& m_mgr;
  const DedupBlocksPass::Config& m_config;

  // map from block size to number of blocks with that size
  std::unordered_map<size_t, size_t> m_dup_sizes;

  // Find blocks with the same exact code
  duplicates_t collect_duplicates(IRCode* code) {
    const auto& blocks = code->cfg().blocks();
    std::unordered_map<hash_t, std::unordered_set<Block*>> duplicates;

    int num_eligible_blocks = 0;
    for (Block* block : blocks) {
      if (has_opcodes(block) && should_remove(code->cfg(), block)) {
        duplicates[hash(block)].insert(block);
        ++num_eligible_blocks;
      }
    }
    m_mgr.incr_metric(METRIC_ELIGIBLE_BLOCKS, num_eligible_blocks);

    remove_singletons(duplicates);
    // the hash function isn't perfect, so check behind with an equals function
    remove_invalid_sets(code, duplicates);
    return duplicates;
  }

  // remove all but one of a duplicate set. Reroute the predecessors to the
  // canonical block
  void deduplicate(const duplicates_t& dups, DexMethod* method) {
    const auto& code = method->get_code();
    for (const auto& entry : dups) {
      std::unordered_set<Block*> blocks = entry.second;

      // canon is block with lowest id.
      Block* canon = nullptr;
      size_t canon_id = std::numeric_limits<size_t>::max();
      for (Block* block : blocks) {
        size_t id = block->id();
        if (id <= canon_id) {
          canon_id = id;
          canon = block;
        }
      }

      for (Block* block : blocks) {
        if (block->id() == canon_id) {
          // We remove the debug line information because it will be incorrect
          // for every block we reroute here. When there's no debug info,
          // the jvm will report the error on the closing brace of the function.
          // It's not perfect but it's better than incorrect information.
          code->remove_debug_line_info(canon);
        } else {
          transform::replace_block(code, block, canon);
          m_mgr.incr_metric(METRIC_BLOCKS_REMOVED, 1);
        }
      }
    }
  }

  void remove_invalid_sets(IRCode* code, duplicates_t& dups) {
    for (auto it = dups.begin(); it != dups.end();) {
      std::unordered_set<Block*> blocks = it->second;
      if (!all_equal(blocks) || conflicting_try_regions(code, blocks)) {
        it = dups.erase(it);
      } else {
        ++it;
      }
    }
  }

  static bool should_remove(const ControlFlowGraph& cfg, Block* block) {
    if (!has_opcodes(block)) {
      return false;
    }

    if (is_catch(block)) {
      // TODO. Should be possible. Skip now for simplicity
      return false;
    }

    // Deal with a verification error like this
    //
    // A: new-instance v0
    //    add-int v1, v2, v3       (this is here to clarify that A != C)
    // B: v0 <init>
    //
    //    ...
    //
    // C: new-instance v0
    // D: v0 <init>
    //
    // B == D. Coalesce!
    //
    // A: new-instance v0
    //    add-int v1, v2, v3
    // B: v0 <init>
    //
    // C: new-instance v0
    //    goto B
    //
    // But the verifier doesn't like this. When it merges v0 on B,
    // it declares it to be a conflict because they were instantiated
    // on different lines.
    // See androidxref.com/6.0.1_r10/xref/art/runtime/verifier/reg_type.cc#684
    //
    // It would be impossible to write this in java, but if you tried it would
    // look like this
    //
    // if (someCondition) {
    //   Foo a;
    // } else {
    //   Foo b;
    // }
    // (a or b) = new Foo();
    //
    // We try to avoid this situation by not considering blocks that call
    // constructors, but this isn't bulletproof. FIXME
    if (calls_constructor(block)) {
      return false;
    }

    // We can't replace blocks that are involved in a fallthrough because they
    // depend on their position in the list of instructions. Deduplicating
    // will involve sending control to a block in a different place.
    for (Block* pred : block->preds()) {
      if (!has_opcodes(pred)) {
        // Skip this case because it's complicated. It should be fixed by
        // upcoming changes to the CFG
        return false;
      }

      if (is_fallthrough(cfg, pred, block)) {
        return false;
      }
    }

    for (Block* succ : block->succs()) {
      if (is_fallthrough(cfg, block, succ)) {
        return false;
      }
    }

    return true;
  }

  static bool calls_constructor(Block* block) {
    for (const auto& mie : InstructionIterable(block)) {
      if (is_invoke(mie.insn->opcode()) && is_init(mie.insn->get_method())) {
        return true;
      }
    }
    return false;
  }

  // `pred` falls through to `succ` iff their connecting edge in the CFG is a
  // goto but `pred`'s last instruction isn't a goto
  static bool is_fallthrough(const ControlFlowGraph& cfg,
                             Block* pred,
                             Block* succ) {
    always_assert_log(has_opcodes(pred), "need opcodes");
    const auto& flags = cfg.edge(pred, succ);
    const auto& last_of_pred = last_opcode(pred);

    return flags[EDGE_GOTO] && !is_goto(last_of_pred->insn->opcode());
  }

  void record_stats(const duplicates_t& duplicates) {
    for (const auto& entry : duplicates) {
      bool first = true;
      bool same_size = true;
      size_t size = 0;
      for (Block* block : entry.second) {
        size_t this_size = num_opcodes(block);
        if (first) {
          size = this_size;
          first = false;
        } else if (size != this_size) {
          same_size = false;
        }
      }
      if (same_size && size > 0) {
        m_dup_sizes[size] += entry.second.size();
      }
    }
  }

  void report_stats() {
    TRACE(DEDUP_BLOCKS,
          2,
          "%d eligible_blocks\n",
          m_mgr.get_metric(METRIC_ELIGIBLE_BLOCKS));

    for (const auto& entry : m_dup_sizes) {
      TRACE(DEDUP_BLOCKS,
            2,
            "found %d duplicate blocks with %d instructions\n",
            entry.second,
            entry.first);
    }

    TRACE(DEDUP_BLOCKS,
          1,
          "%d blocks removed\n",
          m_mgr.get_metric(METRIC_BLOCKS_REMOVED));
  }

  // remove sets with only one block
  static void remove_singletons(duplicates_t& duplicates) {
    for (auto it = duplicates.begin(); it != duplicates.end();) {
      if (it->second.size() <= 1) {
        it = duplicates.erase(it);
      } else {
        ++it;
      }
    }
  }

  static boost::optional<MethodItemEntry&> last_opcode(Block* block) {
    for (auto it = block->rbegin(); it != block->rend(); it++) {
      if (it->type == MFLOW_OPCODE) {
        return *it;
      }
    }
    return boost::none;
  }

  static bool has_opcodes(Block* block) {
    const auto& it = InstructionIterable(block);
    return it.begin() != it.end();
  }

  static size_t num_opcodes(Block* block) {
    size_t result = 0;
    const auto& iterable = InstructionIterable(block);
    for (auto it = iterable.begin(); it != iterable.end(); it++) {
      result++;
    }
    return result;
  }

  // return true if all the blocks don't jump to the same catch handler (or no
  // catch handler at all)
  static bool conflicting_try_regions(IRCode* code,
                                      std::unordered_set<Block*> blocks) {
    MethodItemEntry* active_catch = nullptr;
    bool first = true;
    for (Block* b : blocks) {
      if (first) {
        first = false;
        active_catch = transform::find_active_catch(code, b->begin());
      } else {
        if (active_catch != transform::find_active_catch(code, b->begin())) {
          return true;
        }
      }
    }
    return false;
  }

  static bool all_equal(std::unordered_set<Block*> blocks) {
    Block* canon = nullptr;
    for (auto i = blocks.begin(); i != blocks.end(); i++) {
      if (canon == nullptr) {
        canon = *i;
      } else if (!equals(canon, *i)) {
        return false;
      }
    }
    return true;
  }

  // Structural equality of opcodes except branches targets are ignored
  // because they are unknown until we sync back to DexInstructions.
  //
  // The blocks must also have the exact same successors
  static bool equals(Block* b1, Block* b2) {

    if (!same_successors(b1, b2)) {
      return false;
    }

    const auto& iterable1 = InstructionIterable(b1);
    const auto& iterable2 = InstructionIterable(b2);
    auto it1 = iterable1.begin();
    auto it2 = iterable2.begin();
    for (; it1 != iterable1.end() && it2 != iterable2.end(); it1++, it2++) {
      auto& mie1 = *it1;
      auto& mie2 = *it2;
      if (*mie1.insn != *mie2.insn) {
        return false;
      }
    }

    if (!(it1 == iterable1.end() && it2 == iterable2.end())) {
      // different lengths
      return false;
    }
    return true;
  }

  // FIXME: what if the successors are the same but the type of connecting
  // edges are different?
  // Like so:
  //
  // B0: if-eqz v0, B3
  // B1: return
  // B2: if eqz v0, B1
  // B3: add-int v1, 1
  //
  // B0's succs are B1 (fallthru) and B3 (branch)
  // B2's succs are B3 (fallthru) and B1 (branch)
  //
  // Is this possible to write in java? (because java doesn't have gotos)
  static bool same_successors(Block* b1, Block* b2) {

    const auto& b1_succs = b1->succs();
    const auto& b2_succs = b2->succs();
    if (b1_succs.size() != b2_succs.size()) {
      return false;
    }
    for (Block* b1_succ : b1_succs) {
      if (std::find(b2_succs.begin(), b2_succs.end(), b1_succ) ==
          b2_succs.end()) {
        // b1 has a succ that b2 doesn't
        return false;
      }
    }
    return true;
  }

  static hash_t hash(Block* block) {
    hash_t result = 0;
    for (auto& mie : InstructionIterable(block)) {
      result ^= mie.insn->hash();
    }
    return result;
  }

  static void print_dups(duplicates_t dups) {
    TRACE(DEDUP_BLOCKS, 4, "duplicate blocks set: {\n");
    for (const auto& entry : dups) {
      TRACE(DEDUP_BLOCKS, 4, "  hash = %lu\n", entry.first);
      for (Block* b : entry.second) {
        TRACE(DEDUP_BLOCKS, 4, "    block %d\n", b->id());
        for (const MethodItemEntry& mie : *b) {
          TRACE(DEDUP_BLOCKS, 4, "      %s\n", SHOW(mie));
        }
      }
    }
    TRACE(DEDUP_BLOCKS, 4, "} end duplicate blocks set\n");
  }
};
}

void DedupBlocksPass::run_pass(DexStoresVector& stores,
                               ConfigFiles& /* unused */,
                               PassManager& mgr) {
  const auto& scope = build_class_scope(stores);
  DedupBlocksImpl impl(scope, mgr, m_config);
  impl.run();
}

static DedupBlocksPass s_pass;
<commit_msg>Fix same successors but different edges<commit_after>/**
 * Copyright (c) 2016-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#include "DedupBlocksPass.h"

#include <boost/optional.hpp>
#include <iterator>
#include <unordered_map>
#include <unordered_set>

#include "ControlFlow.h"
#include "DexClass.h"
#include "DexOutput.h"
#include "DexUtil.h"
#include "IRCode.h"
#include "Transform.h"
#include "Walkers.h"

/*
 * This pass removes blocks that are duplicates in a method.
 *
 * If a method has multiple blocks with the same code and the same successors,
 * delete all but one of the blocks. Naming one of them the canonical block.
 *
 * Then, reroute all the predecessors of all the blocks to that canonical block.
 */

namespace {

class DedupBlocksImpl {
 public:
  DedupBlocksImpl(const std::vector<DexClass*>& scope,
                  PassManager& mgr,
                  const DedupBlocksPass::Config& config)
      : m_scope(scope), m_mgr(mgr), m_config(config) {}

  void run() {
    walk_methods(m_scope, [this](DexMethod* method) {
      if (m_config.method_black_list.count(method) != 0) {
        return;
      }

      IRCode* code = method->get_code();
      if (code == nullptr) {
        return;
      }
      code->build_cfg();

      duplicates_t dups = collect_duplicates(code);
      if (dups.size() > 0) {
        record_stats(dups);
        deduplicate(dups, method);
      }
    });
    report_stats();
  }

 private:
  using hash_t = uint64_t;
  using duplicates_t = std::unordered_map<hash_t, std::unordered_set<Block*>>;
  const char* METRIC_BLOCKS_REMOVED = "blocks_removed";
  const char* METRIC_ELIGIBLE_BLOCKS = "eligible_blocks";
  const std::vector<DexClass*>& m_scope;
  PassManager& m_mgr;
  const DedupBlocksPass::Config& m_config;

  // map from block size to number of blocks with that size
  std::unordered_map<size_t, size_t> m_dup_sizes;

  // Find blocks with the same exact code
  duplicates_t collect_duplicates(IRCode* code) {
    const auto& blocks = code->cfg().blocks();
    std::unordered_map<hash_t, std::unordered_set<Block*>> duplicates;

    int num_eligible_blocks = 0;
    for (Block* block : blocks) {
      if (has_opcodes(block) && should_remove(code->cfg(), block)) {
        duplicates[hash(block)].insert(block);
        ++num_eligible_blocks;
      }
    }
    m_mgr.incr_metric(METRIC_ELIGIBLE_BLOCKS, num_eligible_blocks);

    remove_singletons(duplicates);
    // the hash function isn't perfect, so check behind with an equals function
    remove_invalid_sets(code, duplicates);
    return duplicates;
  }

  // remove all but one of a duplicate set. Reroute the predecessors to the
  // canonical block
  void deduplicate(const duplicates_t& dups, DexMethod* method) {
    const auto& code = method->get_code();
    for (const auto& entry : dups) {
      std::unordered_set<Block*> blocks = entry.second;

      // canon is block with lowest id.
      Block* canon = nullptr;
      size_t canon_id = std::numeric_limits<size_t>::max();
      for (Block* block : blocks) {
        size_t id = block->id();
        if (id <= canon_id) {
          canon_id = id;
          canon = block;
        }
      }

      for (Block* block : blocks) {
        if (block->id() == canon_id) {
          // We remove the debug line information because it will be incorrect
          // for every block we reroute here. When there's no debug info,
          // the jvm will report the error on the closing brace of the function.
          // It's not perfect but it's better than incorrect information.
          code->remove_debug_line_info(canon);
        } else {
          transform::replace_block(code, block, canon);
          m_mgr.incr_metric(METRIC_BLOCKS_REMOVED, 1);
        }
      }
    }
  }

  void remove_invalid_sets(IRCode* code, duplicates_t& dups) {
    for (auto it = dups.begin(); it != dups.end();) {
      std::unordered_set<Block*> blocks = it->second;
      if (!all_equal(code->cfg(), blocks) ||
          conflicting_try_regions(code, blocks)) {
        it = dups.erase(it);
      } else {
        ++it;
      }
    }
  }

  static bool should_remove(const ControlFlowGraph& cfg, Block* block) {
    if (!has_opcodes(block)) {
      return false;
    }

    if (is_catch(block)) {
      // TODO. Should be possible. Skip now for simplicity
      return false;
    }

    // Deal with a verification error like this
    //
    // A: new-instance v0
    //    add-int v1, v2, v3       (this is here to clarify that A != C)
    // B: v0 <init>
    //
    //    ...
    //
    // C: new-instance v0
    // D: v0 <init>
    //
    // B == D. Coalesce!
    //
    // A: new-instance v0
    //    add-int v1, v2, v3
    // B: v0 <init>
    //
    // C: new-instance v0
    //    goto B
    //
    // But the verifier doesn't like this. When it merges v0 on B,
    // it declares it to be a conflict because they were instantiated
    // on different lines.
    // See androidxref.com/6.0.1_r10/xref/art/runtime/verifier/reg_type.cc#684
    //
    // It would be impossible to write this in java, but if you tried it would
    // look like this
    //
    // if (someCondition) {
    //   Foo a;
    // } else {
    //   Foo b;
    // }
    // (a or b) = new Foo();
    //
    // We try to avoid this situation by not considering blocks that call
    // constructors, but this isn't bulletproof. FIXME
    if (calls_constructor(block)) {
      return false;
    }

    // We can't replace blocks that are involved in a fallthrough because they
    // depend on their position in the list of instructions. Deduplicating
    // will involve sending control to a block in a different place.
    for (Block* pred : block->preds()) {
      if (!has_opcodes(pred)) {
        // Skip this case because it's complicated. It should be fixed by
        // upcoming changes to the CFG
        return false;
      }

      if (is_fallthrough(cfg, pred, block)) {
        return false;
      }
    }

    for (Block* succ : block->succs()) {
      if (is_fallthrough(cfg, block, succ)) {
        return false;
      }
    }

    return true;
  }

  static bool calls_constructor(Block* block) {
    for (const auto& mie : InstructionIterable(block)) {
      if (is_invoke(mie.insn->opcode()) && is_init(mie.insn->get_method())) {
        return true;
      }
    }
    return false;
  }

  // `pred` falls through to `succ` iff their connecting edge in the CFG is a
  // goto but `pred`'s last instruction isn't a goto
  static bool is_fallthrough(const ControlFlowGraph& cfg,
                             Block* pred,
                             Block* succ) {
    always_assert_log(has_opcodes(pred), "need opcodes");
    const auto& flags = cfg.edge(pred, succ);
    const auto& last_of_pred = last_opcode(pred);

    return flags[EDGE_GOTO] && !is_goto(last_of_pred->insn->opcode());
  }

  void record_stats(const duplicates_t& duplicates) {
    for (const auto& entry : duplicates) {
      bool first = true;
      bool same_size = true;
      size_t size = 0;
      for (Block* block : entry.second) {
        size_t this_size = num_opcodes(block);
        if (first) {
          size = this_size;
          first = false;
        } else if (size != this_size) {
          same_size = false;
        }
      }
      if (same_size && size > 0) {
        m_dup_sizes[size] += entry.second.size();
      }
    }
  }

  void report_stats() {
    TRACE(DEDUP_BLOCKS,
          2,
          "%d eligible_blocks\n",
          m_mgr.get_metric(METRIC_ELIGIBLE_BLOCKS));

    for (const auto& entry : m_dup_sizes) {
      TRACE(DEDUP_BLOCKS,
            2,
            "found %d duplicate blocks with %d instructions\n",
            entry.second,
            entry.first);
    }

    TRACE(DEDUP_BLOCKS,
          1,
          "%d blocks removed\n",
          m_mgr.get_metric(METRIC_BLOCKS_REMOVED));
  }

  // remove sets with only one block
  static void remove_singletons(duplicates_t& duplicates) {
    for (auto it = duplicates.begin(); it != duplicates.end();) {
      if (it->second.size() <= 1) {
        it = duplicates.erase(it);
      } else {
        ++it;
      }
    }
  }

  static boost::optional<MethodItemEntry&> last_opcode(Block* block) {
    for (auto it = block->rbegin(); it != block->rend(); it++) {
      if (it->type == MFLOW_OPCODE) {
        return *it;
      }
    }
    return boost::none;
  }

  static bool has_opcodes(Block* block) {
    const auto& it = InstructionIterable(block);
    return it.begin() != it.end();
  }

  static size_t num_opcodes(Block* block) {
    size_t result = 0;
    const auto& iterable = InstructionIterable(block);
    for (auto it = iterable.begin(); it != iterable.end(); it++) {
      result++;
    }
    return result;
  }

  // return true if all the blocks don't jump to the same catch handler (or no
  // catch handler at all)
  static bool conflicting_try_regions(IRCode* code,
                                      std::unordered_set<Block*> blocks) {
    MethodItemEntry* active_catch = nullptr;
    bool first = true;
    for (Block* b : blocks) {
      if (first) {
        first = false;
        active_catch = transform::find_active_catch(code, b->begin());
      } else {
        if (active_catch != transform::find_active_catch(code, b->begin())) {
          return true;
        }
      }
    }
    return false;
  }

  static bool all_equal(const ControlFlowGraph& cfg,
                        std::unordered_set<Block*> blocks) {
    Block* canon = nullptr;
    for (auto i = blocks.begin(); i != blocks.end(); i++) {
      if (canon == nullptr) {
        canon = *i;
      } else if (!equals(cfg, canon, *i)) {
        return false;
      }
    }
    return true;
  }

  // Structural equality of opcodes except branches targets are ignored
  // because they are unknown until we sync back to DexInstructions.
  //
  // The blocks must also have the exact same successors
  static bool equals(const ControlFlowGraph& cfg, Block* b1, Block* b2) {

    if (!same_successors(cfg, b1, b2)) {
      return false;
    }

    const auto& iterable1 = InstructionIterable(b1);
    const auto& iterable2 = InstructionIterable(b2);
    auto it1 = iterable1.begin();
    auto it2 = iterable2.begin();
    for (; it1 != iterable1.end() && it2 != iterable2.end(); it1++, it2++) {
      auto& mie1 = *it1;
      auto& mie2 = *it2;
      if (*mie1.insn != *mie2.insn) {
        return false;
      }
    }

    if (!(it1 == iterable1.end() && it2 == iterable2.end())) {
      // different lengths
      return false;
    }
    return true;
  }

  static bool same_successors(const ControlFlowGraph& cfg,
                              Block* b1,
                              Block* b2) {
    const auto& b1_succs = b1->succs();
    const auto& b2_succs = b2->succs();
    if (b1_succs.size() != b2_succs.size()) {
      return false;
    }
    for (Block* b1_succ : b1_succs) {
      const auto& in_b2 = std::find(b2_succs.begin(), b2_succs.end(), b1_succ);
      if (in_b2 == b2_succs.end()) {
        // b1 has a succ that b2 doesn't
        return false;
      }

      if (cfg.edge(b1, b1_succ) != cfg.edge(b2, *in_b2)) {
        // successors are the same but the type of connecting
        // edges are different.
        // Like so:
        //
        // B0: if-eqz v0, B3
        // B1: return
        // B2: if eqz v0, B1
        // B3: add-int v1, 1
        //
        // B0's succs are B1 (fallthru) and B3 (branch)
        // B2's succs are B3 (fallthru) and B1 (branch)
        return false;
      }
    }
    return true;
  }

  static hash_t hash(Block* block) {
    hash_t result = 0;
    for (auto& mie : InstructionIterable(block)) {
      result ^= mie.insn->hash();
    }
    return result;
  }

  static void print_dups(duplicates_t dups) {
    TRACE(DEDUP_BLOCKS, 4, "duplicate blocks set: {\n");
    for (const auto& entry : dups) {
      TRACE(DEDUP_BLOCKS, 4, "  hash = %lu\n", entry.first);
      for (Block* b : entry.second) {
        TRACE(DEDUP_BLOCKS, 4, "    block %d\n", b->id());
        for (const MethodItemEntry& mie : *b) {
          TRACE(DEDUP_BLOCKS, 4, "      %s\n", SHOW(mie));
        }
      }
    }
    TRACE(DEDUP_BLOCKS, 4, "} end duplicate blocks set\n");
  }
};
}

void DedupBlocksPass::run_pass(DexStoresVector& stores,
                               ConfigFiles& /* unused */,
                               PassManager& mgr) {
  const auto& scope = build_class_scope(stores);
  DedupBlocksImpl impl(scope, mgr, m_config);
  impl.run();
}

static DedupBlocksPass s_pass;
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <string>
#include <atomic>
#include <memory>
#include <chrono>
#include <iostream>
#include <random>
#include <thread>
#include <future>
#include <iostream>
#include "concurrent.hpp"

namespace {
   typedef std::chrono::steady_clock clock;

   // Random function from http://www2.research.att.com/~bs/C++0xFAQ.html#std-random
   int random_int(int low, int high) {
      using namespace std;
      static std::random_device rd; // Seed with a real random value, if available
      static default_random_engine engine{rd()};
      typedef uniform_int_distribution<int> Distribution;
      static Distribution distribution{};

      return distribution(engine, Distribution::param_type{low, high});
   }

   struct DummyObject { };

   // Verify clean destruction

   struct TrueAtExit {
      std::atomic<bool>* flag;
      explicit TrueAtExit(std::atomic<bool>* f) : flag(f) { flag->store(false); }
      bool value() { return *flag; }
      ~TrueAtExit() { flag->store(true); }
      // concurrent improvement from the original Herb Sutter example
      // Which would copy/move the object into the concurrent wrapper
      // i.e the original concurrent wrapper could not use an object such as this (or a unique_ptr for that matter)
      TrueAtExit(const TrueAtExit&) = delete;
      TrueAtExit& operator=(const TrueAtExit&) = delete;
   };

   // Verify concurrent runs,. "no" delay for the caller.
   struct DelayedCaller {
      void DoDelayedCall() { std::this_thread::sleep_for(std::chrono::seconds(1));   }
   };

   // To verify that it is FIFO access
   class FlipOnce {
      std::atomic<size_t>* _stored_counter;
      std::atomic<size_t>* _stored_attempts;
      bool _is_flipped;
      size_t _counter;
      size_t _attempts;

   public:

      explicit FlipOnce(std::atomic<size_t>* c, std::atomic<size_t>* t)
      : _stored_counter(c), _stored_attempts(t)
      , _is_flipped(false), _counter(0), _attempts(0) { }

      ~FlipOnce() {
         (*_stored_counter) = _counter;
         (*_stored_attempts) = _attempts;
      }

      // Void flip will  count up NON ATOMIC internal variables. They are non atomic to avoid
      // any kind of unforseen atomic synchronization. Only in the destructor will the values
      // be saved to the atomic storages
      void doFlip() {
         if (!_is_flipped) {
            std::this_thread::sleep_for(std::chrono::milliseconds(random_int(0, 1000)));
            _is_flipped = true;
            _counter++;
         }
         _attempts++;
      }
   };

   struct Animal {  virtual std::string sound() = 0;  };

   struct Dog : public Animal {
      std::string sound() override {   return  {"Wof Wof"};}
   };
   struct Cat : public Animal {
      std::string sound() override {  return  {"Miauu Miauu"};}
   };
} // anonymous

TEST(TestOfConcurrent, CompilerCheckForEmptyStruct) {
   concurrent<DummyObject> doNothing1{};
   concurrent<DummyObject> doNothing2;
   concurrent<DummyObject> doNothing3 = {};
}

TEST(TestOfConcurrent, CompilerCheckUniquePtrTest) {
   typedef std::unique_ptr<Animal> RaiiAnimal;
   concurrent<RaiiAnimal> animal1{new Dog};
   concurrent<RaiiAnimal> animal2{new Cat};

   auto make_sound = [](RaiiAnimal & animal) {
      return animal->sound();
   };
   EXPECT_EQ("Wof Wof", animal1(make_sound).get());
   EXPECT_EQ("Miauu Miauu", animal2(make_sound).get());
}

TEST(TestOfConcurrent, VerifyDestruction) {
   std::atomic<bool> flag{true};
   {
      concurrent<TrueAtExit> notifyAtExit1{&flag};
      EXPECT_FALSE(flag); // i.e. constructor has run
   }
   {
      EXPECT_TRUE(flag); // notifyAtExit destructor
      concurrent<TrueAtExit> notifyAtExit2 = {&flag};
      EXPECT_FALSE(flag);
   }
   EXPECT_TRUE(flag); // notifyAtExit destructor
}

TEST(TestOfConcurrent, VerifyFifoCalls) {

   concurrent<std::string> asyncString = {"start"};
   auto received = asyncString([](std::string & s) {
      s.append(" received message"); return std::string{s}; });

   auto clear = asyncString([](std::string & s) {
      s.clear(); return s; });

   EXPECT_EQ("start received message", received.get());
   EXPECT_EQ("", clear.get());

   std::string toCompare;
   for (size_t index = 0; index < 100000; ++index) {
      toCompare.append(std::to_string(index)).append(" ");
      asyncString([ = ](std::string & s){s.append(std::to_string(index)).append(" ");});
   }

   auto appended = asyncString([](const std::string & s) {
      return s; });
   EXPECT_EQ(appended.get(), toCompare);
}

TEST(TestOfConcurrent, VerifyImmediateReturnForSlowFunctionCalls) {
   auto start = clock::now();
   {
      concurrent<DelayedCaller> snail;
      for (size_t call = 0; call < 10; ++call) {
         snail([](DelayedCaller & slowRunner) {
            slowRunner.DoDelayedCall(); });
      }
      EXPECT_LT(std::chrono::duration_cast<std::chrono::seconds>(clock::now() - start).count(), 1);
   } // at destruction all 1 second calls will be executed before we quit

   EXPECT_TRUE(std::chrono::duration_cast<std::chrono::seconds>(clock::now() - start).count() >= 10); //
}

std::future<void> DoAFlip(concurrent<FlipOnce>& flipper) {
   return flipper([] (FlipOnce & obj) {
      obj.doFlip(); });
}
TEST(TestOfConcurrent, IsConcurrentReallyAsyncWithFifoGuarantee__Wait1Minute) {
   auto start = clock::now();

   for (size_t howmanyflips = 0; howmanyflips < 60; ++howmanyflips) {
      std::atomic<size_t> count_of_flip{0};
      std::atomic<size_t> total_thread_access{0};
      {
         concurrent<FlipOnce> flipOnceObject{&count_of_flip, &total_thread_access};
         ASSERT_EQ(0, count_of_flip);
         auto try0 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try1 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try2 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try3 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try4 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try5 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try6 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try7 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try8 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try9 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));

         // scope exit. ALL jobs will be executed before this finished.
         //This means that all 10 jobs in the loop must be done
         // all 10 will wait here till they are finished
      }
      auto total_time = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start).count();
      std::cout << "Run " << howmanyflips << ", took: " << total_time << " milliseconds" << std::endl;
      start = clock::now();
      ASSERT_EQ(1, count_of_flip);
      ASSERT_EQ(10, total_thread_access);
   }
}

<commit_msg>Improved test. Repeat the same but with atomics inside. It should be the same result<commit_after>#include <gtest/gtest.h>
#include <string>
#include <atomic>
#include <memory>
#include <chrono>
#include <iostream>
#include <random>
#include <thread>
#include <future>
#include <cassert>
#include <iostream>
#include "concurrent.hpp"

namespace {
   typedef std::chrono::steady_clock clock;

   // Random function from http://www2.research.att.com/~bs/C++0xFAQ.html#std-random
   int random_int(int low, int high) {
      using namespace std;
      static std::random_device rd; // Seed with a real random value, if available
      static default_random_engine engine{rd()};
      typedef uniform_int_distribution<int> Distribution;
      static Distribution distribution{};

      return distribution(engine, Distribution::param_type{low, high});
   }

   struct DummyObject { };

   // Verify clean destruction

   struct TrueAtExit {
      std::atomic<bool>* flag;
      explicit TrueAtExit(std::atomic<bool>* f) : flag(f) { flag->store(false); }
      bool value() { return *flag; }
      ~TrueAtExit() { flag->store(true); }
      // concurrent improvement from the original Herb Sutter example
      // Which would copy/move the object into the concurrent wrapper
      // i.e the original concurrent wrapper could not use an object such as this (or a unique_ptr for that matter)
      TrueAtExit(const TrueAtExit&) = delete;
      TrueAtExit& operator=(const TrueAtExit&) = delete;
   };

   // Verify concurrent runs,. "no" delay for the caller.
   struct DelayedCaller {
      void DoDelayedCall() { std::this_thread::sleep_for(std::chrono::seconds(1));   }
   };

   // To verify that it is FIFO access
   class FlipOnce {
      std::atomic<size_t>* _stored_counter;
      std::atomic<size_t>* _stored_attempts;
      bool _is_flipped;
      size_t _counter;
      size_t _attempts;

   public:

      explicit FlipOnce(std::atomic<size_t>* c, std::atomic<size_t>* t)
      : _stored_counter(c), _stored_attempts(t)
      , _is_flipped(false), _counter(0), _attempts(0) { }

      ~FlipOnce() {
         if (0 == *_stored_counter) {
            std::cout << "FlipOnce was with no atomics in the doFlip operation " << std::endl;
            (*_stored_counter) = _counter;
            (*_stored_attempts) = _attempts;
         } else {
            std::cout << "FlipOnce was WITH atomics in the doFlipAtomic operation " << std::endl;
            assert(0==_counter);
            assert(0==_attempts);
         }
      }

      // Void flip will  count up NON ATOMIC internal variables. They are non atomic to avoid
      // any kind of unforseen atomic synchronization. Only in the destructor will the values
      // be saved to the atomic storages
      void doFlip() {
         if (!_is_flipped) {
            std::this_thread::sleep_for(std::chrono::milliseconds(random_int(0, 1000)));
            _is_flipped = true;
            _counter++;
         }
         _attempts++;
      }

      void doFlipAtomic() {
            if (!_is_flipped) {
            std::this_thread::sleep_for(std::chrono::milliseconds(random_int(0, 1000)));
            _is_flipped = true;
            (*_stored_counter)++;
         }
         (*_stored_attempts)++;
      }
   };

   struct Animal {  virtual std::string sound() = 0;  };

   struct Dog : public Animal {
      std::string sound() override {   return  {"Wof Wof"};}
   };
   struct Cat : public Animal {
      std::string sound() override {  return  {"Miauu Miauu"};}
   };
} // anonymous

TEST(TestOfConcurrent, CompilerCheckForEmptyStruct) {
   concurrent<DummyObject> doNothing1{};
   concurrent<DummyObject> doNothing2;
   concurrent<DummyObject> doNothing3 = {};
}

TEST(TestOfConcurrent, CompilerCheckUniquePtrTest) {
   typedef std::unique_ptr<Animal> RaiiAnimal;
   concurrent<RaiiAnimal> animal1{new Dog};
   concurrent<RaiiAnimal> animal2{new Cat};

   auto make_sound = [](RaiiAnimal & animal) {
      return animal->sound();
   };
   EXPECT_EQ("Wof Wof", animal1(make_sound).get());
   EXPECT_EQ("Miauu Miauu", animal2(make_sound).get());
}

TEST(TestOfConcurrent, VerifyDestruction) {
   std::atomic<bool> flag{true};
   {
      concurrent<TrueAtExit> notifyAtExit1{&flag};
      EXPECT_FALSE(flag); // i.e. constructor has run
   }
   {
      EXPECT_TRUE(flag); // notifyAtExit destructor
      concurrent<TrueAtExit> notifyAtExit2 = {&flag};
      EXPECT_FALSE(flag);
   }
   EXPECT_TRUE(flag); // notifyAtExit destructor
}

TEST(TestOfConcurrent, VerifyFifoCalls) {

   concurrent<std::string> asyncString = {"start"};
   auto received = asyncString([](std::string & s) {
      s.append(" received message"); return std::string{s}; });

   auto clear = asyncString([](std::string & s) {
      s.clear(); return s; });

   EXPECT_EQ("start received message", received.get());
   EXPECT_EQ("", clear.get());

   std::string toCompare;
   for (size_t index = 0; index < 100000; ++index) {
      toCompare.append(std::to_string(index)).append(" ");
      asyncString([ = ](std::string & s){s.append(std::to_string(index)).append(" ");});
   }

   auto appended = asyncString([](const std::string & s) {
      return s; });
   EXPECT_EQ(appended.get(), toCompare);
}

TEST(TestOfConcurrent, VerifyImmediateReturnForSlowFunctionCalls) {
   auto start = clock::now();
   {
      concurrent<DelayedCaller> snail;
      for (size_t call = 0; call < 10; ++call) {
         snail([](DelayedCaller & slowRunner) {
            slowRunner.DoDelayedCall(); });
      }
      EXPECT_LT(std::chrono::duration_cast<std::chrono::seconds>(clock::now() - start).count(), 1);
   } // at destruction all 1 second calls will be executed before we quit

   EXPECT_TRUE(std::chrono::duration_cast<std::chrono::seconds>(clock::now() - start).count() >= 10); //
}

std::future<void> DoAFlip(concurrent<FlipOnce>& flipper) {
   return flipper([] (FlipOnce & obj) {
      obj.doFlip(); });
}
TEST(TestOfConcurrent, IsConcurrentReallyAsyncWithFifoGuarantee__Wait1Minute) {
   auto start = clock::now();

   for (size_t howmanyflips = 0; howmanyflips < 60; ++howmanyflips) {
      std::atomic<size_t> count_of_flip{0};
      std::atomic<size_t> total_thread_access{0};
      {
         concurrent<FlipOnce> flipOnceObject{&count_of_flip, &total_thread_access};
         ASSERT_EQ(0, count_of_flip);
         auto try0 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try1 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try2 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try3 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try4 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try5 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try6 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try7 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try8 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));
         auto try9 = std::async(std::launch::async, DoAFlip, std::ref(flipOnceObject));

         // scope exit. ALL jobs will be executed before this finished.
         //This means that all 10 jobs in the loop must be done
         // all 10 will wait here till they are finished
      }
      auto total_time = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start).count();
      std::cout << "Run " << howmanyflips << ", took: " << total_time << " milliseconds" << std::endl;
      start = clock::now();
      ASSERT_EQ(1, count_of_flip);
      ASSERT_EQ(10, total_thread_access);
   }
}

std::future<void> DoAFlipAtomic(concurrent<FlipOnce>& flipper) {
   return flipper([] (FlipOnce & obj) {
      obj.doFlipAtomic(); });
}
TEST(TestOfConcurrent, IsConcurrentReallyAsyncWithFifoGuarantee__AtomicInside_Wait1Minute) {
   auto start = clock::now();

   for (size_t howmanyflips = 0; howmanyflips < 60; ++howmanyflips) {
      std::atomic<size_t> count_of_flip{0};
      std::atomic<size_t> total_thread_access{0};
      {
         concurrent<FlipOnce> flipOnceObject{&count_of_flip, &total_thread_access};
         ASSERT_EQ(0, count_of_flip);
         auto try0 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try1 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try2 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try3 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try4 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try5 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try6 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try7 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try8 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));
         auto try9 = std::async(std::launch::async, DoAFlipAtomic, std::ref(flipOnceObject));

         // scope exit. ALL jobs will be executed before this finished.
         //This means that all 10 jobs in the loop must be done
         // all 10 will wait here till they are finished
      }
      auto total_time = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - start).count();
      std::cout << "Run 2: " << howmanyflips << ", took: " << total_time << " milliseconds" << std::endl;
      start = clock::now();
      ASSERT_EQ(1, count_of_flip);
      ASSERT_EQ(10, total_thread_access);
   }
}

<|endoftext|>


<commit_before>#include <boost/test/unit_test.hpp>
#include <cinatra/body_parser.hpp>

using namespace cinatra;

BOOST_AUTO_TEST_CASE(urlencoded_body_parser_test)
{
    CaseMap cm = urlencoded_body_parser("k%261=v%3d1&k%262=v%3d2");
    BOOST_CHECK(cm["k%261"] == "v%3d1");
    BOOST_CHECK(cm["k%262"] == "v%3d2");
}
<commit_msg>fix test<commit_after>#include <boost/test/unit_test.hpp>
#include <cinatra/body_parser.hpp>

using namespace cinatra;

BOOST_AUTO_TEST_CASE(urlencoded_body_parser_test)
{
    CaseMap cm = urlencoded_body_parser("k%261=v%3d1&k%262=v%3d2");
    BOOST_CHECK(cm["k&1"] == "v=1");
    BOOST_CHECK(cm["k&2"] == "v=2");
}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include <iostream>
#include <dgcrypto/dgcrypto.hh>
#include <rsa-crypt-lib/rsa-crypt-lib.hh>

TEST(RSACrypt, RsaKeysConstructor1024) {
  RsaKeys my_keys(512);
  EXPECT_GT(my_keys.totient(), my_keys.e());

}

TEST(RSACrypt, RsaDecodeEncode1) {
  RsaKeys my_keys(1024);
  //std::cerr<<"Encoded value"<<my_keys.encode_result()<<std::endl;
  EXPECT_EQ("Hello my name is Rafa", my_keys.decoded_result());
}
 TEST(RSACrypt, RsaDecodeEncode2) {
  RsaKeys my_keys(1024);
  //std::cerr<<"Encoded value"<<my_keys.encode_result()<<std::endl;
  //std::cerr<<"decoded result"<<my_keys.decoded_result()<<std::endl;
  EXPECT_EQ("Hello my name is Rafa", my_keys.decoded_result());
}
<commit_msg>Added tests for new encrypt/decrypt functions<commit_after>#include <gtest/gtest.h>

#include <iostream>
#include <dgcrypto/dgcrypto.hh>
#include <rsa-crypt-lib/rsa-crypt-lib.hh>

TEST(RSACrypt, RsaKeysConstructor1024) {
  RsaKeys my_keys(512);
  EXPECT_GT(my_keys.totient(), my_keys.e());
}
TEST(RSACrypt, RSAEncrypt){
  RsaKeys my_keys(512);
  std::string message = "attack at dawn";
  std::string encrypted = my_keys.encrypt_message(message);
  EXPECT_NE(message, encrypted);
  EXPECT_EQ(message, my_keys.decrypt_message(encrypted));
}

TEST(RSACrypt, RSAEncryptUpperCase){
  RsaKeys my_keys(512);
  std::string message = "ATTACK AT DAWN";
  std::string encrypted = my_keys.encrypt_message(message);
  EXPECT_NE(message, encrypted);
  EXPECT_EQ(message, my_keys.decrypt_message(encrypted));
}

TEST(RSACrypt, RSAOverflowTest){
  RsaKeys my_keys(512);
  std::string message = "a";
  for(int i = 0; i < 100; ++i){
    message += "aaa";
    std::string encrypted = my_keys.encrypt_message(message);
    EXPECT_EQ(message, my_keys.decrypt_message(encrypted));
  }
}
TEST(RSACrypt, RSAEncryptMonkeyTest){
  RsaKeys my_keys(512);
  std::string message = "aaaaaaaaaaaaaaaaLONG ASS MSG FROM A MONKEYas;dlfjasd;fasdjgkasdfj 29835892069qf )@#(*%Y)aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa asdfasdf  sadfRO SALKFROO )(*&^)@#%& rolling face on keyboard asdfaskdfljasZBLAHABLHASDFfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

  std::string encrypted = my_keys.encrypt_message(message);
  EXPECT_NE(message, encrypted);
  EXPECT_EQ(message, my_keys.decrypt_message(encrypted));
}

<|endoftext|>


<commit_before>/**
 * This file is part of the CernVM File System.
 */

#include <gtest/gtest.h>

#include "../../cvmfs/util.h"
#include "testutil.h"

class T_ManagedExec : public ::testing::Test {
};

TEST_F(T_ManagedExec, RunShell) {
  int fd_stdin;
  int fd_stdout;
  int fd_stderr;
  bool retval = Shell(&fd_stdin, &fd_stdout, &fd_stderr);
  EXPECT_TRUE(retval);

  Pipe shell_pipe(fd_stdout, fd_stdin);
  const char *command = "echo \"Hello World\"\n";
  retval = shell_pipe.Write(command, strlen(command));
  EXPECT_TRUE(retval);

  char buffer[20];
  retval = shell_pipe.Read(&buffer, 11);
  EXPECT_TRUE(retval);
  buffer[11] = '\0';

  EXPECT_EQ(0, strncmp("Hello World", buffer, 12));
}

TEST_F(T_ManagedExec, ExecuteBinaryDoubleFork) {
  int fd_stdin, fd_stdout, fd_stderr;
  pid_t child_pid;

  // find gdb in the $PATH of this system
  const std::string gdb = GetExecutablePath("gdb");
  ASSERT_NE("", gdb) << "gdb not found, but needed by this test case";

  // spawn detached (double forked) child process
  const bool double_fork = true;
  bool retval = ExecuteBinary(&fd_stdin,
                              &fd_stdout,
                              &fd_stderr,
                               gdb,
                               std::vector<std::string>(),
                               double_fork,
                              &child_pid);
  // check if process is running
  ASSERT_TRUE(retval);
  EXPECT_EQ(0, kill(child_pid, 0));

  // check that the PPID of the process is 1 (belongs to init)
  pid_t child_parent_pid = GetParentPid(child_pid);
  EXPECT_EQ(1, child_parent_pid);

  // tell the process to terminate
  Pipe shell_pipe(fd_stdout, fd_stdin);
  const char *quit = "quit\n";
  retval = shell_pipe.Write(quit, strlen(quit));
  EXPECT_TRUE(retval);
  shell_pipe.Close();
  close(fd_stderr);

  // wait for the child process to terminate
  const unsigned int timeout = 1000;
  unsigned int counter = 0;
  while (counter < timeout && kill(child_pid, 0) == 0) {
    usleep(5000);
    ++counter;
  }
  EXPECT_LT(counter, timeout) << "detached process did not terminate in time";
}

TEST_F(T_ManagedExec, ExecuteBinaryAsChild) {
  int fd_stdin, fd_stdout, fd_stderr;
  pid_t child_pid;
  pid_t my_pid = getpid();

  // spawn a child process (not double forked)
  const bool double_fork = false;
  bool retval = ExecuteBinary(&fd_stdin,
                              &fd_stdout,
                              &fd_stderr,
                               "gdb",
                               std::vector<std::string>(),
                               double_fork,
                              &child_pid);

  // check that the process is running
  ASSERT_TRUE(retval);
  EXPECT_EQ(0, kill(child_pid, 0));

  // check that we are the parent of the spawned process
  pid_t child_parent_pid = GetParentPid(child_pid);
  EXPECT_EQ(my_pid, child_parent_pid);

  // tell the process to terminate
  Pipe shell_pipe(fd_stdout, fd_stdin);
  const char *quit = "quit\n";
  retval = shell_pipe.Write(quit, strlen(quit));
  EXPECT_TRUE(retval);
  shell_pipe.Close();
  close(fd_stderr);

  // wait for the child process to terminate
  int statloc;
  retval = waitpid(child_pid, &statloc, 0);
  EXPECT_NE(-1, retval);
}
<commit_msg>add a method to discover a debugger<commit_after>/**
 * This file is part of the CernVM File System.
 */

#include <gtest/gtest.h>

#include "../../cvmfs/util.h"
#include "testutil.h"

class T_ManagedExec : public ::testing::Test {
 protected:
  std::string GetDebugger() const {
    // check if we have a GDB installed
    const std::string gdb = GetExecutablePath("gdb");
    if (!gdb.empty()) {
      return gdb;
    }

    // maybe we are on a recent OS X and we find LLDB?
    return GetExecutablePath("lldb");
  }
};

TEST_F(T_ManagedExec, RunShell) {
  int fd_stdin;
  int fd_stdout;
  int fd_stderr;
  bool retval = Shell(&fd_stdin, &fd_stdout, &fd_stderr);
  EXPECT_TRUE(retval);

  Pipe shell_pipe(fd_stdout, fd_stdin);
  const char *command = "echo \"Hello World\"\n";
  retval = shell_pipe.Write(command, strlen(command));
  EXPECT_TRUE(retval);

  char buffer[20];
  retval = shell_pipe.Read(&buffer, 11);
  EXPECT_TRUE(retval);
  buffer[11] = '\0';

  EXPECT_EQ(0, strncmp("Hello World", buffer, 12));
}

TEST_F(T_ManagedExec, ExecuteBinaryDoubleFork) {
  int fd_stdin, fd_stdout, fd_stderr;
  pid_t child_pid;

  // find gdb in the $PATH of this system
  const std::string gdb = GetExecutablePath("gdb");
  ASSERT_NE("", gdb) << "gdb not found, but needed by this test case";

  // spawn detached (double forked) child process
  const bool double_fork = true;
  bool retval = ExecuteBinary(&fd_stdin,
                              &fd_stdout,
                              &fd_stderr,
                               gdb,
                               std::vector<std::string>(),
                               double_fork,
                              &child_pid);
  // check if process is running
  ASSERT_TRUE(retval);
  EXPECT_EQ(0, kill(child_pid, 0));

  // check that the PPID of the process is 1 (belongs to init)
  pid_t child_parent_pid = GetParentPid(child_pid);
  EXPECT_EQ(1, child_parent_pid);

  // tell the process to terminate
  Pipe shell_pipe(fd_stdout, fd_stdin);
  const char *quit = "quit\n";
  retval = shell_pipe.Write(quit, strlen(quit));
  EXPECT_TRUE(retval);
  shell_pipe.Close();
  close(fd_stderr);

  // wait for the child process to terminate
  const unsigned int timeout = 1000;
  unsigned int counter = 0;
  while (counter < timeout && kill(child_pid, 0) == 0) {
    usleep(5000);
    ++counter;
  }
  EXPECT_LT(counter, timeout) << "detached process did not terminate in time";
}

TEST_F(T_ManagedExec, ExecuteBinaryAsChild) {
  int fd_stdin, fd_stdout, fd_stderr;
  pid_t child_pid;
  pid_t my_pid = getpid();

  // spawn a child process (not double forked)
  const bool double_fork = false;
  bool retval = ExecuteBinary(&fd_stdin,
                              &fd_stdout,
                              &fd_stderr,
                               "gdb",
                               std::vector<std::string>(),
                               double_fork,
                              &child_pid);

  // check that the process is running
  ASSERT_TRUE(retval);
  EXPECT_EQ(0, kill(child_pid, 0));

  // check that we are the parent of the spawned process
  pid_t child_parent_pid = GetParentPid(child_pid);
  EXPECT_EQ(my_pid, child_parent_pid);

  // tell the process to terminate
  Pipe shell_pipe(fd_stdout, fd_stdin);
  const char *quit = "quit\n";
  retval = shell_pipe.Write(quit, strlen(quit));
  EXPECT_TRUE(retval);
  shell_pipe.Close();
  close(fd_stderr);

  // wait for the child process to terminate
  int statloc;
  retval = waitpid(child_pid, &statloc, 0);
  EXPECT_NE(-1, retval);
}
<|endoftext|>


<commit_before>/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

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

	This program 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 Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "Timeval.h"
#include <iostream>
#include <assert.h>

using namespace std;

int main(int argc, char *argv[])
{
	Timeval then(10000);
	assert(then.elapsed() == -10000);
	cerr << then << " elapsed: " << then.elapsed() << endl;
	double then_seconds = then.seconds();
	double last_now = Timeval().seconds();
	long last_remaining = 10000;
	int loops = 0;

	while (!then.passed()) {
		double tnow = Timeval().seconds();
		cerr << "now: " << tnow << " then: " << then << " remaining: " << then.remaining() << endl;
		assert(last_now <= tnow && last_remaining >= then.remaining());
		assert(then_seconds == then.seconds());
		usleep(500000);
		loops++;
	}
	cerr << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
	assert(then.remaining() <= 0);
	assert(loops >= 18);

	printf("Done\n");
}
<commit_msg>tests: TimevalTest: refactor and avoid double comparison<commit_after>/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

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

	This program 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 Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "Timeval.h"
#include <iostream>
#include <assert.h>
#include <sys/time.h>

using namespace std;

int main(int argc, char *argv[])
{
	long last_remaining = 10000;
	Timeval then(last_remaining);
	assert(then.elapsed() == -last_remaining);
	cerr << then << " elapsed: " << then.elapsed() << endl;

	/* Check that last_remaining parameter affects setting time in the future */
	usleep(10000);
	double increased_time_secs = Timeval().seconds();
	assert(increased_time_secs <= then.seconds());

	struct timespec invariant_time  = then.timespec();
	int loops = 0;

	while (!then.passed()) {
		struct timespec tspecnow = then.timespec();
		cerr << "now: " << Timeval().seconds() << " then: " << then << " remaining: " << then.remaining() << endl;
		assert(last_remaining >= then.remaining());
		assert(tspecnow.tv_sec == invariant_time.tv_sec && tspecnow.tv_nsec == invariant_time.tv_nsec);
		usleep(500000);
		loops++;
	}
	cerr << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
	assert(then.remaining() <= 0);
	assert(loops >= 18);

	printf("Done\n");
}
<|endoftext|>


<commit_before>#include <ting/debug.hpp>
#include <algorithm>
#include <ting/Buffer.hpp>

#include "../src/cliser/ServerThread.hpp"
#include "../src/cliser/ClientThread.hpp"

namespace{
const unsigned DMaxConnections = 63;

const ting::u32 DMaxCnt = 16384;
const ting::u16 DPort = 13666;
const char* DIpAddress = "127.0.0.1";
}

class Connection : public cliser::Connection{
public:

	ting::StaticBuffer<ting::u8, sizeof(ting::u32)> rbuf;
	ting::Inited<unsigned, 0> rbufBytes;

	ting::Inited<ting::u32, 0> cnt;

	ting::Inited<ting::u32, 0> rcnt;

	ting::Inited<bool, false> isConnected;

	Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	~Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	void SendPortion(){
		ASSERT_INFO(this->cnt <= DMaxCnt, "this->cnt = " << this->cnt)

		if(this->cnt == DMaxCnt){
			if(this->rcnt == DMaxCnt){
				this->Disconnect_ts();
			}
			return;
		}

		ting::Array<ting::u8> buf(sizeof(ting::u32) * ( (std::min)(ting::u32((0xffff + 1) / sizeof(ting::u32)), DMaxCnt - this->cnt)) );

		ASSERT(buf.Size() > 0)

		ASSERT_INFO((buf.Size() % sizeof(ting::u32)) == 0, "buf.Size() = " << buf.Size() << " (buf.Size() % sizeof(ting::u32)) = " << (buf.Size() % sizeof(ting::u32)))

		for(ting::u8* p = buf.Begin(); p != buf.End(); p += sizeof(ting::u32)){
			ting::Serialize32(this->cnt, p);
			++this->cnt;
		}

		this->Send_ts(buf);
	}

	void HandleReceivedData(const ting::Buffer<ting::u8>& d){
		for(const ting::u8* p = d.Begin(); p != d.End(); ++p){
			this->rbuf[this->rbufBytes] = *p;
			++this->rbufBytes;

			if(this->rbufBytes == this->rbuf.Size()){
				this->rbufBytes = 0;
				ting::u32 num = ting::Deserialize32(this->rbuf.Begin());
				ASSERT_INFO_ALWAYS(this->rcnt == num, "num = " << num << " rcnt = " << this->rcnt)
				++this->rcnt;
			}
		}

		ASSERT_INFO(this->rcnt <= DMaxCnt, "this->rcnt = " << this->rcnt)

		if(this->rcnt == DMaxCnt){
			if(this->cnt == DMaxCnt){
				this->Disconnect_ts();
			}
		}
	}

	static ting::Ref<Connection> New(){
		return ting::Ref<Connection>(new Connection());
	}
};

class Server : private cliser::Listener, public cliser::ServerThread{
public:
	Server() :
			cliser::Listener(),
			cliser::ServerThread(DPort, 2, this, true, 100)
	{}

	~Server(){
		ASSERT_INFO_ALWAYS(this->numConnections == 0, "this->numConnections = " << this->numConnections)
	}
private:
	ting::Mutex numConsMut;
	ting::Inited<unsigned, 0> numConnections;

	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Server::OnDisconnected_ts(): CONNECTED!!!" << std::endl)

		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_ALWAYS(!conn->isConnected)
		conn->isConnected = true;

		{
			ting::Mutex::Guard mutexGuard(this->numConsMut);
			++(this->numConnections);
			ASSERT_INFO_ALWAYS(this->numConnections <= 2 * DMaxConnections, "this->numConnections = " << this->numConnections)
		}

		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Server::OnDisconnected_ts(): DISCONNECTED!!!" << std::endl)

		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_INFO_ALWAYS(conn->isConnected, "Server: disconnected non-connected connection")
		conn->isConnected = false;

		{
			ting::Mutex::Guard mutexGuard(this->numConsMut);
			--(this->numConnections);
			ASSERT_INFO_ALWAYS(this->numConnections <= 2 * DMaxConnections, "this->numConnections = " << this->numConnections)
		}
	}

	class HandleDataMessage : public ting::Message{
		ting::Ref<Connection> conn;
	public:
		HandleDataMessage(const ting::Ref<Connection>& conn) :
				conn(conn)
		{}

		//override
		void Handle(){
			if(ting::Array<ting::u8> d = this->conn->GetReceivedData_ts()){
				this->conn->HandleReceivedData(d);
			}
		}
	};

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		TRACE_ALWAYS(<< "Server: data received" << std::endl)
		this->PushMessage(
				ting::Ptr<ting::Message>(
						new HandleDataMessage(c.StaticCast<Connection>())
					)
			);

		return false;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

class Client : private cliser::Listener, public cliser::ClientThread{
public:
	Client() :
			cliser::Listener(),
			cliser::ClientThread(DMaxConnections, this) //max connections
	{}

	~Client(){
		ASSERT_INFO_ALWAYS(this->numConnections == 0, "this->numConnections = " << this->numConnections)
	}

private:
	ting::Mutex numConsMut;
	ting::Inited<unsigned, 0> numConnections;

	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Client::OnConnected_ts(): CONNECTED!!!" << std::endl)

		{
			ting::Mutex::Guard mutexGuard(this->numConsMut);
			++(this->numConnections);
			ASSERT_INFO_ALWAYS(this->numConnections <= DMaxConnections, "this->numConnections = " << this->numConnections)
		}

		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_ALWAYS(!conn->isConnected)
		conn->isConnected = true;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		conn->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		ting::Ref<Connection> conn = c.StaticCast<Connection>();

		if(conn->isConnected){
			TRACE_ALWAYS(<< "Client::OnDisconnected_ts(): DISCONNECTED!!!" << std::endl)

			conn->isConnected = false;

			{
				ting::Mutex::Guard mutexGuard(this->numConsMut);
				--(this->numConnections);
				ASSERT_INFO_ALWAYS(this->numConnections <= DMaxConnections, "this->numConnections = " << this->numConnections)
			}
		}else{
			//if we get here then it is a connect request failure
			ASSERT_INFO_ALWAYS(false, "Connection failed")
		}

		this->Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		ting::Ref<Connection> con = c.StaticCast<Connection>();

		con->HandleReceivedData(d);
		TRACE_ALWAYS(<< "Client: data received" << std::endl)
		return true;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

int main(int argc, char *argv[]){
	TRACE_ALWAYS(<< "Cliser test" << std::endl)

	unsigned msec = 20000;

	if(argc >= 2){
		if(std::string("0") == argv[1]){
			msec = 0;
		}
	}

	ting::SocketLib socketsLib;

	Server server;
	server.Start();

	ting::Thread::Sleep(100);//give server thread some time to start waiting on the socket

	Client client;
	client.Start();

	for(unsigned i = 0; i < client.MaxConnections(); ++i){
		client.Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	if(msec == 0){
		while(true){
			ting::Thread::Sleep(1000000);
		}
	}else{
		ting::Thread::Sleep(20000);
	}

	client.PushQuitMessage();
	client.Join();

	server.PushQuitMessage();
	server.Join();

	return 0;
}
<commit_msg>logging corrected<commit_after>#include <ting/debug.hpp>
#include <algorithm>
#include <ting/Buffer.hpp>

#include "../src/cliser/ServerThread.hpp"
#include "../src/cliser/ClientThread.hpp"

namespace{
const unsigned DMaxConnections = 63;

const ting::u32 DMaxCnt = 16384;
const ting::u16 DPort = 13666;
const char* DIpAddress = "127.0.0.1";
}

class Connection : public cliser::Connection{
public:

	ting::StaticBuffer<ting::u8, sizeof(ting::u32)> rbuf;
	ting::Inited<unsigned, 0> rbufBytes;

	ting::Inited<ting::u32, 0> cnt;

	ting::Inited<ting::u32, 0> rcnt;

	ting::Inited<bool, false> isConnected;

	Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	~Connection(){
//		TRACE(<< "Connection::" << __func__ << "(): invoked" << std::endl)
	}

	void SendPortion(){
		ASSERT_INFO(this->cnt <= DMaxCnt, "this->cnt = " << this->cnt)

		if(this->cnt == DMaxCnt){
			if(this->rcnt == DMaxCnt){
				this->Disconnect_ts();
			}
			return;
		}

		ting::Array<ting::u8> buf(sizeof(ting::u32) * ( (std::min)(ting::u32((0xffff + 1) / sizeof(ting::u32)), DMaxCnt - this->cnt)) );

		ASSERT(buf.Size() > 0)

		ASSERT_INFO((buf.Size() % sizeof(ting::u32)) == 0, "buf.Size() = " << buf.Size() << " (buf.Size() % sizeof(ting::u32)) = " << (buf.Size() % sizeof(ting::u32)))

		for(ting::u8* p = buf.Begin(); p != buf.End(); p += sizeof(ting::u32)){
			ting::Serialize32(this->cnt, p);
			++this->cnt;
		}

		this->Send_ts(buf);
	}

	void HandleReceivedData(const ting::Buffer<ting::u8>& d){
		for(const ting::u8* p = d.Begin(); p != d.End(); ++p){
			this->rbuf[this->rbufBytes] = *p;
			++this->rbufBytes;

			if(this->rbufBytes == this->rbuf.Size()){
				this->rbufBytes = 0;
				ting::u32 num = ting::Deserialize32(this->rbuf.Begin());
				ASSERT_INFO_ALWAYS(this->rcnt == num, "num = " << num << " rcnt = " << this->rcnt)
				++this->rcnt;
			}
		}

		ASSERT_INFO(this->rcnt <= DMaxCnt, "this->rcnt = " << this->rcnt)

		if(this->rcnt == DMaxCnt){
			if(this->cnt == DMaxCnt){
				this->Disconnect_ts();
			}
		}
	}

	static ting::Ref<Connection> New(){
		return ting::Ref<Connection>(new Connection());
	}
};

class Server : private cliser::Listener, public cliser::ServerThread{
public:
	Server() :
			cliser::Listener(),
			cliser::ServerThread(DPort, 2, this, true, 100)
	{}

	~Server(){
		ASSERT_INFO_ALWAYS(this->numConnections == 0, "this->numConnections = " << this->numConnections)
	}
private:
	ting::Mutex numConsMut;
	ting::Inited<unsigned, 0> numConnections;

	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Server::" << __func__ << "(): CONNECTED!!!" << std::endl)

		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_ALWAYS(!conn->isConnected)
		conn->isConnected = true;

		{
			ting::Mutex::Guard mutexGuard(this->numConsMut);
			++(this->numConnections);
			ASSERT_INFO_ALWAYS(this->numConnections <= 2 * DMaxConnections, "this->numConnections = " << this->numConnections)
		}

		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Server::" << __func__ << "(): DISCONNECTED!!!" << std::endl)

		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_INFO_ALWAYS(conn->isConnected, "Server: disconnected non-connected connection")
		conn->isConnected = false;

		{
			ting::Mutex::Guard mutexGuard(this->numConsMut);
			--(this->numConnections);
			ASSERT_INFO_ALWAYS(this->numConnections <= 2 * DMaxConnections, "this->numConnections = " << this->numConnections)
		}
	}

	class HandleDataMessage : public ting::Message{
		ting::Ref<Connection> conn;
	public:
		HandleDataMessage(const ting::Ref<Connection>& conn) :
				conn(conn)
		{}

		//override
		void Handle(){
			if(ting::Array<ting::u8> d = this->conn->GetReceivedData_ts()){
				this->conn->HandleReceivedData(d);
			}
		}
	};

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		TRACE_ALWAYS(<< "Server: data received" << std::endl)
		this->PushMessage(
				ting::Ptr<ting::Message>(
						new HandleDataMessage(c.StaticCast<Connection>())
					)
			);

		return false;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Server: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

class Client : private cliser::Listener, public cliser::ClientThread{
public:
	Client() :
			cliser::Listener(),
			cliser::ClientThread(DMaxConnections, this) //max connections
	{}

	~Client(){
		ASSERT_INFO_ALWAYS(this->numConnections == 0, "this->numConnections = " << this->numConnections)
	}

private:
	ting::Mutex numConsMut;
	ting::Inited<unsigned, 0> numConnections;

	//override
	ting::Ref<cliser::Connection> CreateConnectionObject(){
		return Connection::New();
	}

	//override
	void OnConnected_ts(const ting::Ref<cliser::Connection>& c){
		TRACE_ALWAYS(<< "Client::" << __func__ << "(): CONNECTED!!!" << std::endl)

		{
			ting::Mutex::Guard mutexGuard(this->numConsMut);
			++(this->numConnections);
			ASSERT_INFO_ALWAYS(this->numConnections <= DMaxConnections, "this->numConnections = " << this->numConnections)
		}

		ting::Ref<Connection> conn = c.StaticCast<Connection>();
		ASSERT_ALWAYS(!conn->isConnected)
		conn->isConnected = true;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		conn->SendPortion();
	}

	//override
	void OnDisconnected_ts(const ting::Ref<cliser::Connection>& c){
		ting::Ref<Connection> conn = c.StaticCast<Connection>();

		if(conn->isConnected){
			TRACE_ALWAYS(<< "Client::" << __func__ << "(): DISCONNECTED!!!" << std::endl)

			conn->isConnected = false;

			{
				ting::Mutex::Guard mutexGuard(this->numConsMut);
				--(this->numConnections);
				ASSERT_INFO_ALWAYS(this->numConnections <= DMaxConnections, "this->numConnections = " << this->numConnections)
			}
		}else{
			//if we get here then it is a connect request failure
			ASSERT_INFO_ALWAYS(false, "Connection failed")
		}

		this->Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	//override
	bool OnDataReceived_ts(const ting::Ref<cliser::Connection>& c, const ting::Buffer<ting::u8>& d){
		ting::Ref<Connection> con = c.StaticCast<Connection>();

		con->HandleReceivedData(d);
		TRACE_ALWAYS(<< "Client: data received" << std::endl)
		return true;
	}

	//override
	void OnDataSent_ts(const ting::Ref<cliser::Connection>& c, unsigned numPacketsInQueue, bool addedToQueue){
		if(numPacketsInQueue >= 2)
			return;

		TRACE_ALWAYS(<< "Client: sending data" << std::endl)
		c.StaticCast<Connection>()->SendPortion();
	}
};

int main(int argc, char *argv[]){
	TRACE_ALWAYS(<< "Cliser test" << std::endl)

	unsigned msec = 20000;

	if(argc >= 2){
		if(std::string("0") == argv[1]){
			msec = 0;
		}
	}

	ting::SocketLib socketsLib;

	Server server;
	server.Start();

	ting::Thread::Sleep(100);//give server thread some time to start waiting on the socket

	Client client;
	client.Start();

	for(unsigned i = 0; i < client.MaxConnections(); ++i){
		client.Connect_ts(ting::IPAddress(DIpAddress, DPort));
	}

	if(msec == 0){
		while(true){
			ting::Thread::Sleep(1000000);
		}
	}else{
		ting::Thread::Sleep(20000);
	}

	client.PushQuitMessage();
	client.Join();

	server.PushQuitMessage();
	server.Join();

	return 0;
}
<|endoftext|>


<commit_before>/**
 * @mainpage	COSSB(Component-based Open & Simple Service Broker)
 * @details	COSSB is a software framework to support IoT service interoperability among the networked devices or gadgets scattered in WLAN environment.
 */

/**
 * @file		engine.hpp
 * @brief		COSSB Engine
 * @author		Byunghun Hwang<bhhwang@nsynapse.com>
 * @date 		2015. 6. 9
 * @details	COSSB Engine main routine
 */

#include <iostream>
#include <csignal>
#include <cstdlib>
#include <unistd.h>
#include <popt.h>
#include <memory>

#include "cossb.hpp"

using namespace std;
using namespace cossb;

/**
 * @brief	destroy main instance
 * @details	It will be called by SIGINT signal
 */
void destroy()
{
	manager::component_manager::get()->destroy();
	broker::component_broker::get()->destroy();
}

/**
 * @brief	SIGINT signal callback function
 * @details	Stop all services and destroy all instances
 */
void sigc_interrupt(int param) { destroy();	exit(0); }

/**
 * @brief	Main routine
 * @param	command
 * @details	Start with default components
 */
int main(int argc, char* argv[])
{
	signal(SIGINT, sigc_interrupt);

	char* config_file = nullptr;
	struct poptOption optionTable[] = {
			{"run", 'r', POPT_ARG_NONE, 0, 'r', "Run Engine with default configuration", ""},
			{"version", 'v', POPT_ARG_NONE, 0, 'v', "Version", "version"},
			{"config", 'c', POPT_ARG_STRING, (void*)config_file, 'c', "Open config file", "XML Config file"},
			{"debug", 'd', POPT_ARG_NONE, 0, 'd', "DEBUG mode", ""},
			POPT_AUTOHELP
			POPT_TABLEEND
	};
	poptContext optionCon = poptGetContext(NULL, argc, (const char**)argv, optionTable, 0);
	poptSetOtherOptionHelp(optionCon, "<option>");

	if(argc<2)
	{
		destroy();
		exit(0);
	}

	int opt = 0;
	while((opt = poptGetNextOpt(optionCon))>=0)
	{
		switch(opt)
		{
		// install components
		case 'v':
		{
			std::cout << COSSB_VERSION << " (Released " << __DATE__ << " " <<__TIME__ << ")" << std::endl;
			destroy();
			exit(0);

		} break;

		//load config file
		case 'c':
		{
			string config_file = (const char*)poptGetOptArg(optionCon);
			manager::component_manager::get()->load_config(new config(config_file.c_str()));
		}
			break;

		//run in debug mode
		case 'd':
		{
		}
			break;

		case 'r':
		{
			manager::component_manager::get()->load_config(new config("manifest.xml"));
			//manager::component_manager::get()
		}

		}
	}

	if (opt<-1)
	{
		cout << poptBadOption(optionCon, POPT_BADOPTION_NOALIAS) << ":" << poptStrerror(opt) << endl;
		destroy();
		exit(0);
	}

	pause();

	//notice, this function affects the checking memory leak, especially when you are using valgrind.
	poptFreeContext(optionCon);

	destroy();
	exit(0);

	return 0;
}
<commit_msg>clear un-necessary codes<commit_after>/**
 * @mainpage	COSSB(Component-based Open & Simple Service Broker)
 * @details	COSSB is a software framework to support IoT service interoperability among the networked devices or gadgets scattered in WLAN environment.
 */

/**
 * @file		engine.hpp
 * @brief		COSSB Engine
 * @author		Byunghun Hwang<bhhwang@nsynapse.com>
 * @date 		2015. 6. 9
 * @details	COSSB Engine main routine
 */

#include <iostream>
#include <csignal>
#include <cstdlib>
#include <unistd.h>
#include <popt.h>
#include <memory>

#include "cossb.hpp"

using namespace std;
using namespace cossb;

/**
 * @brief	destroy main instance
 * @details	It will be called by SIGINT signal
 */
void destroy()
{
	manager::component_manager::get()->destroy();
	broker::component_broker::get()->destroy();
}

/**
 * @brief	SIGINT signal callback function
 * @details	Stop all services and destroy all instances
 */
void sigc_interrupt(int param) { destroy();	exit(0); }

/**
 * @brief	Main routine
 * @param	command
 * @details	Start with default components
 */
int main(int argc, char* argv[])
{
	signal(SIGINT, sigc_interrupt);

	char* config_file = nullptr;
	struct poptOption optionTable[] = {
			{"run", 'r', POPT_ARG_NONE, 0, 'r', "Run Engine with default configuration", ""},
			{"version", 'v', POPT_ARG_NONE, 0, 'v', "Version", "version"},
			{"config", 'c', POPT_ARG_STRING, (void*)config_file, 'c', "Open configuration file", "XML Configuration file"},
			{"debug", 'd', POPT_ARG_NONE, 0, 'd', "DEBUG mode", ""},
			{"reset", 'e', POPT_ARG_NONE, 0, 'e', "Reset All system", "Clear and initialize the configurations"},
			POPT_AUTOHELP
			POPT_TABLEEND
	};
	poptContext optionCon = poptGetContext(NULL, argc, (const char**)argv, optionTable, 0);
	poptSetOtherOptionHelp(optionCon, "<option>");

	if(argc<2)
	{
		std::cout << poptStrerror(POPT_ERROR_NOARG) << endl;
		destroy();
		exit(0);
	}

	int opt = 0;
	while((opt = poptGetNextOpt(optionCon))>=0)
	{
		switch(opt)
		{
		// install components
		case 'v':
		{
			std::cout << COSSB_VERSION << " (Released " << __DATE__ << " " <<__TIME__ << ")" << std::endl;
			destroy();
			exit(0);

		} break;

		//load configuration file
		case 'c':
		{
			string config_file = (const char*)poptGetOptArg(optionCon);
			manager::component_manager::get()->load_config(new config(config_file.c_str()));
		}
			break;

		//run in debug mode
		case 'd':
		{
		}
			break;

		case 'r':
		{
			manager::component_manager::get()->load_config(new config("manifest.xml"));
		}
		break;

		}
	}

	if (opt<-1)
	{
		cout << poptBadOption(optionCon, POPT_BADOPTION_NOALIAS) << ":" << poptStrerror(opt) << endl;
		destroy();
		exit(0);
	}

	pause();

	//notice, this function affects the checking memory leak, especially when you are using valgrind.
	poptFreeContext(optionCon);

	destroy();
	exit(0);

	return 0;
}
<|endoftext|>


<commit_before>/*
 *
 * StellarLikeBlockchainExplorerAccountSynchronizer.cpp
 * ledger-core
 *
 * Created by Pierre Pollastri on 10/07/2019.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ledger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "StellarLikeBlockchainExplorerAccountSynchronizer.hpp"

#include <common/AccountHelper.hpp>
#include <wallet/stellar/StellarLikeAccount.hpp>

/**
 * Current version of the synchronization. Please keep this field up to date to ensure that we have proper migration
 * mechanism if the synchronizer needs to save more data in its state in the future (or modify current value). This
 * version allow smooth migration.
 */
static const auto SYNCHRONIZATION_ALGORITHM_VERSION = 1;

namespace ledger {
    namespace core {

        StellarLikeBlockchainExplorerAccountSynchronizer::StellarLikeBlockchainExplorerAccountSynchronizer(
                const std::shared_ptr<WalletPool> &pool,
                const std::shared_ptr<StellarLikeBlockchainExplorer> &explorer) :
                DedicatedContext(pool->getDispatcher()->getSerialExecutionContext("stellar_like_account_synchronizer")),
                _explorer(explorer),
                _database(pool->getDatabaseSessionPool()) {

        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::reset(const std::shared_ptr<StellarLikeAccount> &account,
                                                                     const std::chrono::system_clock::time_point &toDate) {
            account->getInternalPreferences()->getSubPreferences("StellarLikeBlockchainExplorerAccountSynchronizer")->editor()->clear();
        }

        std::shared_ptr<ProgressNotifier<BlockchainExplorerAccountSynchronizationResult>> StellarLikeBlockchainExplorerAccountSynchronizer::synchronize(
                const std::shared_ptr<StellarLikeAccount> &account) {
            if (_notifier != nullptr) {
                return _notifier;
            }
            _notifier = std::make_shared<ProgressNotifier<BlockchainExplorerAccountSynchronizationResult>>();
            synchronizeAccount(account);
            return _notifier;
        }

        bool StellarLikeBlockchainExplorerAccountSynchronizer::isSynchronizing() const {
            return _notifier != nullptr;
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::synchronizeAccount(const std::shared_ptr<StellarLikeAccount> &account) {
            auto self = shared_from_this();
            auto preferences = account->getInternalPreferences()->getSubPreferences("StellarLikeBlockchainExplorerAccountSynchronizer");
            auto state = preferences
                ->template getObject<StellarLikeBlockchainExplorerAccountSynchronizer::SavedState>("state")
                // provide default state if none exists yet
                .getValueOr(SavedState{});

            synchronizeAccount(account, state);
        }

        void
        StellarLikeBlockchainExplorerAccountSynchronizer::synchronizeAccount(const std::shared_ptr<StellarLikeAccount>& account,
                                                                             StellarLikeBlockchainExplorerAccountSynchronizer::SavedState &state) {
            auto address = account->getKeychain()->getAddress()->toString();
            auto self = shared_from_this();

            _explorer->getAccount(address)
                .onComplete(account->getContext(), [self, account, state = std::move(state)] (const Try<std::shared_ptr<stellar::Account>>& accountInfo) mutable {
                   if (accountInfo.isFailure() && accountInfo.getFailure().getErrorCode() == api::ErrorCode::ACCOUNT_NOT_FOUND) {
                        self->endSynchronization(state);
                    } else if (accountInfo.isFailure()) {
                        self->failSynchronization(accountInfo.getFailure());
                    } else {
                        soci::session sql(self->_database->getPool());
                        account->updateAccountInfo(sql, *accountInfo.getValue());
                        self->synchronizeTransactions(account, state);
                    }
                });
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::synchronizeTransactions(
                const std::shared_ptr<StellarLikeAccount> &account,
                StellarLikeBlockchainExplorerAccountSynchronizer::SavedState &state) {
            auto address = account->getKeychain()->getAddress()->toString();
            auto self = shared_from_this();

            _explorer->getTransactions(address, state.transactionPagingToken)
                .onComplete(account->getContext(), [self, account, state = std::move(state)] (const Try<stellar::TransactionVector>& txs) mutable {
                if (txs.isFailure()) {
                    self->failSynchronization(txs.getFailure());
                } else {
                    {
                        soci::session sql(self->_database->getPool());
                        soci::transaction tr(sql);

                        for (const auto &tx : txs.getValue()) {
                            auto const flag = account->putTransaction(sql, *tx);

                            if (::ledger::core::account::isInsertedOperation(flag)) {
                                ++state.insertedOperations;
                            }

                            state.lastBlockHeight = std::max(state.lastBlockHeight, tx->ledger);
                        }
                        tr.commit();
                    }
                    if (!txs.getValue().empty()) {
                        state.transactionPagingToken = txs.getValue().back()->pagingToken;

                        {
                            auto preferences = account->getInternalPreferences()->getSubPreferences("StellarLikeBlockchainExplorerAccountSynchronizer");
                            preferences->editor()->putObject("state", state)->commit();
                        }

                        self->synchronizeTransactions(account, state);
                    } else {
                        self->endSynchronization(state);
                    }
                }
            });
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::endSynchronization(const StellarLikeBlockchainExplorerAccountSynchronizer::SavedState &state) {
            BlockchainExplorerAccountSynchronizationResult result;

            result.lastBlockHeight = state.lastBlockHeight;
            result.newOperations = state.insertedOperations;

            _notifier->success(std::move(result));
            _notifier = nullptr;
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::failSynchronization(const Exception &ex) {
            _notifier->failure(ex);
            _notifier = nullptr;
        }
    }
}
<commit_msg>Log stellar transactions<commit_after>/*
 *
 * StellarLikeBlockchainExplorerAccountSynchronizer.cpp
 * ledger-core
 *
 * Created by Pierre Pollastri on 10/07/2019.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Ledger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "StellarLikeBlockchainExplorerAccountSynchronizer.hpp"

#include <common/AccountHelper.hpp>
#include <wallet/stellar/StellarLikeAccount.hpp>

/**
 * Current version of the synchronization. Please keep this field up to date to ensure that we have proper migration
 * mechanism if the synchronizer needs to save more data in its state in the future (or modify current value). This
 * version allow smooth migration.
 */
static const auto SYNCHRONIZATION_ALGORITHM_VERSION = 1;

namespace ledger {
    namespace core {

        StellarLikeBlockchainExplorerAccountSynchronizer::StellarLikeBlockchainExplorerAccountSynchronizer(
                const std::shared_ptr<WalletPool> &pool,
                const std::shared_ptr<StellarLikeBlockchainExplorer> &explorer) :
                DedicatedContext(pool->getDispatcher()->getSerialExecutionContext("stellar_like_account_synchronizer")),
                _explorer(explorer),
                _database(pool->getDatabaseSessionPool()) {

        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::reset(const std::shared_ptr<StellarLikeAccount> &account,
                                                                     const std::chrono::system_clock::time_point &toDate) {
            account->getInternalPreferences()->getSubPreferences("StellarLikeBlockchainExplorerAccountSynchronizer")->editor()->clear();
        }

        std::shared_ptr<ProgressNotifier<BlockchainExplorerAccountSynchronizationResult>> StellarLikeBlockchainExplorerAccountSynchronizer::synchronize(
                const std::shared_ptr<StellarLikeAccount> &account) {
            if (_notifier != nullptr) {
                return _notifier;
            }
            _notifier = std::make_shared<ProgressNotifier<BlockchainExplorerAccountSynchronizationResult>>();
            synchronizeAccount(account);
            return _notifier;
        }

        bool StellarLikeBlockchainExplorerAccountSynchronizer::isSynchronizing() const {
            return _notifier != nullptr;
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::synchronizeAccount(const std::shared_ptr<StellarLikeAccount> &account) {
            auto self = shared_from_this();
            auto preferences = account->getInternalPreferences()->getSubPreferences("StellarLikeBlockchainExplorerAccountSynchronizer");
            auto state = preferences
                ->template getObject<StellarLikeBlockchainExplorerAccountSynchronizer::SavedState>("state")
                // provide default state if none exists yet
                .getValueOr(SavedState{});

            synchronizeAccount(account, state);
        }

        void
        StellarLikeBlockchainExplorerAccountSynchronizer::synchronizeAccount(const std::shared_ptr<StellarLikeAccount>& account,
                                                                             StellarLikeBlockchainExplorerAccountSynchronizer::SavedState &state) {
            auto address = account->getKeychain()->getAddress()->toString();
            auto self = shared_from_this();

            _explorer->getAccount(address)
                .onComplete(account->getContext(), [self, account, state = std::move(state)] (const Try<std::shared_ptr<stellar::Account>>& accountInfo) mutable {
                   if (accountInfo.isFailure() && accountInfo.getFailure().getErrorCode() == api::ErrorCode::ACCOUNT_NOT_FOUND) {
                        self->endSynchronization(state);
                    } else if (accountInfo.isFailure()) {
                        self->failSynchronization(accountInfo.getFailure());
                    } else {
                        soci::session sql(self->_database->getPool());
                        account->updateAccountInfo(sql, *accountInfo.getValue());
                        self->synchronizeTransactions(account, state);
                    }
                });
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::synchronizeTransactions(
                const std::shared_ptr<StellarLikeAccount> &account,
                StellarLikeBlockchainExplorerAccountSynchronizer::SavedState &state) {
            auto address = account->getKeychain()->getAddress()->toString();
            auto self = shared_from_this();

            _explorer->getTransactions(address, state.transactionPagingToken)
                .onComplete(account->getContext(), [self, account, state = std::move(state)] (const Try<stellar::TransactionVector>& txs) mutable {
                if (txs.isFailure()) {
                    self->failSynchronization(txs.getFailure());
                } else {
                    {
                        soci::session sql(self->_database->getPool());
                        soci::transaction tr(sql);

                        for (const auto &tx : txs.getValue()) {
                            account->logger()->debug("XLM transaction hash: {}, paging_token: {}", tx->hash, tx->pagingToken);
                            auto const flag = account->putTransaction(sql, *tx);

                            if (::ledger::core::account::isInsertedOperation(flag)) {
                                ++state.insertedOperations;
                            }

                            state.lastBlockHeight = std::max(state.lastBlockHeight, tx->ledger);
                        }
                        tr.commit();
                    }
                    if (!txs.getValue().empty()) {
                        state.transactionPagingToken = txs.getValue().back()->pagingToken;

                        {
                            auto preferences = account->getInternalPreferences()->getSubPreferences("StellarLikeBlockchainExplorerAccountSynchronizer");
                            preferences->editor()->putObject("state", state)->commit();
                        }

                        self->synchronizeTransactions(account, state);
                    } else {
                        self->endSynchronization(state);
                    }
                }
            });
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::endSynchronization(const StellarLikeBlockchainExplorerAccountSynchronizer::SavedState &state) {
            BlockchainExplorerAccountSynchronizationResult result;

            result.lastBlockHeight = state.lastBlockHeight;
            result.newOperations = state.insertedOperations;

            _notifier->success(std::move(result));
            _notifier = nullptr;
        }

        void StellarLikeBlockchainExplorerAccountSynchronizer::failSynchronization(const Exception &ex) {
            _notifier->failure(ex);
            _notifier = nullptr;
        }
    }
}
<|endoftext|>


<commit_before>/*
 * simple_testing.cpp
 *
 *  Created on: Dec 21, 2012
 *      Author: michael.hunter
 */
#include "simple_testing.hpp"
#include "debug_new_and_delete.hpp"

#include <exception>
#include <iostream>
#include <time.h>
#include <stdio.h>

std::vector<SimpleTest*>* SimpleTest::allTests = 0;
std::string* SimpleTest::lastFileName_ = 0;

SimpleTest::SimpleTest(const char* testName, const char* fileName, int line)
	: isFailed_(false), testName_(testName), fileName_(fileName),
	testStartLine_(line), passCount_(0), failCount_(0)
{
	if (!allTests)
		allTests = new std::vector<SimpleTest*>();
	allTests->push_back(this);
}

void SimpleTest::checkMemory(int line)
{
	switch (checkConsistency())    {
	case 1: fail("Heap corrupt", line); break;
	case 2: fail("Free header corrupt", line); break;
	case 3: fail("Free memory modified", line); break;
	}

	if (isMultipleFree)
		fail("The same pointer has been freed twice", line);
}

void SimpleTest::executeTest()
{
	try
	{
		if (!lastFileName_ || (*lastFileName_ != fileName_))
		{
			if (!lastFileName_)
				lastFileName_ = new std::string(fileName_);
			else
				*lastFileName_ = fileName_;
			std::cout << "-------------- " << fileName_ << "----------------" << std::endl;
		}
		size_t bytesBefore = bytesAllocatedCount;
		passCount_ = 0;
		failCount_ = 0;
		run();
		checkMemory(testStartLine_);
		if (bytesBefore != bytesAllocatedCount)
			fail("Memory leak during test", testStartLine_);
		std::cout << testName_ << ": " << passCount_  << "/" << (passCount_ + failCount_) << std::endl;
	}
	catch (std::exception& e)
	{
		fail(e.what(), testStartLine_);
	}
	catch (...)
	{
		fail("Test threw an exception", testStartLine_);
	}
}

int SimpleTest::runAllTests()
{
	if (!allTests)
	{
		std::cout << "No Tests" << std:: endl;
		return 0;
	}

	std::cout << "Running " << allTests->size() << " tests" << std:: endl;
	for (auto test : *allTests)
	{

		test->executeTest();
		if (test->isFailed_)
			return 1;
	}
	std::cout << "All tests passed" << std::endl;
	return 0;
}

void SimpleTest::assert_(bool proposition, const char* msg, int line)
{
	if (proposition)
		pass(msg);
	else
		fail(msg, line);
}

void SimpleTest::pass(const char* passText)
{
	passCount_ ++;
	//std::cout << "Passed: " << passText << std::endl;
}

void SimpleTest::fail(const char* failText, int line)
{
	isFailed_ = true;
	failCount_ ++;
#if _MSC_VER
	std::cout << fileName_ << "(" << line << "): error: " << testName_ << " failed: " << failText << std::endl;
#else
	std::cout << fileName_ << ":" << line << ": error: " << testName_ << " failed: " << failText << std::endl;
#endif
}

int main(int argc, char *argv[])
{
	int result = SimpleTest::runAllTests();

	const char* successFile = nullptr;
	for (int i = 0; i < argc - 1; i ++)
		if (strcmp(argv[i], "-s") == 0)
			successFile = argv[i+1];

	if (result == 0 && successFile)
	{
		struct tm *current;
		time_t now;
		time(&now);
		current = localtime(&now);
		FILE* fp = fopen(successFile, "w");
		fprintf(fp, "Last successful build: %04u-%02i-%02i %02i:%02i:%02i\n",
			current->tm_year + 1900,
			current->tm_mon,
			current->tm_mday,
			current->tm_hour,
			current->tm_min,
			current->tm_sec
			);
		fclose(fp);
	}

	return result;
}
<commit_msg>Report number of tests when successful<commit_after>/*
 * simple_testing.cpp
 *
 *  Created on: Dec 21, 2012
 *      Author: michael.hunter
 */
#include "simple_testing.hpp"
#include "debug_new_and_delete.hpp"

#include <exception>
#include <iostream>
#include <time.h>
#include <stdio.h>

std::vector<SimpleTest*>* SimpleTest::allTests = 0;
std::string* SimpleTest::lastFileName_ = 0;

SimpleTest::SimpleTest(const char* testName, const char* fileName, int line)
	: isFailed_(false), testName_(testName), fileName_(fileName),
	testStartLine_(line), passCount_(0), failCount_(0)
{
	if (!allTests)
		allTests = new std::vector<SimpleTest*>();
	allTests->push_back(this);
}

void SimpleTest::checkMemory(int line)
{
	switch (checkConsistency())    {
	case 1: fail("Heap corrupt", line); break;
	case 2: fail("Free header corrupt", line); break;
	case 3: fail("Free memory modified", line); break;
	}

	if (isMultipleFree)
		fail("The same pointer has been freed twice", line);
}

void SimpleTest::executeTest()
{
	try
	{
		if (!lastFileName_ || (*lastFileName_ != fileName_))
		{
			if (!lastFileName_)
				lastFileName_ = new std::string(fileName_);
			else
				*lastFileName_ = fileName_;
			std::cout << "-------------- " << fileName_ << "----------------" << std::endl;
		}
		size_t bytesBefore = bytesAllocatedCount;
		passCount_ = 0;
		failCount_ = 0;
		run();
		checkMemory(testStartLine_);
		if (bytesBefore != bytesAllocatedCount)
			fail("Memory leak during test", testStartLine_);
		std::cout << testName_ << ": " << passCount_  << "/" << (passCount_ + failCount_) << std::endl;
	}
	catch (std::exception& e)
	{
		fail(e.what(), testStartLine_);
	}
	catch (...)
	{
		fail("Test threw an exception", testStartLine_);
	}
}

int SimpleTest::runAllTests()
{
	if (!allTests)
	{
		std::cout << "No Tests" << std:: endl;
		return 0;
	}

	std::cout << "Running " << allTests->size() << " tests" << std:: endl;
	for (auto test : *allTests)
	{

		test->executeTest();
		if (test->isFailed_)
			return 1;
	}
	std::cout << "All " << allTests->size() << " tests passed" << std::endl;
	return 0;
}

void SimpleTest::assert_(bool proposition, const char* msg, int line)
{
	if (proposition)
		pass(msg);
	else
		fail(msg, line);
}

void SimpleTest::pass(const char* passText)
{
	passCount_ ++;
	//std::cout << "Passed: " << passText << std::endl;
}

void SimpleTest::fail(const char* failText, int line)
{
	isFailed_ = true;
	failCount_ ++;
#if _MSC_VER
	std::cout << fileName_ << "(" << line << "): error: " << testName_ << " failed: " << failText << std::endl;
#else
	std::cout << fileName_ << ":" << line << ": error: " << testName_ << " failed: " << failText << std::endl;
#endif
}

int main(int argc, char *argv[])
{
	int result = SimpleTest::runAllTests();

	const char* successFile = nullptr;
	for (int i = 0; i < argc - 1; i ++)
		if (strcmp(argv[i], "-s") == 0)
			successFile = argv[i+1];

	if (result == 0 && successFile)
	{
		struct tm *current;
		time_t now;
		time(&now);
		current = localtime(&now);
		FILE* fp = fopen(successFile, "w");
		fprintf(fp, "Last successful build: %04u-%02i-%02i %02i:%02i:%02i\n",
			current->tm_year + 1900,
			current->tm_mon,
			current->tm_mday,
			current->tm_hour,
			current->tm_min,
			current->tm_sec
			);
		fclose(fp);
	}

	return result;
}
<|endoftext|>