<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// materialization_test.cpp
//
// Identification: tests/executor/materialization_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <chrono>
#include <iostream>
#include <ctime>

#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "backend/planner/abstract_plan.h"
#include "backend/planner/materialization_plan.h"
#include "backend/planner/seq_scan_plan.h"

#include "backend/catalog/manager.h"
#include "backend/catalog/schema.h"
#include "backend/common/types.h"
#include "backend/common/value.h"
#include "backend/common/value_factory.h"
#include "backend/executor/logical_tile.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/materialization_executor.h"
#include "backend/storage/backend_vm.h"
#include "backend/storage/tile.h"
#include "backend/storage/tile_group.h"
#include "backend/storage/data_table.h"
#include "backend/concurrency/transaction.h"
#include "backend/executor/abstract_executor.h"
#include "backend/executor/seq_scan_executor.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/expression_util.h"
#include "backend/storage/table_factory.h"
#include "backend/index/index_factory.h"

#include "executor/executor_tests_util.h"
#include "executor/mock_executor.h"

using ::testing::NotNull;

namespace peloton {
namespace test {

//===--------------------------------------------------------------------===//
// Tile Group Layout Tests
//===--------------------------------------------------------------------===//

void RunTest() {
  std::chrono::time_point<std::chrono::system_clock> start, end;

  const int tuples_per_tilegroup_count = 10;
  const int tile_group_count = 5;
  const int tuple_count = tuples_per_tilegroup_count * tile_group_count;
  const oid_t col_count = 250;
  const bool is_inlined = true;
  const bool indexes = false;

  std::vector<catalog::Column> columns;

  for(oid_t col_itr = 0 ; col_itr <= col_count; col_itr++) {
    auto column =
        catalog::Column(VALUE_TYPE_INTEGER, GetTypeSize(VALUE_TYPE_INTEGER),
                        "FIELD" + std::to_string(col_itr), is_inlined);

    columns.push_back(column);
  }

  catalog::Schema *table_schema = new catalog::Schema(columns);
  std::string table_name("TEST_TABLE");

  /////////////////////////////////////////////////////////
  // Create table.
  /////////////////////////////////////////////////////////

  bool own_schema = true;
  bool adapt_table = true;
  std::unique_ptr<storage::DataTable> table(storage::TableFactory::GetDataTable(
      INVALID_OID, INVALID_OID, table_schema, table_name,
      tuples_per_tilegroup_count,
      own_schema, adapt_table));

  // PRIMARY INDEX
  if (indexes == true) {
    std::vector<oid_t> key_attrs;

    auto tuple_schema = table->GetSchema();
    catalog::Schema *key_schema;
    index::IndexMetadata *index_metadata;
    bool unique;

    key_attrs = {0};
    key_schema = catalog::Schema::CopySchema(tuple_schema, key_attrs);
    key_schema->SetIndexedColumns(key_attrs);

    unique = true;

    index_metadata = new index::IndexMetadata(
        "primary_index", 123, INDEX_TYPE_BTREE,
        INDEX_CONSTRAINT_TYPE_PRIMARY_KEY, tuple_schema, key_schema, unique);

    index::Index *pkey_index = index::IndexFactory::GetInstance(index_metadata);
    table->AddIndex(pkey_index);
  }

  /////////////////////////////////////////////////////////
  // Load in the data
  /////////////////////////////////////////////////////////

  // Insert tuples into tile_group.
  auto &txn_manager = concurrency::TransactionManager::GetInstance();
  const bool allocate = true;
  auto txn = txn_manager.BeginTransaction();

  for (int rowid = 0; rowid < tuple_count; rowid++) {
    int populate_value = rowid;

    storage::Tuple tuple(table_schema, allocate);

    for(oid_t col_itr = 0 ; col_itr <= col_count; col_itr++) {
      auto value = ValueFactory::GetIntegerValue(populate_value + col_itr);
      tuple.SetValue(col_itr, value);
    }

    ItemPointer tuple_slot_id = table->InsertTuple(txn, &tuple);
    EXPECT_TRUE(tuple_slot_id.block != INVALID_OID);
    EXPECT_TRUE(tuple_slot_id.offset != INVALID_OID);
    txn->RecordInsert(tuple_slot_id);
  }

  txn_manager.CommitTransaction(txn);

  /////////////////////////////////////////////////////////
  // Do a seq scan with predicate on top of the table
  /////////////////////////////////////////////////////////

  start = std::chrono::system_clock::now();

  txn = txn_manager.BeginTransaction();
  std::unique_ptr<executor::ExecutorContext> context(
      new executor::ExecutorContext(txn));

  // Column ids to be added to logical tile after scan.
  //std::vector<oid_t> column_ids;
  //for(oid_t col_itr = 0 ; col_itr <= 200; col_itr++) {
  //  column_ids.push_back(col_itr);
  //}
  std::vector<oid_t> column_ids({198, 206});

  // Create and set up seq scan executor
  planner::SeqScanPlan seq_scan_node(table.get(), nullptr, column_ids);
  int expected_num_tiles = tile_group_count;

  executor::SeqScanExecutor seq_scan_executor(&seq_scan_node, context.get());

  // Create and set up materialization executor
  std::vector<catalog::Column> output_columns;
  std::unordered_map<oid_t, oid_t> old_to_new_cols;
  oid_t col_itr = 0;
  for(auto column_id : column_ids) {
    auto column =
        catalog::Column(VALUE_TYPE_INTEGER, GetTypeSize(VALUE_TYPE_INTEGER),
                        "FIELD" + std::to_string(column_id), is_inlined);
    output_columns.push_back(column);

    old_to_new_cols[col_itr] = col_itr;
    col_itr++;
  }

  std::unique_ptr<catalog::Schema> output_schema(
      new catalog::Schema(output_columns));
  bool physify_flag = true;  // is going to create a physical tile
  planner::MaterializationPlan mat_node(old_to_new_cols, output_schema.release(),
                                        physify_flag);

  executor::MaterializationExecutor mat_executor(&mat_node, nullptr);
  mat_executor.AddChild(&seq_scan_executor);

  EXPECT_TRUE(mat_executor.Init());

  std::vector<std::unique_ptr<executor::LogicalTile>> result_tiles;
  for (int i = 0; i < expected_num_tiles; i++) {
    EXPECT_TRUE(mat_executor.Execute());
    std::unique_ptr<executor::LogicalTile> result_tile(mat_executor.GetOutput());
    EXPECT_THAT(result_tile, NotNull());
    result_tiles.emplace_back(result_tile.release());
  }

  EXPECT_FALSE(mat_executor.Execute());

  txn_manager.CommitTransaction(txn);

  end = std::chrono::system_clock::now();
  std::chrono::duration<double> elapsed_seconds = end-start;

  std::cout << "duration :: " << elapsed_seconds.count() << "s\n";
}

TEST(TileGroupLayoutTest, RowLayout) {
  peloton_layout = LAYOUT_ROW;
  RunTest();
}

TEST(TileGroupLayoutTest, ColumnLayout) {
  peloton_layout = LAYOUT_COLUMN;
  RunTest();
}

TEST(TileGroupLayoutTest, HybridLayout) {
  peloton_layout = LAYOUT_HYBRID;
  RunTest();
}

}  // namespace test
}  // namespace peloton
<commit_msg>Fix tile group layout test<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// materialization_test.cpp
//
// Identification: tests/executor/materialization_test.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <chrono>
#include <iostream>
#include <ctime>

#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "backend/planner/abstract_plan.h"
#include "backend/planner/materialization_plan.h"
#include "backend/planner/seq_scan_plan.h"

#include "backend/catalog/manager.h"
#include "backend/catalog/schema.h"
#include "backend/common/types.h"
#include "backend/common/value.h"
#include "backend/common/value_factory.h"
#include "backend/executor/logical_tile.h"
#include "backend/executor/logical_tile_factory.h"
#include "backend/executor/materialization_executor.h"
#include "backend/storage/backend_vm.h"
#include "backend/storage/tile.h"
#include "backend/storage/tile_group.h"
#include "backend/storage/data_table.h"
#include "backend/concurrency/transaction.h"
#include "backend/executor/abstract_executor.h"
#include "backend/executor/seq_scan_executor.h"
#include "backend/expression/abstract_expression.h"
#include "backend/expression/expression_util.h"
#include "backend/storage/table_factory.h"
#include "backend/index/index_factory.h"

#include "executor/executor_tests_util.h"
#include "executor/mock_executor.h"

using ::testing::NotNull;

namespace peloton {
namespace test {

//===--------------------------------------------------------------------===//
// Tile Group Layout Tests
//===--------------------------------------------------------------------===//

void RunTest() {
  std::chrono::time_point<std::chrono::system_clock> start, end;

  const int tuples_per_tilegroup_count = 10;
  const int tile_group_count = 5;
  const int tuple_count = tuples_per_tilegroup_count * tile_group_count;
  const oid_t col_count = 250;
  const bool is_inlined = true;
  const bool indexes = false;

  std::vector<catalog::Column> columns;

  for(oid_t col_itr = 0 ; col_itr <= col_count; col_itr++) {
    auto column =
        catalog::Column(VALUE_TYPE_INTEGER, GetTypeSize(VALUE_TYPE_INTEGER),
                        "FIELD" + std::to_string(col_itr), is_inlined);

    columns.push_back(column);
  }

  catalog::Schema *table_schema = new catalog::Schema(columns);
  std::string table_name("TEST_TABLE");

  /////////////////////////////////////////////////////////
  // Create table.
  /////////////////////////////////////////////////////////

  bool own_schema = true;
  bool adapt_table = true;
  std::unique_ptr<storage::DataTable> table(storage::TableFactory::GetDataTable(
      INVALID_OID, INVALID_OID, table_schema, table_name,
      tuples_per_tilegroup_count,
      own_schema, adapt_table));

  // PRIMARY INDEX
  if (indexes == true) {
    std::vector<oid_t> key_attrs;

    auto tuple_schema = table->GetSchema();
    catalog::Schema *key_schema;
    index::IndexMetadata *index_metadata;
    bool unique;

    key_attrs = {0};
    key_schema = catalog::Schema::CopySchema(tuple_schema, key_attrs);
    key_schema->SetIndexedColumns(key_attrs);

    unique = true;

    index_metadata = new index::IndexMetadata(
        "primary_index", 123, INDEX_TYPE_BTREE,
        INDEX_CONSTRAINT_TYPE_PRIMARY_KEY, tuple_schema, key_schema, unique);

    index::Index *pkey_index = index::IndexFactory::GetInstance(index_metadata);
    table->AddIndex(pkey_index);
  }

  /////////////////////////////////////////////////////////
  // Load in the data
  /////////////////////////////////////////////////////////

  // Insert tuples into tile_group.
  auto &txn_manager = concurrency::TransactionManager::GetInstance();
  const bool allocate = true;
  auto txn = txn_manager.BeginTransaction();

  for (int rowid = 0; rowid < tuple_count; rowid++) {
    int populate_value = rowid;

    storage::Tuple tuple(table_schema, allocate);

    for(oid_t col_itr = 0 ; col_itr <= col_count; col_itr++) {
      auto value = ValueFactory::GetIntegerValue(populate_value + col_itr);
      tuple.SetValue(col_itr, value);
    }

    ItemPointer tuple_slot_id = table->InsertTuple(txn, &tuple);
    EXPECT_TRUE(tuple_slot_id.block != INVALID_OID);
    EXPECT_TRUE(tuple_slot_id.offset != INVALID_OID);
    txn->RecordInsert(tuple_slot_id);
  }

  txn_manager.CommitTransaction(txn);

  /////////////////////////////////////////////////////////
  // Do a seq scan with predicate on top of the table
  /////////////////////////////////////////////////////////

  start = std::chrono::system_clock::now();

  txn = txn_manager.BeginTransaction();
  std::unique_ptr<executor::ExecutorContext> context(
      new executor::ExecutorContext(txn));

  // Column ids to be added to logical tile after scan.
  //std::vector<oid_t> column_ids;
  //for(oid_t col_itr = 0 ; col_itr <= 200; col_itr++) {
  //  column_ids.push_back(col_itr);
  //}
  std::vector<oid_t> column_ids({198, 206});

  // Create and set up seq scan executor
  planner::SeqScanPlan seq_scan_node(table.get(), nullptr, column_ids);
  int expected_num_tiles = tile_group_count;

  executor::SeqScanExecutor seq_scan_executor(&seq_scan_node, context.get());

  // Create and set up materialization executor
  std::vector<catalog::Column> output_columns;
  std::unordered_map<oid_t, oid_t> old_to_new_cols;
  oid_t col_itr = 0;
  for(auto column_id : column_ids) {
    auto column =
        catalog::Column(VALUE_TYPE_INTEGER, GetTypeSize(VALUE_TYPE_INTEGER),
                        "FIELD" + std::to_string(column_id), is_inlined);
    output_columns.push_back(column);

    old_to_new_cols[col_itr] = col_itr;
    col_itr++;
  }

  std::unique_ptr<catalog::Schema> output_schema(
      new catalog::Schema(output_columns));
  bool physify_flag = true;  // is going to create a physical tile
  planner::MaterializationPlan mat_node(old_to_new_cols, output_schema.release(),
                                        physify_flag);

  executor::MaterializationExecutor mat_executor(&mat_node, nullptr);
  mat_executor.AddChild(&seq_scan_executor);

  EXPECT_TRUE(mat_executor.Init());

  std::vector<std::unique_ptr<executor::LogicalTile>> result_tiles;
  for (int i = 0; i < expected_num_tiles; i++) {
    EXPECT_TRUE(mat_executor.Execute());
    std::unique_ptr<executor::LogicalTile> result_tile(mat_executor.GetOutput());
    EXPECT_THAT(result_tile, NotNull());
    result_tiles.emplace_back(result_tile.release());
  }

  EXPECT_FALSE(mat_executor.Execute());

  txn_manager.CommitTransaction(txn);

  end = std::chrono::system_clock::now();
  std::chrono::duration<double> elapsed_seconds = end-start;

  std::cout << "duration :: " << elapsed_seconds.count() << "s\n";
}

TEST(TileGroupLayoutTest, RowLayout) {
  peloton_layout = LAYOUT_ROW;
  RunTest();
}

TEST(TileGroupLayoutTest, ColumnLayout) {
  peloton_layout = LAYOUT_COLUMN;
  RunTest();
}

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


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

#include "PreprocessorTest.h"
#include "Token.h"

class VersionTest : public PreprocessorTest
{
  protected:
    void lex()
    {
        pp::Token token;
        mPreprocessor.lex(&token);
        EXPECT_EQ(pp::Token::LAST, token.type);
        EXPECT_EQ("", token.value);
    }
};

TEST_F(VersionTest, Valid)
{
    const char* str = "#version 200\n";
    ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));

    using testing::_;
    EXPECT_CALL(mDirectiveHandler,
                handleVersion(pp::SourceLocation(0, 1), 200));
    // No error or warning.
    EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);

    lex();
}

TEST_F(VersionTest, CommentsIgnored)
{
    const char* str = "/*foo*/"
                      "#"
                      "/*foo*/"
                      "version"
                      "/*foo*/"
                      "200"
                      "/*foo*/"
                      "//foo"
                      "\n";
    ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));

    using testing::_;
    EXPECT_CALL(mDirectiveHandler,
                handleVersion(pp::SourceLocation(0, 1), 200));
    // No error or warning.
    EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);

    lex();
}

TEST_F(VersionTest, MissingNewline)
{
    const char* str = "#version 200";
    ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));

    using testing::_;
    // Directive successfully parsed.
    EXPECT_CALL(mDirectiveHandler,
                handleVersion(pp::SourceLocation(0, 1), 200));
    // Error reported about EOF.
    EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::EOF_IN_DIRECTIVE, _, _));

    lex();
}

struct VersionTestParam
{
    const char* str;
    pp::Diagnostics::ID id;
};

class InvalidVersionTest : public VersionTest,
                           public testing::WithParamInterface<VersionTestParam>
{
};

TEST_P(InvalidVersionTest, Identified)
{
    VersionTestParam param = GetParam();
    ASSERT_TRUE(mPreprocessor.init(1, &param.str, NULL));

    using testing::_;
    // No handleVersion call.
    EXPECT_CALL(mDirectiveHandler, handleVersion(_, _)).Times(0);
    // Invalid version directive call.
    EXPECT_CALL(mDiagnostics, print(param.id, pp::SourceLocation(0, 1), _));

    lex();
}

static const VersionTestParam kParams[] = {
    {"#version\n", pp::Diagnostics::INVALID_VERSION_DIRECTIVE},
    {"#version foo\n", pp::Diagnostics::INVALID_VERSION_NUMBER},
    {"#version 100 foo\n", pp::Diagnostics::UNEXPECTED_TOKEN}
};

INSTANTIATE_TEST_CASE_P(All, InvalidVersionTest, testing::ValuesIn(kParams));
<commit_msg>Updated VersionTest to use the same pattern as other tests.<commit_after>//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

#include "PreprocessorTest.h"
#include "Token.h"

class VersionTest : public PreprocessorTest
{
  protected:
    void preprocess(const char* str)
    {
        ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));

        pp::Token token;
        mPreprocessor.lex(&token);
        EXPECT_EQ(pp::Token::LAST, token.type);
        EXPECT_EQ("", token.value);
    }
};

TEST_F(VersionTest, Valid)
{
    const char* str = "#version 200\n";

    using testing::_;
    EXPECT_CALL(mDirectiveHandler,
                handleVersion(pp::SourceLocation(0, 1), 200));
    // No error or warning.
    EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);

    preprocess(str);
}

TEST_F(VersionTest, CommentsIgnored)
{
    const char* str = "/*foo*/"
                      "#"
                      "/*foo*/"
                      "version"
                      "/*foo*/"
                      "200"
                      "/*foo*/"
                      "//foo"
                      "\n";

    using testing::_;
    EXPECT_CALL(mDirectiveHandler,
                handleVersion(pp::SourceLocation(0, 1), 200));
    // No error or warning.
    EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);

    preprocess(str);
}

TEST_F(VersionTest, MissingNewline)
{
    const char* str = "#version 200";

    using testing::_;
    // Directive successfully parsed.
    EXPECT_CALL(mDirectiveHandler,
                handleVersion(pp::SourceLocation(0, 1), 200));
    // Error reported about EOF.
    EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::EOF_IN_DIRECTIVE, _, _));

    preprocess(str);
}

struct VersionTestParam
{
    const char* str;
    pp::Diagnostics::ID id;
};

class InvalidVersionTest : public VersionTest,
                           public testing::WithParamInterface<VersionTestParam>
{
};

TEST_P(InvalidVersionTest, Identified)
{
    VersionTestParam param = GetParam();

    using testing::_;
    // No handleVersion call.
    EXPECT_CALL(mDirectiveHandler, handleVersion(_, _)).Times(0);
    // Invalid version directive call.
    EXPECT_CALL(mDiagnostics, print(param.id, pp::SourceLocation(0, 1), _));

    preprocess(param.str);
}

static const VersionTestParam kParams[] = {
    {"#version\n", pp::Diagnostics::INVALID_VERSION_DIRECTIVE},
    {"#version foo\n", pp::Diagnostics::INVALID_VERSION_NUMBER},
    {"#version 100 foo\n", pp::Diagnostics::UNEXPECTED_TOKEN}
};

INSTANTIATE_TEST_CASE_P(All, InvalidVersionTest, testing::ValuesIn(kParams));
<|endoftext|>


<commit_before>//  example of macro to add two histogram files containing the same histograms
//  (in a directory structure) or ntuples/trees.
//  Histograms are added in memory as well as profile histograms.
//  ntuples and trees are merged.
//  The resulting histograms are saved into a new file.
//  original implementation : Rene Brun
//  extensions by Dirk Geppert to support files with sub-directories

//______________________________________________________________________
// give the list of files below. Last file must be a NULL string
const char *cfiles[] = {
  "file1.root",
  "file2.root",
  ""};
const char *outfile="file.root";
//______________________________________________________________________

TFile   *fnew;
TList   *flist;
TFile   *afile, *file1;

TH1     *h1, *h2;
TTree   *t1, *t2;
TObject *obj;
TKey    *key;

void AddRecursive(TDirectory *root,TDirectory* node);
//______________________________________________________________________
//
//
//
//______________________________________________________________________
void hadd() {

  // create the result file
  fnew = new TFile(outfile,"RECREATE");

  //create a support list for the input files
  flist = new TList();

  //open all input files and insert them in the list of files
  Int_t nfiles = 0;
  while (strlen(cfiles[nfiles])) {
    afile = new TFile(cfiles[nfiles]);
    flist->Add(afile);
    nfiles++;
  }

  //Get a pointer to the first file
  afile = file1 = (TFile*)flist->First();

  AddRecursive(fnew,file1);

  //fnew->ls();
  fnew->Write();
  fnew->Close();
  delete fnew;
  flist->Delete();
  delete flist;
}

//______________________________________________________________________
//
//
//
//______________________________________________________________________
void AddRecursive(TDirectory *root,TDirectory* node) {

  static TDirectory *dact;

  TDirectory *dirsav;

  //We create an iterator to loop on all objects(keys) of first file
  TIter nextkey(node->GetListOfKeys());
  while (key = (TKey*)nextkey()) {
    node->cd();
    obj = key->ReadObj();
    if (obj->IsA()->InheritsFrom("TTree")) { //case of a TTree or TNtuple
      t1 = (TTree*)obj;
      // this part still to be implemented
      // use TChain::Merge instead
    } elseif(obj->IsA()->InheritsFrom("TH1")) { //case of TH1 or TProfile
      h1 = (TH1*)obj;
      afile = (TFile*)flist->After(file1);
      while (afile) { //loop on all files starting at second file
        char* base=strstr(root->GetPath(),":"); base+=2;
        //printf("base=%s\n",base);

        dirsav=gDirectory;
        afile->cd(base);
        h2 = (TH1*)gDirectory->Get(h1->GetName());
        dirsav->cd();
        if (h2) { // here we should check that we can add
          //printf("adding histo %s to %s\n",h2->GetName(),h1->GetName());
          h1->Add(h2);
          delete h2;
        }
        afile = (TFile*)flist->After(afile);
      }
    } elseif(obj->IsA()->InheritsFrom("TDirectory")) { //case of TDirectory
      // recursion
      // printf("Found TDirectory name=%s title=%s\n",
      //     obj->GetName(),obj->GetTitle());
      root->cd();
      dact=root->mkdir(obj->GetName(),obj->GetTitle());
      dact->cd();
      AddRecursive(dact,(TDirectory*)obj);
    } else { //another object
      printf("anotherobjname=%s, title=%s\n",obj->GetName(),obj->GetTitle());
    }

    // write node object, modified or not into fnew
    if (obj) {
      root->cd();
      obj->Write(key->GetName());
      delete obj;
      obj=NULL;
    }
  }
  root->cd();
}

<commit_msg>Add the relevant statements to support the case of files with multiple sub directories. The global pointers obj and key must be saved and restored when creating a new subdirectory. Thanks to Erik Brubaker <brubaker@uclink.berkeley.edu> for reporting this problem<commit_after>//  example of macro to add two histogram files containing the same histograms
//  (in a directory structure) or ntuples/trees.
//  Histograms are added in memory as well as profile histograms.
//  ntuples and trees are merged.
//  The resulting histograms are saved into a new file.
//  original implementation : Rene Brun
//  extensions by Dirk Geppert to support files with sub-directories

//______________________________________________________________________
// give the list of files below. Last file must be a NULL string
const char *cfiles[] = {
  "file1.root",
  "file2.root",
  ""};
const char *outfile="file.root";
//______________________________________________________________________

TFile   *fnew;
TList   *flist;
TFile   *afile, *file1;

TH1     *h1, *h2;
TTree   *t1, *t2;
TObject *obj;
TKey    *key;

void AddRecursive(TDirectory *root,TDirectory* node);
//______________________________________________________________________
//
//
//
//______________________________________________________________________
void hadd() {

  // create the result file
  fnew = new TFile(outfile,"RECREATE");

  //create a support list for the input files
  flist = new TList();

  //open all input files and insert them in the list of files
  Int_t nfiles = 0;
  while (strlen(cfiles[nfiles])) {
    afile = new TFile(cfiles[nfiles]);
    flist->Add(afile);
    nfiles++;
  }

  //Get a pointer to the first file
  afile = file1 = (TFile*)flist->First();

  AddRecursive(fnew,file1);

  //fnew->ls();
  fnew->Write();
  fnew->Close();
  delete fnew;
  flist->Delete();
  delete flist;
}

//______________________________________________________________________
//
//
//
//______________________________________________________________________
void AddRecursive(TDirectory *root,TDirectory* node) {

  static TDirectory *dact;

  TDirectory *dirsav;

  //We create an iterator to loop on all objects(keys) of first file
  TIter nextkey(node->GetListOfKeys());
  while (key = (TKey*)nextkey()) {
    node->cd();
    obj = key->ReadObj();
    if (obj->IsA()->InheritsFrom("TTree")) { //case of a TTree or TNtuple
      t1 = (TTree*)obj;
      // this part still to be implemented
      // use TChain::Merge instead
    } elseif(obj->IsA()->InheritsFrom("TH1")) { //case of TH1 or TProfile
      h1 = (TH1*)obj;
      afile = (TFile*)flist->After(file1);
      while (afile) { //loop on all files starting at second file
        char* base=strstr(root->GetPath(),":"); base+=2;
        //printf("base=%s\n",base);

        dirsav=gDirectory;
        afile->cd(base);
        h2 = (TH1*)gDirectory->Get(h1->GetName());
        dirsav->cd();
        if (h2) { // here we should check that we can add
          //printf("adding histo %s to %s\n",h2->GetName(),h1->GetName());
          h1->Add(h2);
          delete h2;
        }
        afile = (TFile*)flist->After(afile);
      }
    } elseif(obj->IsA()->InheritsFrom("TDirectory")) { //case of TDirectory
      // recursion
      // printf("Found TDirectory name=%s title=%s\n",
      //     obj->GetName(),obj->GetTitle());
      root->cd();
      dact=root->mkdir(obj->GetName(),obj->GetTitle());
      dact->cd();
      TObject *objsave = obj;
      TKey    *keysave = key;
      AddRecursive(dact,(TDirectory*)obj);
      obj = objsave;
      key = keysave;
    } else { //another object
      printf("anotherobjname=%s, title=%s\n",obj->GetName(),obj->GetTitle());
    }

    // write node object, modified or not into fnew
    if (obj) {
      root->cd();
      obj->Write(key->GetName());
      delete obj;
      obj=NULL;
    }
  }
  root->cd();
}

<|endoftext|>


<commit_before>#include "mainWindow.h"

mainWindow::mainWindow(QWidget *parent)
  : QMainWindow(parent)
{
  // Setup GUI
  ui.setupUi(this);
  ui.actionQuit->setShortcut(QKeySequence::Quit);

  ui.equation->setText("4*((a*(1+sqrt(5))/2)^2*x^2-1*y^2)*((a*(1+sqrt(5))/2)^2*y^2-1*z^2)*((a*(1+sqrt(5))/2)^2*z^2-1*x^2)-1*(1+2*(a*(1+sqrt(5))/2))*(x^2+y^2+z^2-1*1)^2");
  connect(ui.equation, SIGNAL(editingFinished()), this, SLOT(equationChanged()));

  connect( ui.actionSurface_1, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface1()) );
  connect( ui.actionSurface_2, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface2()) );
  connect( ui.actionSurface_3, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface3()) );
  connect( ui.actionSurface_4, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface4()) );
  connect( ui.actionSurface_5, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface5()) );
  connect( ui.actionSurface_6, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface6()) );
  connect( ui.actionSurface_7, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface7()) );
  connect( ui.actionSurface_8, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface8()) );

  connect( ui.aSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderMoved(int)) );
  sliderMoved( ui.aSlider->value() );

  scene.surface.setEquation(ui.equation->text());
  ui.sceneWidget->setScene(&scene);
}

mainWindow::~mainWindow()
{
}

void mainWindow::setSampleSurface1()
{
  ui.equation->setText("(2*x^2 + y^2 + z^2 - 1)^3 - x^2*z^3/10 - y^2*z^3");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface2()
{
  ui.equation->setText("x^2 + 4 * y^2 + z^2 - 8");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface3()
{
  ui.equation->setText("(x^2 + y^2 + z^2 + 2)^2 - 12*(x^2 + y^2)");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface4()
{
  ui.equation->setText("x*y*z");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface5()
{
  ui.equation->setText("x^2 + y^2 + z - 1");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface6()
{
  ui.equation->setText("x^2 + y^2 - 1");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface7()
{
  ui.equation->setText("x^2 + y^2 - z^2");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface8()
{
  ui.equation->setText("4*((a*(1+sqrt(5))/2)^2*x^2-1*y^2)*((a*(1+sqrt(5))/2)^2*y^2-1*z^2)*((a*(1+sqrt(5))/2)^2*z^2-1*x^2)-1*(1+2*(a*(1+sqrt(5))/2))*(x^2+y^2+z^2-1*1)^2");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::equationChanged()
{
  scene.surface.setEquation(ui.equation->text());

  if (scene.surface.hasError()) {
    statusBar()->showMessage(scene.surface.errorString(), 2000);
    ui.equation->setCursorPosition(scene.surface.errorIndex());
  }
}

void mainWindow::sliderMoved(int val)
{
  qreal a = val/100.0;
  ui.aLabel->setText( QString("a = %1").arg(a));
  scene.surface.setA(a);
}
<commit_msg>Triviale Änderung. Am besten ignorieren.<commit_after>#include "mainWindow.h"

mainWindow::mainWindow(QWidget *parent)
  : QMainWindow(parent)
{
  // Setup GUI
  ui.setupUi(this);
  ui.actionQuit->setShortcut(QKeySequence::Quit);

  connect(ui.equation, SIGNAL(editingFinished()), this, SLOT(equationChanged()));

  connect(ui.aSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderMoved(int)) );
  sliderMoved( ui.aSlider->value() );

  connect( ui.actionSurface_1, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface1()) );
  connect( ui.actionSurface_2, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface2()) );
  connect( ui.actionSurface_3, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface3()) );
  connect( ui.actionSurface_4, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface4()) );
  connect( ui.actionSurface_5, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface5()) );
  connect( ui.actionSurface_6, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface6()) );
  connect( ui.actionSurface_7, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface7()) );
  connect( ui.actionSurface_8, SIGNAL(triggered(bool)), this, SLOT(setSampleSurface8()) );

  scene.surface.setEquation(ui.equation->text());
  ui.sceneWidget->setScene(&scene);

  setSampleSurface8();
}

mainWindow::~mainWindow()
{
}

void mainWindow::setSampleSurface1()
{
  ui.equation->setText("(2*x^2 + y^2 + z^2 - 1)^3 - x^2*z^3/10 - y^2*z^3");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface2()
{
  ui.equation->setText("x^2 + 4 * y^2 + z^2 - 8");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface3()
{
  ui.equation->setText("(x^2 + y^2 + z^2 + 2)^2 - 12*(x^2 + y^2)");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface4()
{
  ui.equation->setText("x*y*z");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface5()
{
  ui.equation->setText("x^2 + y^2 + z - 1");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface6()
{
  ui.equation->setText("x^2 + y^2 - 1");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface7()
{
  ui.equation->setText("x^2 + y^2 - z^2");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::setSampleSurface8()
{
  ui.equation->setText("4*((a*(1+sqrt(5))/2)^2*x^2-1*y^2)*((a*(1+sqrt(5))/2)^2*y^2-1*z^2)*((a*(1+sqrt(5))/2)^2*z^2-1*x^2)-1*(1+2*(a*(1+sqrt(5))/2))*(x^2+y^2+z^2-1*1)^2");
  ui.equation->setCursorPosition(0);
  equationChanged();
}

void mainWindow::equationChanged()
{
  scene.surface.setEquation(ui.equation->text());

  if (scene.surface.hasError()) {
    statusBar()->showMessage(scene.surface.errorString(), 2000);
    ui.equation->setCursorPosition(scene.surface.errorIndex());
  }
}

void mainWindow::sliderMoved(int val)
{
  qreal a = val/100.0;
  ui.aLabel->setText( QString("a = %1").arg(a));
  scene.surface.setA(a);
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 Eugene Lazin <4lazin@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "queryprocessor.h"
#include "util.h"

#include <random>
#include <algorithm>

#include <boost/lexical_cast.hpp>

namespace Akumuli {

BoltException::BoltException(Bolt::BoltType type, const char* msg)
    : std::runtime_error(msg)
    , type_(type)
{
}

Bolt::BoltType BoltException::get_type() const {
    return type_;
}

struct RandomSamplingBolt : std::enable_shared_from_this<RandomSamplingBolt>, Bolt {
    const uint32_t                      buffer_size_;
    std::vector<std::shared_ptr<Bolt>>  outputs_;
    std::vector<std::weak_ptr<Bolt>>    inputs_;
    std::vector<aku_Timestamp>          timestamps_;
    std::vector<aku_ParamId>            paramids_;
    std::vector<double>                 values_;
    Rand                                random_;

    RandomSamplingBolt(uint32_t buffer_size)
        : buffer_size_(buffer_size)
    {
    }

    // Bolt interface
    virtual void add_output(std::shared_ptr<Bolt> next) {
        outputs_.push_back(next);
    }

    virtual void add_input(std::weak_ptr<Bolt> input) {
        inputs_.push_back(input);
    }

    virtual BoltType get_bolt_type() const {
        return Bolt::RandomSampler;
    }

    virtual std::vector<std::shared_ptr<Bolt>> get_bolt_outputs() const {
        return outputs_;
    }

    virtual std::vector<std::shared_ptr<Bolt>> get_bolt_inputs() const {
        std::vector<std::shared_ptr<Bolt>> result;
        for(auto wref: inputs_) {
            result.push_back(wref.lock());
        }
        return result;
    }

    virtual void complete(std::shared_ptr<Bolt> caller) {

        // Reset caller in inputs list
        for(auto& wref: inputs_) {                              // TODO: this logic should be
            auto sref = wref.lock();                            // moved to separate mixin class
            if (sref && sref == caller) {                       // tested and inherited by all
                wref.reset();                                   // blots.
                break;
            }
        }

        // Check precondition (all inputs was gone)
        for(auto& wref: inputs_) {
            if (wref.expired() == false) {
                return;
            }
        }
        if (outputs_.empty()) {
            BoltException except(Bolt::RandomSampler, "no output bolt");
            BOOST_THROW_EXCEPTION(except);
        }

        // Do the actual job
        auto& tsarray = timestamps_;
        auto predicate = [&tsarray](uint32_t lhs, uint32_t rhs) {
            return tsarray.at(lhs) < tsarray.at(rhs);
        };

        std::vector<uint32_t> indexes;
        uint32_t gencnt = 0u;
        std::generate_n(std::back_inserter(indexes), timestamps_.size(), [&gencnt]() { return gencnt++; });
        std::stable_sort(indexes.begin(), indexes.end(), predicate);

        for(auto ix: indexes) {
            for(auto& bolt: outputs_) {
                bolt->put(timestamps_.at(ix),
                         paramids_.at(ix),
                         values_.at(ix));
            }
        }

        for(auto& bolt: outputs_) {
            bolt->complete(shared_from_this());
        }
    }

    virtual void put(aku_Timestamp ts, aku_ParamId id, double value) {
        if (outputs_.empty()) {
            BoltException except(Bolt::RandomSampler, "no output bolt");
            BOOST_THROW_EXCEPTION(except);
        }
        if (timestamps_.size() < buffer_size_) {
            // Just append new values
            timestamps_.push_back(ts);
            paramids_.push_back(id);
            values_.push_back(value);
        } else {
            // Flip a coin
            uint32_t ix = random_() % timestamps_.size();
            if (ix < buffer_size_) {
                timestamps_.at(ix) =    ts;
                paramids_.at(ix)   =    id;
                values_.at(ix)     = value;
            }
        }
    }
};

std::shared_ptr<Bolt> BoltsBuilder::make_random_sampler(std::string type,
                                                        size_t buffer_size,
                                                        aku_logger_cb_t logger)
{
    {
        std::stringstream logfmt;
        logfmt << "Creating random sampler of type " << type << " with buffer size " << buffer_size;
        (*logger)(AKU_LOG_TRACE, logfmt.str().c_str());
    }
    // only reservoir sampling is supported
    if (type != "reservoir") {
        BoltException except(Bolt::RandomSampler, "unsupported sampler type");
        BOOST_THROW_EXCEPTION(except);
    }
    // Build object
    return std::make_shared<RandomSamplingBolt>(buffer_size);
}

QueryProcessor::QueryProcessor(std::shared_ptr<Bolt> root,
               std::vector<std::string> metrics,
               aku_Timestamp begin,
               aku_Timestamp end)
    : lowerbound(std::min(begin, end))
    , upperbound(std::max(begin, end))
    , direction(begin > end ? AKU_CURSOR_DIR_FORWARD : AKU_CURSOR_DIR_BACKWARD)
    , metrics(metrics)
    , namesofinterest(StringTools::create_table(0x1000))
    , root_bolt(root)
{
}

int QueryProcessor::match(uint64_t param_id) {
    return -1;
}

}

<commit_msg>WIP: query processor<commit_after>/**
 * Copyright (c) 2015 Eugene Lazin <4lazin@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "queryprocessor.h"
#include "util.h"

#include <random>
#include <algorithm>

#include <boost/lexical_cast.hpp>

namespace Akumuli {

BoltException::BoltException(Bolt::BoltType type, const char* msg)
    : std::runtime_error(msg)
    , type_(type)
{
}

Bolt::BoltType BoltException::get_type() const {
    return type_;
}

/** Abstract graph node. Handles inputs and outputs.
  * Implements part of the Bolt interface.
  */
struct AcyclicGraphNode : Bolt
{
    std::vector<std::shared_ptr<Bolt>>  outputs_;
    std::vector<std::weak_ptr<Bolt>>    inputs_;

    virtual void add_output(std::shared_ptr<Bolt> next) {
        outputs_.push_back(next);
    }

    virtual void add_input(std::weak_ptr<Bolt> input) {
        inputs_.push_back(input);
    }

    virtual std::vector<std::shared_ptr<Bolt>> get_bolt_outputs() const {
        return outputs_;
    }

    virtual std::vector<std::shared_ptr<Bolt>> get_bolt_inputs() const {
        std::vector<std::shared_ptr<Bolt>> result;
        for(auto wref: inputs_) {
            result.push_back(wref.lock());
        }
        return result;
    }

    /** Remove input from inputs list.
      * @return true if all inputs was gone and false otherwise
      * @param caller is a pointer to input node that needs to be removed
      */
    bool remove_input(std::shared_ptr<Bolt> caller) {
        // Reset caller in inputs list
        for(auto& wref: inputs_) {
            auto sref = wref.lock();
            if (sref && sref == caller) {
                wref.reset();
                break;
            }
        }

        // Check precondition (all inputs was gone)
        for(auto& wref: inputs_) {
            if (wref.expired() == false) {
                return false;
            }
        }
        return true;
    }

    void distribute(aku_Timestamp ts, aku_ParamId pid, double value) {
        for(auto& bolt: outputs_) {
            bolt->put(ts, pid, value);
        }
    }

    /** Throw exception if there is no output node
      * @throw BoltException
      */
    void throw_if_no_output() {
        if (outputs_.empty()) {
            BoltException except(Bolt::RandomSampler, "no output bolt");
            BOOST_THROW_EXCEPTION(except);
        }
    }

    template<class Derived>
    void complete_children(std::shared_ptr<Derived> caller) {
        for(auto& bolt: outputs_) {
            bolt->complete(caller);
        }
    }
};

struct RandomSamplingBolt : std::enable_shared_from_this<RandomSamplingBolt>, AcyclicGraphNode {
    const uint32_t                      buffer_size_;
    std::vector<aku_Timestamp>          timestamps_;
    std::vector<aku_ParamId>            paramids_;
    std::vector<double>                 values_;
    Rand                                random_;

    RandomSamplingBolt(uint32_t buffer_size)
        : buffer_size_(buffer_size)
    {
    }

    // Bolt interface
    virtual BoltType get_bolt_type() const {
        return Bolt::RandomSampler;
    }

    virtual void complete(std::shared_ptr<Bolt> caller) {
        if (remove_input(caller)) {
            throw_if_no_output();

            // Do the actual job
            auto& tsarray = timestamps_;
            auto predicate = [&tsarray](uint32_t lhs, uint32_t rhs) {
                return tsarray.at(lhs) < tsarray.at(rhs);
            };

            std::vector<uint32_t> indexes;
            uint32_t gencnt = 0u;
            std::generate_n(std::back_inserter(indexes), timestamps_.size(), [&gencnt]() { return gencnt++; });
            std::stable_sort(indexes.begin(), indexes.end(), predicate);

            for(auto ix: indexes) {
                distribute(timestamps_.at(ix),
                           paramids_.at(ix),
                           values_.at(ix));
            }

            complete_children(shared_from_this());
        }
    }

    virtual void put(aku_Timestamp ts, aku_ParamId id, double value) {
        throw_if_no_output();
        if (timestamps_.size() < buffer_size_) {
            // Just append new values
            timestamps_.push_back(ts);
            paramids_.push_back(id);
            values_.push_back(value);
        } else {
            // Flip a coin
            uint32_t ix = random_() % timestamps_.size();
            if (ix < buffer_size_) {
                timestamps_.at(ix) =    ts;
                paramids_.at(ix)   =    id;
                values_.at(ix)     = value;
            }
        }
    }
};

/*
struct RandomSamplingBolt : AcyclicGraphNode<RandomSamplingBolt> {

    // Bolt interface
    virtual void complete(std::shared_ptr<Bolt> caller);
    virtual void put(aku_Timestamp ts, aku_ParamId id, double value);
    virtual void add_output(std::shared_ptr<Bolt> next);
    virtual void add_input(std::weak_ptr<Bolt> input);
    virtual BoltType get_bolt_type() const;
    virtual std::vector<std::shared_ptr<Bolt> > get_bolt_inputs() const;
    virtual std::vector<std::shared_ptr<Bolt> > get_bolt_outputs() const;
};
*/

//                                   //
//         Factory methods           //
//                                   //

std::shared_ptr<Bolt> BoltsBuilder::make_random_sampler(std::string type,
                                                        size_t buffer_size,
                                                        aku_logger_cb_t logger)
{
    {
        std::stringstream logfmt;
        logfmt << "Creating random sampler of type " << type << " with buffer size " << buffer_size;
        (*logger)(AKU_LOG_TRACE, logfmt.str().c_str());
    }
    // only reservoir sampling is supported
    if (type != "reservoir") {
        BoltException except(Bolt::RandomSampler, "unsupported sampler type");
        BOOST_THROW_EXCEPTION(except);
    }
    // Build object
    return std::make_shared<RandomSamplingBolt>(buffer_size);
}

QueryProcessor::QueryProcessor(std::shared_ptr<Bolt> root,
               std::vector<std::string> metrics,
               aku_Timestamp begin,
               aku_Timestamp end)
    : lowerbound(std::min(begin, end))
    , upperbound(std::max(begin, end))
    , direction(begin > end ? AKU_CURSOR_DIR_FORWARD : AKU_CURSOR_DIR_BACKWARD)
    , metrics(metrics)
    , namesofinterest(StringTools::create_table(0x1000))
    , root_bolt(root)
{
}

int QueryProcessor::match(uint64_t param_id) {
    return -1;
}

}

<|endoftext|>


<commit_before>/**
 * \file
 * \brief SoftwareTimerOperationsTestCase class implementation
 *
 * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "SoftwareTimerOperationsTestCase.hpp"

#include "waitForNextTick.hpp"

#include "distortos/StaticSoftwareTimer.hpp"
#include "distortos/ThisThread.hpp"

namespace distortos
{

namespace test
{

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool SoftwareTimerOperationsTestCase::run_() const
{
	volatile uint32_t value {};
	auto softwareTimer = makeStaticSoftwareTimer(
			[&value]()
			{
				++value;
			});

	{
		constexpr auto duration = TickClock::duration{11};

		if (softwareTimer.isRunning() != false || value != 0)	// initially must be stopped and must not execute
			return false;

		waitForNextTick();
		if (softwareTimer.start(duration) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 0)	// must be started, but may not execute yet
			return false;

		if (softwareTimer.stop() != 0)
			return false;
		if (softwareTimer.isRunning() != false || value != 0)	// must be stopped, must not execute
			return false;

		ThisThread::sleepFor(duration * 2);
		if (softwareTimer.isRunning() != false || value != 0)	// make sure it did not execute
			return false;
	}
	{
		constexpr auto duration = TickClock::duration{9};

		waitForNextTick();
		if (softwareTimer.start(duration) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 0)	// must be started, but may not execute yet
			return false;

		const auto start = TickClock::now();
		while (softwareTimer.isRunning() == true);

		// must be stopped, function must be executed, real duration must equal what is expected
		if (softwareTimer.isRunning() != false || value != 1 ||
				TickClock::now() - start != duration + decltype(duration){1})
			return false;
	}
	{
		waitForNextTick();
		const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{13};
		if (softwareTimer.start(wakeUpTimePoint) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 1)	// must be started, but may not execute yet
			return false;

		while (softwareTimer.isRunning() == true);

		// must be stopped, function must be executed, wake up time point must equal what is expected
		if (softwareTimer.isRunning() != false || value != 2 || TickClock::now() != wakeUpTimePoint)
			return false;
	}
	{
		constexpr auto period = TickClock::duration{6};

		waitForNextTick();
		const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{17};
		if (softwareTimer.start(wakeUpTimePoint, period) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 2)	// must be started, but may not execute yet
			return false;

		for (size_t iteration {}; iteration < 4; ++iteration)
		{
			while (softwareTimer.isRunning() == true && value == 2 + iteration);

			// must be still running, function must be executed, wake up time point must equal what is expected
			if (softwareTimer.isRunning() != true || value != 3 + iteration ||
					TickClock::now() != wakeUpTimePoint + period * iteration)
				return false;
		}

		if (softwareTimer.stop() != 0)
			return false;
		if (softwareTimer.isRunning() != false || value != 6)	// must be stopped
			return false;

		ThisThread::sleepFor(period * 2);
		if (softwareTimer.isRunning() != false || value != 6)	// make sure it did not execute again
			return false;
	}
	{
		// restart one-shot into one-shot
		for (size_t iteration {}; iteration < 4; ++iteration)
		{
			constexpr auto duration = TickClock::duration{15};
			const auto wakeUpTimePoint = TickClock::now() + duration;
			if (softwareTimer.start(wakeUpTimePoint) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;

			ThisThread::sleepUntil(wakeUpTimePoint - duration / 2);

			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;
		}

		// restart one-shot into periodic, then periodic into periodic
		for (size_t iteration {}; iteration < 4; ++iteration)
		{
			constexpr auto delay = TickClock::duration{12};

			const auto wakeUpTimePoint = TickClock::now() + delay;
			if (softwareTimer.start(wakeUpTimePoint, TickClock::duration{10}) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;

			ThisThread::sleepUntil(wakeUpTimePoint - delay / 2);

			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;
		}

		// restart periodic into periodic
		{
			constexpr auto period = TickClock::duration{19};

			waitForNextTick();
			const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{14};
			if (softwareTimer.start(wakeUpTimePoint, period) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;

			for (size_t iteration {}; iteration < 4; ++iteration)
			{
				while (softwareTimer.isRunning() == true && value == 6 + iteration);

				// must be still running, function must be executed, wake up time point must equal what is expected
				if (softwareTimer.isRunning() != true || value != 7 + iteration ||
						TickClock::now() != wakeUpTimePoint + period * iteration)
					return false;
			}
		}

		// restart periodic into periodic with different settings
		{
			constexpr auto period = TickClock::duration{7};

			waitForNextTick();
			const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{5};
			if (softwareTimer.start(wakeUpTimePoint, period) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 10)	// must be started, but may not execute yet
				return false;

			for (size_t iteration {}; iteration < 4; ++iteration)
			{
				while (softwareTimer.isRunning() == true && value == 10 + iteration);

				// must be still running, function must be executed, wake up time point must equal what is expected
				if (softwareTimer.isRunning() != true || value != 11 + iteration ||
						TickClock::now() != wakeUpTimePoint + period * iteration)
					return false;
			}
		}

		// restart periodic into one-shot
		waitForNextTick();
		const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{16};
		if (softwareTimer.start(wakeUpTimePoint) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 14)	// must be started, but may not execute yet
			return false;

		while (softwareTimer.isRunning() == true);

		// must be stopped, function must be executed, wake up time point must equal what is expected
		if (softwareTimer.isRunning() != false || value != 15 || TickClock::now() != wakeUpTimePoint)
			return false;
	}

	return true;
}

}	// namespace test

}	// namespace distortos
<commit_msg>Use wrapper class instead of lambda in SoftwareTimerOperationsTestCase<commit_after>/**
 * \file
 * \brief SoftwareTimerOperationsTestCase class implementation
 *
 * \author Copyright (C) 2014-2016 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "SoftwareTimerOperationsTestCase.hpp"

#include "waitForNextTick.hpp"

#include "distortos/StaticSoftwareTimer.hpp"
#include "distortos/ThisThread.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local types
+---------------------------------------------------------------------------------------------------------------------*/

/// wrapper for software timer
struct SoftwareTimerWrapper
{
public:

	/**
	 * \brief SoftwareTimerWrapper's constructor
	 *
	 * \param [in,out] value is a reference to volatile uint32_t variable which will be incremented when timer's
	 * function is executed
	 */

	explicit SoftwareTimerWrapper(volatile uint32_t& value) :
			softwareTimer_{&SoftwareTimerWrapper::function, *this},
			value_{value}
	{

	}

	/**
	 * \return reference to internal software timer
	 */

	SoftwareTimer& get()
	{
		return softwareTimer_;
	}

private:

	/**
	 * \brief Function used by software timer
	 */

	void function()
	{
		++value_;
	}

	/// type of internal software timer
	using SoftwareTimerType = decltype(makeStaticSoftwareTimer(&SoftwareTimerWrapper::function,
			std::ref(std::declval<SoftwareTimerWrapper&>())));

	/// internal software timer
	SoftwareTimerType softwareTimer_;

	/// reference to volatile uint32_t variable which will be incremented when timer's function is executed
	volatile uint32_t& value_;
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

bool SoftwareTimerOperationsTestCase::run_() const
{
	volatile uint32_t value {};
	SoftwareTimerWrapper softwareTimerWrapper {value};
	auto& softwareTimer = softwareTimerWrapper.get();

	{
		constexpr auto duration = TickClock::duration{11};

		if (softwareTimer.isRunning() != false || value != 0)	// initially must be stopped and must not execute
			return false;

		waitForNextTick();
		if (softwareTimer.start(duration) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 0)	// must be started, but may not execute yet
			return false;

		if (softwareTimer.stop() != 0)
			return false;
		if (softwareTimer.isRunning() != false || value != 0)	// must be stopped, must not execute
			return false;

		ThisThread::sleepFor(duration * 2);
		if (softwareTimer.isRunning() != false || value != 0)	// make sure it did not execute
			return false;
	}
	{
		constexpr auto duration = TickClock::duration{9};

		waitForNextTick();
		if (softwareTimer.start(duration) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 0)	// must be started, but may not execute yet
			return false;

		const auto start = TickClock::now();
		while (softwareTimer.isRunning() == true);

		// must be stopped, function must be executed, real duration must equal what is expected
		if (softwareTimer.isRunning() != false || value != 1 ||
				TickClock::now() - start != duration + decltype(duration){1})
			return false;
	}
	{
		waitForNextTick();
		const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{13};
		if (softwareTimer.start(wakeUpTimePoint) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 1)	// must be started, but may not execute yet
			return false;

		while (softwareTimer.isRunning() == true);

		// must be stopped, function must be executed, wake up time point must equal what is expected
		if (softwareTimer.isRunning() != false || value != 2 || TickClock::now() != wakeUpTimePoint)
			return false;
	}
	{
		constexpr auto period = TickClock::duration{6};

		waitForNextTick();
		const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{17};
		if (softwareTimer.start(wakeUpTimePoint, period) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 2)	// must be started, but may not execute yet
			return false;

		for (size_t iteration {}; iteration < 4; ++iteration)
		{
			while (softwareTimer.isRunning() == true && value == 2 + iteration);

			// must be still running, function must be executed, wake up time point must equal what is expected
			if (softwareTimer.isRunning() != true || value != 3 + iteration ||
					TickClock::now() != wakeUpTimePoint + period * iteration)
				return false;
		}

		if (softwareTimer.stop() != 0)
			return false;
		if (softwareTimer.isRunning() != false || value != 6)	// must be stopped
			return false;

		ThisThread::sleepFor(period * 2);
		if (softwareTimer.isRunning() != false || value != 6)	// make sure it did not execute again
			return false;
	}
	{
		// restart one-shot into one-shot
		for (size_t iteration {}; iteration < 4; ++iteration)
		{
			constexpr auto duration = TickClock::duration{15};
			const auto wakeUpTimePoint = TickClock::now() + duration;
			if (softwareTimer.start(wakeUpTimePoint) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;

			ThisThread::sleepUntil(wakeUpTimePoint - duration / 2);

			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;
		}

		// restart one-shot into periodic, then periodic into periodic
		for (size_t iteration {}; iteration < 4; ++iteration)
		{
			constexpr auto delay = TickClock::duration{12};

			const auto wakeUpTimePoint = TickClock::now() + delay;
			if (softwareTimer.start(wakeUpTimePoint, TickClock::duration{10}) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;

			ThisThread::sleepUntil(wakeUpTimePoint - delay / 2);

			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;
		}

		// restart periodic into periodic
		{
			constexpr auto period = TickClock::duration{19};

			waitForNextTick();
			const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{14};
			if (softwareTimer.start(wakeUpTimePoint, period) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 6)	// must be started, but may not execute yet
				return false;

			for (size_t iteration {}; iteration < 4; ++iteration)
			{
				while (softwareTimer.isRunning() == true && value == 6 + iteration);

				// must be still running, function must be executed, wake up time point must equal what is expected
				if (softwareTimer.isRunning() != true || value != 7 + iteration ||
						TickClock::now() != wakeUpTimePoint + period * iteration)
					return false;
			}
		}

		// restart periodic into periodic with different settings
		{
			constexpr auto period = TickClock::duration{7};

			waitForNextTick();
			const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{5};
			if (softwareTimer.start(wakeUpTimePoint, period) != 0)
				return false;
			if (softwareTimer.isRunning() != true || value != 10)	// must be started, but may not execute yet
				return false;

			for (size_t iteration {}; iteration < 4; ++iteration)
			{
				while (softwareTimer.isRunning() == true && value == 10 + iteration);

				// must be still running, function must be executed, wake up time point must equal what is expected
				if (softwareTimer.isRunning() != true || value != 11 + iteration ||
						TickClock::now() != wakeUpTimePoint + period * iteration)
					return false;
			}
		}

		// restart periodic into one-shot
		waitForNextTick();
		const auto wakeUpTimePoint = TickClock::now() + TickClock::duration{16};
		if (softwareTimer.start(wakeUpTimePoint) != 0)
			return false;
		if (softwareTimer.isRunning() != true || value != 14)	// must be started, but may not execute yet
			return false;

		while (softwareTimer.isRunning() == true);

		// must be stopped, function must be executed, wake up time point must equal what is expected
		if (softwareTimer.isRunning() != false || value != 15 || TickClock::now() != wakeUpTimePoint)
			return false;
	}

	return true;
}

}	// namespace test

}	// namespace distortos
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla 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.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include "test/boost/sstable_test.hh"
#include <seastar/core/thread.hh>
#include "sstables/sstables.hh"
#include "test/lib/mutation_source_test.hh"
#include "test/lib/sstable_utils.hh"
#include "row_cache.hh"
#include "test/lib/simple_schema.hh"
#include "partition_slice_builder.hh"
#include "test/lib/flat_mutation_reader_assertions.hh"
#include "test/lib/random_utils.hh"
#include "test/lib/random_schema.hh"

using namespace sstables;
using namespace std::chrono_literals;

static
mutation_source make_sstable_mutation_source(sstables::test_env& env, schema_ptr s, sstring dir, std::vector<mutation> mutations,
        sstable_writer_config cfg, sstables::sstable::version_types version, gc_clock::time_point query_time = gc_clock::now()) {
    return as_mutation_source(make_sstable(env, s, dir, std::move(mutations), cfg, version, query_time));
}

static void consume_all(flat_mutation_reader& rd) {
    while (auto mfopt = rd().get0()) {}
}

// It is assumed that src won't change.
static snapshot_source snapshot_source_from_snapshot(mutation_source src) {
    return snapshot_source([src = std::move(src)] {
        return src;
    });
}

static
void test_cache_population_with_range_tombstone_adjacent_to_population_range(populate_fn_ex populate) {
    simple_schema s;
    tests::reader_concurrency_semaphore_wrapper semaphore;
    auto cache_mt = make_lw_shared<memtable>(s.schema());

    auto pkey = s.make_pkey();

    // underlying should not be empty, otherwise cache will make the whole range continuous
    mutation m1(s.schema(), pkey);
    s.add_row(m1, s.make_ckey(0), "v1");
    s.add_row(m1, s.make_ckey(1), "v2");
    s.add_row(m1, s.make_ckey(2), "v3");
    s.delete_range(m1, s.make_ckey_range(2, 100));
    cache_mt->apply(m1);

    cache_tracker tracker;
    auto ms = populate(s.schema(), std::vector<mutation>({m1}), gc_clock::now());
    row_cache cache(s.schema(), snapshot_source_from_snapshot(std::move(ms)), tracker);

    auto pr = dht::partition_range::make_singular(pkey);

    auto populate_range = [&] (int start) {
        auto slice = partition_slice_builder(*s.schema())
                .with_range(query::clustering_range::make_singular(s.make_ckey(start)))
                .build();
        auto rd = cache.make_reader(s.schema(), semaphore.make_permit(), pr, slice);
        auto close_rd = deferred_close(rd);
        consume_all(rd);
    };

    populate_range(2);

    // The cache now has only row with ckey 2 populated and the rest is discontinuous.
    // Populating reader which stops populating at entry with ckey 2 should not forget
    // to emit range_tombstone which starts at before(2).

    assert_that(cache.make_reader(s.schema(), semaphore.make_permit()))
            .produces(m1)
            .produces_end_of_stream();
}

static future<> test_sstable_conforms_to_mutation_source(sstable_version_types version, int index_block_size) {
    return sstables::test_env::do_with_async([version, index_block_size] (sstables::test_env& env) {
        sstable_writer_config cfg = env.manager().configure_writer();
        cfg.promoted_index_block_size = index_block_size;

        std::vector<tmpdir> dirs;
        auto populate = [&env, &dirs, &cfg, version] (schema_ptr s, const std::vector<mutation>& partitions,
                                                      gc_clock::time_point query_time) -> mutation_source {
            dirs.emplace_back();
            return make_sstable_mutation_source(env, s, dirs.back().path().string(), partitions, cfg, version, query_time);
        };

        run_mutation_source_tests(populate);

        if (index_block_size == 1) {
            // The tests below are not sensitive to index bock size so run once.
            test_cache_population_with_range_tombstone_adjacent_to_population_range(populate);
        }
    });
}

static constexpr std::array<int, 3> block_sizes = { 1, 128, 64 * 1024 };

// Split for better parallelizm

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[2]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[2]);
}

// This assert makes sure we don't miss writable vertions
static_assert(writable_sstable_versions.size() == 2);

// TODO: could probably be placed in random_utils/random_schema after some tuning
static std::vector<query::clustering_range> random_ranges(const std::vector<clustering_key> keys, const schema& s, std::mt19937& engine) {
    std::vector<clustering_key_prefix> ckp_sample;
    for (auto& k: keys) {
        auto exploded = k.explode(s);
        auto key_size = tests::random::get_int<size_t>(1, exploded.size(), engine);
        ckp_sample.push_back(clustering_key_prefix::from_exploded(s,
            std::vector<bytes>{exploded.begin(), exploded.begin() + key_size}));
    }

    auto subset_size = tests::random::get_int<size_t>(0, ckp_sample.size(), engine);
    ckp_sample = tests::random::random_subset<clustering_key_prefix>(std::move(ckp_sample), subset_size, engine);
    std::sort(ckp_sample.begin(), ckp_sample.end(), clustering_key_prefix::less_compare(s));

    std::vector<query::clustering_range> ranges;

    // (-inf, ...
    if (!ckp_sample.empty() && tests::random::get_bool(engine)) {
        ranges.emplace_back(
                std::nullopt,
                query::clustering_range::bound{ckp_sample.front(), tests::random::get_bool(engine)});
        std::swap(ckp_sample.front(), ckp_sample.back());
        ckp_sample.pop_back();
    }

    // ..., +inf)
    std::optional<query::clustering_range> last_range;
    if (!ckp_sample.empty() && tests::random::get_bool(engine)) {
        last_range.emplace(
                query::clustering_range::bound{ckp_sample.back(), tests::random::get_bool(engine)},
                std::nullopt);
        ckp_sample.pop_back();
    }

    for (unsigned i = 0; i + 1 < ckp_sample.size(); i += 2) {
        ranges.emplace_back(
                query::clustering_range::bound{ckp_sample[i], tests::random::get_bool(engine)},
                query::clustering_range::bound{ckp_sample[i+1], tests::random::get_bool(engine)});
    }

    if (last_range) {
        ranges.push_back(std::move(*last_range));
    }

    if (ranges.empty()) {
        // no keys sampled, return (-inf, +inf)
        ranges.push_back(query::clustering_range::make_open_ended_both_sides());
    }

    return ranges;
}

SEASTAR_THREAD_TEST_CASE(test_sstable_reversing_reader_random_schema) {
    // Create two sources: one by creating an sstable from a set of mutations,
    // and one by creating an sstable from the same set of mutations but reversed.
    // We query the first source in forward mode and the second in reversed mode.
    // The two queries should return the same result.

    auto random_spec = tests::make_random_schema_specification(get_name());
    auto random_schema = tests::random_schema{tests::random::get_int<uint32_t>(), *random_spec};
    testlog.debug("Random schema:\n{}", random_schema.cql());

    auto muts = tests::generate_random_mutations(random_schema).get();

    // FIXME: workaround for #9352. The index pages for reversed source would sometimes be different
    // from the forward source, causing one source to hit the bug from #9352 but not the other.
    muts.erase(std::remove_if(muts.begin(), muts.end(), [] (auto& m) { return m.decorated_key().token() == dht::token::from_int64(0); }));

    std::vector<mutation> reversed_muts;
    for (auto& m : muts) {
        reversed_muts.push_back(reverse(m));
    }

    sstables::test_env::do_with([
            s = random_schema.schema(), muts = std::move(muts), reversed_muts = std::move(reversed_muts),
            version = writable_sstable_versions[1]] (sstables::test_env& env) {

        std::vector<tmpdir> dirs;
        sstable_writer_config cfg = env.manager().configure_writer();

        for (auto index_block_size: block_sizes) {
            cfg.promoted_index_block_size = index_block_size;

            dirs.emplace_back();
            auto source = make_sstable_mutation_source(env, s, dirs.back().path().string(), muts, cfg, version);

            dirs.emplace_back();
            auto rev_source = make_sstable_mutation_source(env, s->make_reversed(), dirs.back().path().string(), reversed_muts, cfg, version);

            tests::reader_concurrency_semaphore_wrapper semaphore;

            std::mt19937 engine{tests::random::get_int<uint32_t>()};

            std::vector<clustering_key> keys;
            for (const auto& m: muts) {
                for (auto& r: m.partition().clustered_rows()) {
                    keys.push_back(r.key());
                }
            }

            auto slice = partition_slice_builder(*s)
                .with_ranges(random_ranges(keys, *s, engine))
                .build();

            testlog.trace("Slice: {}", slice);

            for (const auto& m: muts) {
                auto prange = dht::partition_range::make_singular(m.decorated_key());

                auto r1 = source.make_reader(s, semaphore.make_permit(), prange,
                        slice, default_priority_class(), nullptr,
                        streamed_mutation::forwarding::no, mutation_reader::forwarding::no);
                auto close_r1 = deferred_action([&r1] { r1.close().get(); });

                auto rev_slice = native_reverse_slice_to_legacy_reverse_slice(*s, slice);
                rev_slice.options.set(query::partition_slice::option::reversed);
                auto r2 = rev_source.make_reader(s, semaphore.make_permit(), prange,
                        rev_slice, default_priority_class(), nullptr,
                        streamed_mutation::forwarding::no, mutation_reader::forwarding::no);
                close_r1.cancel();

                compare_readers(*s, std::move(r1), std::move(r2));
            }
        }
    }).get();
}
<commit_msg>test: sstable_conforms_to_mutation_source_test: fix `vector::erase` call<commit_after>/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla 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.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include "test/boost/sstable_test.hh"
#include <seastar/core/thread.hh>
#include "sstables/sstables.hh"
#include "test/lib/mutation_source_test.hh"
#include "test/lib/sstable_utils.hh"
#include "row_cache.hh"
#include "test/lib/simple_schema.hh"
#include "partition_slice_builder.hh"
#include "test/lib/flat_mutation_reader_assertions.hh"
#include "test/lib/random_utils.hh"
#include "test/lib/random_schema.hh"

using namespace sstables;
using namespace std::chrono_literals;

static
mutation_source make_sstable_mutation_source(sstables::test_env& env, schema_ptr s, sstring dir, std::vector<mutation> mutations,
        sstable_writer_config cfg, sstables::sstable::version_types version, gc_clock::time_point query_time = gc_clock::now()) {
    return as_mutation_source(make_sstable(env, s, dir, std::move(mutations), cfg, version, query_time));
}

static void consume_all(flat_mutation_reader& rd) {
    while (auto mfopt = rd().get0()) {}
}

// It is assumed that src won't change.
static snapshot_source snapshot_source_from_snapshot(mutation_source src) {
    return snapshot_source([src = std::move(src)] {
        return src;
    });
}

static
void test_cache_population_with_range_tombstone_adjacent_to_population_range(populate_fn_ex populate) {
    simple_schema s;
    tests::reader_concurrency_semaphore_wrapper semaphore;
    auto cache_mt = make_lw_shared<memtable>(s.schema());

    auto pkey = s.make_pkey();

    // underlying should not be empty, otherwise cache will make the whole range continuous
    mutation m1(s.schema(), pkey);
    s.add_row(m1, s.make_ckey(0), "v1");
    s.add_row(m1, s.make_ckey(1), "v2");
    s.add_row(m1, s.make_ckey(2), "v3");
    s.delete_range(m1, s.make_ckey_range(2, 100));
    cache_mt->apply(m1);

    cache_tracker tracker;
    auto ms = populate(s.schema(), std::vector<mutation>({m1}), gc_clock::now());
    row_cache cache(s.schema(), snapshot_source_from_snapshot(std::move(ms)), tracker);

    auto pr = dht::partition_range::make_singular(pkey);

    auto populate_range = [&] (int start) {
        auto slice = partition_slice_builder(*s.schema())
                .with_range(query::clustering_range::make_singular(s.make_ckey(start)))
                .build();
        auto rd = cache.make_reader(s.schema(), semaphore.make_permit(), pr, slice);
        auto close_rd = deferred_close(rd);
        consume_all(rd);
    };

    populate_range(2);

    // The cache now has only row with ckey 2 populated and the rest is discontinuous.
    // Populating reader which stops populating at entry with ckey 2 should not forget
    // to emit range_tombstone which starts at before(2).

    assert_that(cache.make_reader(s.schema(), semaphore.make_permit()))
            .produces(m1)
            .produces_end_of_stream();
}

static future<> test_sstable_conforms_to_mutation_source(sstable_version_types version, int index_block_size) {
    return sstables::test_env::do_with_async([version, index_block_size] (sstables::test_env& env) {
        sstable_writer_config cfg = env.manager().configure_writer();
        cfg.promoted_index_block_size = index_block_size;

        std::vector<tmpdir> dirs;
        auto populate = [&env, &dirs, &cfg, version] (schema_ptr s, const std::vector<mutation>& partitions,
                                                      gc_clock::time_point query_time) -> mutation_source {
            dirs.emplace_back();
            return make_sstable_mutation_source(env, s, dirs.back().path().string(), partitions, cfg, version, query_time);
        };

        run_mutation_source_tests(populate);

        if (index_block_size == 1) {
            // The tests below are not sensitive to index bock size so run once.
            test_cache_population_with_range_tombstone_adjacent_to_population_range(populate);
        }
    });
}

static constexpr std::array<int, 3> block_sizes = { 1, 128, 64 * 1024 };

// Split for better parallelizm

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_mc_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[0], block_sizes[2]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_tiny) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[0]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_medium) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[1]);
}

SEASTAR_TEST_CASE(test_sstable_conforms_to_mutation_source_md_large) {
    return test_sstable_conforms_to_mutation_source(writable_sstable_versions[1], block_sizes[2]);
}

// This assert makes sure we don't miss writable vertions
static_assert(writable_sstable_versions.size() == 2);

// TODO: could probably be placed in random_utils/random_schema after some tuning
static std::vector<query::clustering_range> random_ranges(const std::vector<clustering_key> keys, const schema& s, std::mt19937& engine) {
    std::vector<clustering_key_prefix> ckp_sample;
    for (auto& k: keys) {
        auto exploded = k.explode(s);
        auto key_size = tests::random::get_int<size_t>(1, exploded.size(), engine);
        ckp_sample.push_back(clustering_key_prefix::from_exploded(s,
            std::vector<bytes>{exploded.begin(), exploded.begin() + key_size}));
    }

    auto subset_size = tests::random::get_int<size_t>(0, ckp_sample.size(), engine);
    ckp_sample = tests::random::random_subset<clustering_key_prefix>(std::move(ckp_sample), subset_size, engine);
    std::sort(ckp_sample.begin(), ckp_sample.end(), clustering_key_prefix::less_compare(s));

    std::vector<query::clustering_range> ranges;

    // (-inf, ...
    if (!ckp_sample.empty() && tests::random::get_bool(engine)) {
        ranges.emplace_back(
                std::nullopt,
                query::clustering_range::bound{ckp_sample.front(), tests::random::get_bool(engine)});
        std::swap(ckp_sample.front(), ckp_sample.back());
        ckp_sample.pop_back();
    }

    // ..., +inf)
    std::optional<query::clustering_range> last_range;
    if (!ckp_sample.empty() && tests::random::get_bool(engine)) {
        last_range.emplace(
                query::clustering_range::bound{ckp_sample.back(), tests::random::get_bool(engine)},
                std::nullopt);
        ckp_sample.pop_back();
    }

    for (unsigned i = 0; i + 1 < ckp_sample.size(); i += 2) {
        ranges.emplace_back(
                query::clustering_range::bound{ckp_sample[i], tests::random::get_bool(engine)},
                query::clustering_range::bound{ckp_sample[i+1], tests::random::get_bool(engine)});
    }

    if (last_range) {
        ranges.push_back(std::move(*last_range));
    }

    if (ranges.empty()) {
        // no keys sampled, return (-inf, +inf)
        ranges.push_back(query::clustering_range::make_open_ended_both_sides());
    }

    return ranges;
}

SEASTAR_THREAD_TEST_CASE(test_sstable_reversing_reader_random_schema) {
    // Create two sources: one by creating an sstable from a set of mutations,
    // and one by creating an sstable from the same set of mutations but reversed.
    // We query the first source in forward mode and the second in reversed mode.
    // The two queries should return the same result.

    auto random_spec = tests::make_random_schema_specification(get_name());
    auto random_schema = tests::random_schema{tests::random::get_int<uint32_t>(), *random_spec};
    testlog.debug("Random schema:\n{}", random_schema.cql());

    auto muts = tests::generate_random_mutations(random_schema).get();

    // FIXME: workaround for #9352. The index pages for reversed source would sometimes be different
    // from the forward source, causing one source to hit the bug from #9352 but not the other.
    muts.erase(std::remove_if(muts.begin(), muts.end(), [] (auto& m) { return m.decorated_key().token() == dht::token::from_int64(0); }), muts.end());

    std::vector<mutation> reversed_muts;
    for (auto& m : muts) {
        reversed_muts.push_back(reverse(m));
    }

    sstables::test_env::do_with([
            s = random_schema.schema(), muts = std::move(muts), reversed_muts = std::move(reversed_muts),
            version = writable_sstable_versions[1]] (sstables::test_env& env) {

        std::vector<tmpdir> dirs;
        sstable_writer_config cfg = env.manager().configure_writer();

        for (auto index_block_size: block_sizes) {
            cfg.promoted_index_block_size = index_block_size;

            dirs.emplace_back();
            auto source = make_sstable_mutation_source(env, s, dirs.back().path().string(), muts, cfg, version);

            dirs.emplace_back();
            auto rev_source = make_sstable_mutation_source(env, s->make_reversed(), dirs.back().path().string(), reversed_muts, cfg, version);

            tests::reader_concurrency_semaphore_wrapper semaphore;

            std::mt19937 engine{tests::random::get_int<uint32_t>()};

            std::vector<clustering_key> keys;
            for (const auto& m: muts) {
                for (auto& r: m.partition().clustered_rows()) {
                    keys.push_back(r.key());
                }
            }

            auto slice = partition_slice_builder(*s)
                .with_ranges(random_ranges(keys, *s, engine))
                .build();

            testlog.trace("Slice: {}", slice);

            for (const auto& m: muts) {
                auto prange = dht::partition_range::make_singular(m.decorated_key());

                auto r1 = source.make_reader(s, semaphore.make_permit(), prange,
                        slice, default_priority_class(), nullptr,
                        streamed_mutation::forwarding::no, mutation_reader::forwarding::no);
                auto close_r1 = deferred_action([&r1] { r1.close().get(); });

                auto rev_slice = native_reverse_slice_to_legacy_reverse_slice(*s, slice);
                rev_slice.options.set(query::partition_slice::option::reversed);
                auto r2 = rev_source.make_reader(s, semaphore.make_permit(), prange,
                        rev_slice, default_priority_class(), nullptr,
                        streamed_mutation::forwarding::no, mutation_reader::forwarding::no);
                close_r1.cancel();

                compare_readers(*s, std::move(r1), std::move(r2));
            }
        }
    }).get();
}
<|endoftext|>


<commit_before>/*
 * Monitor which expects a string on a TCP connection.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_expect_monitor.hxx"
#include "lb_monitor.hxx"
#include "lb_config.hxx"
#include "pool.hxx"
#include "async.hxx"
#include "gerrno.h"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"

#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>

struct ExpectMonitor final : ConnectSocketHandler {
    struct pool &pool;
    const LbMonitorConfig &config;

    int fd;

    Event event;

    LbMonitorHandler &handler;

    struct async_operation_ref &async_ref;
    struct async_operation operation;

    ExpectMonitor(struct pool &_pool, const LbMonitorConfig &_config,
                  LbMonitorHandler &_handler,
                  async_operation_ref &_async_ref)
        :pool(_pool), config(_config),
         handler(_handler),
         async_ref(_async_ref) {}

    ExpectMonitor(const ExpectMonitor &other) = delete;

    void EventCallback(evutil_socket_t _fd, short events);

    void Abort();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;

    void OnSocketConnectTimeout() override {
        handler.Timeout();
        delete this;
    }

    void OnSocketConnectError(GError *error) override {
        handler.Error(error);
        delete this;
    }
};

static bool
check_expectation(char *received, size_t received_length,
                  const char *expect)
{
    return g_strrstr_len(received, received_length, expect) != NULL;
}

/*
 * async operation
 *
 */

inline void
ExpectMonitor::Abort()
{
    event.Delete();
    close(fd);
    pool_unref(&pool);
    delete this;
}

/*
 * libevent callback
 *
 */

inline void
ExpectMonitor::EventCallback(evutil_socket_t _fd, short events)
{
    operation.Finished();

    if (events & EV_TIMEOUT) {
        close(fd);
        handler.Timeout();
    } else {
        char buffer[1024];

        ssize_t nbytes = recv(_fd, buffer, sizeof(buffer),
                              MSG_DONTWAIT);
        if (nbytes < 0) {
            GError *error = new_error_errno();
            close(fd);
            handler.Error(error);
        } else if (!config.fade_expect.empty() &&
                   check_expectation(buffer, nbytes,
                                     config.fade_expect.c_str())) {
            close(fd);
            handler.Fade();
        } else if (config.expect.empty() ||
                   check_expectation(buffer, nbytes,
                                     config.expect.c_str())) {
            close(fd);
            handler.Success();
        } else {
            close(fd);
            GError *error = g_error_new_literal(g_file_error_quark(), 0,
                                                "Expectation failed");
            handler.Error(error);
        }
    }

    pool_unref(&pool);
    delete this;
    pool_commit();
}

/*
 * client_socket handler
 *
 */

void
ExpectMonitor::OnSocketConnectSuccess(SocketDescriptor &&new_fd)
{
    if (!config.send.empty()) {
        ssize_t nbytes = send(new_fd.Get(), config.send.data(),
                              config.send.length(),
                              MSG_DONTWAIT);
        if (nbytes < 0) {
            GError *error = new_error_errno();
            handler.Error(error);
            return;
        }
    }

    struct timeval expect_timeout = {
        time_t(config.timeout > 0 ? config.timeout : 10),
        0,
    };

    fd = new_fd.Steal();
    event.Set(fd, EV_READ|EV_TIMEOUT,
              MakeEventCallback(ExpectMonitor, EventCallback), this);
    event.Add(expect_timeout);

    operation.Init2<ExpectMonitor>();
    async_ref.Set(operation);

    pool_ref(&pool);
}

/*
 * lb_monitor_class
 *
 */

static void
expect_monitor_run(gcc_unused EventLoop &event_loop, struct pool &pool,
                   const LbMonitorConfig &config,
                   SocketAddress address,
                   LbMonitorHandler &handler,
                   struct async_operation_ref &async_ref)
{
    ExpectMonitor *expect = new ExpectMonitor(pool, config,
                                              handler,
                                              async_ref);

    const unsigned connect_timeout = config.connect_timeout > 0
        ? config.connect_timeout
        : (config.timeout > 0
           ? config.timeout
           : 30);

    client_socket_new(pool, address.GetFamily(), SOCK_STREAM, 0,
                      false,
                      SocketAddress::Null(),
                      address,
                      connect_timeout,
                      *expect, async_ref);
}

const LbMonitorClass expect_monitor_class = {
    .run = expect_monitor_run,
};
<commit_msg>lb_expect_monitor: use class SocketEvent<commit_after>/*
 * Monitor which expects a string on a TCP connection.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_expect_monitor.hxx"
#include "lb_monitor.hxx"
#include "lb_config.hxx"
#include "pool.hxx"
#include "async.hxx"
#include "gerrno.h"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"
#include "event/SocketEvent.hxx"

#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>

struct ExpectMonitor final : ConnectSocketHandler {
    struct pool &pool;
    const LbMonitorConfig &config;

    int fd;

    SocketEvent event;

    LbMonitorHandler &handler;

    struct async_operation_ref &async_ref;
    struct async_operation operation;

    ExpectMonitor(EventLoop &event_loop,
                  struct pool &_pool, const LbMonitorConfig &_config,
                  LbMonitorHandler &_handler,
                  async_operation_ref &_async_ref)
        :pool(_pool), config(_config),
         event(event_loop, BIND_THIS_METHOD(EventCallback)),
         handler(_handler),
         async_ref(_async_ref) {}

    ExpectMonitor(const ExpectMonitor &other) = delete;

    void Abort();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;

    void OnSocketConnectTimeout() override {
        handler.Timeout();
        delete this;
    }

    void OnSocketConnectError(GError *error) override {
        handler.Error(error);
        delete this;
    }

private:
    void EventCallback(short events);
};

static bool
check_expectation(char *received, size_t received_length,
                  const char *expect)
{
    return g_strrstr_len(received, received_length, expect) != NULL;
}

/*
 * async operation
 *
 */

inline void
ExpectMonitor::Abort()
{
    event.Delete();
    close(fd);
    pool_unref(&pool);
    delete this;
}

/*
 * libevent callback
 *
 */

inline void
ExpectMonitor::EventCallback(short events)
{
    operation.Finished();

    if (events & EV_TIMEOUT) {
        close(fd);
        handler.Timeout();
    } else {
        char buffer[1024];

        ssize_t nbytes = recv(fd, buffer, sizeof(buffer),
                              MSG_DONTWAIT);
        if (nbytes < 0) {
            GError *error = new_error_errno();
            close(fd);
            handler.Error(error);
        } else if (!config.fade_expect.empty() &&
                   check_expectation(buffer, nbytes,
                                     config.fade_expect.c_str())) {
            close(fd);
            handler.Fade();
        } else if (config.expect.empty() ||
                   check_expectation(buffer, nbytes,
                                     config.expect.c_str())) {
            close(fd);
            handler.Success();
        } else {
            close(fd);
            GError *error = g_error_new_literal(g_file_error_quark(), 0,
                                                "Expectation failed");
            handler.Error(error);
        }
    }

    pool_unref(&pool);
    delete this;
    pool_commit();
}

/*
 * client_socket handler
 *
 */

void
ExpectMonitor::OnSocketConnectSuccess(SocketDescriptor &&new_fd)
{
    if (!config.send.empty()) {
        ssize_t nbytes = send(new_fd.Get(), config.send.data(),
                              config.send.length(),
                              MSG_DONTWAIT);
        if (nbytes < 0) {
            GError *error = new_error_errno();
            handler.Error(error);
            return;
        }
    }

    struct timeval expect_timeout = {
        time_t(config.timeout > 0 ? config.timeout : 10),
        0,
    };

    fd = new_fd.Steal();
    event.Set(fd, EV_READ|EV_TIMEOUT);
    event.Add(expect_timeout);

    operation.Init2<ExpectMonitor>();
    async_ref.Set(operation);

    pool_ref(&pool);
}

/*
 * lb_monitor_class
 *
 */

static void
expect_monitor_run(EventLoop &event_loop, struct pool &pool,
                   const LbMonitorConfig &config,
                   SocketAddress address,
                   LbMonitorHandler &handler,
                   struct async_operation_ref &async_ref)
{
    ExpectMonitor *expect = new ExpectMonitor(event_loop, pool, config,
                                              handler,
                                              async_ref);

    const unsigned connect_timeout = config.connect_timeout > 0
        ? config.connect_timeout
        : (config.timeout > 0
           ? config.timeout
           : 30);

    client_socket_new(pool, address.GetFamily(), SOCK_STREAM, 0,
                      false,
                      SocketAddress::Null(),
                      address,
                      connect_timeout,
                      *expect, async_ref);
}

const LbMonitorClass expect_monitor_class = {
    .run = expect_monitor_run,
};
<|endoftext|>


<commit_before>#pragma once

#include <gmock/gmock.h>

#if defined(__MINGW32__) || defined(__CYGWIN__)
	#define _GLAPI extern
#else
	#define _GLAPI __declspec(dllexport)
#endif
#define GLAPI _GLAPI
#include <GL/glew.h>
#define GLAPI _GLAPI

#include <memory>

class GLMock {
public:
	static std::shared_ptr<GLMock> getSingleton();
	static std::shared_ptr<GLMock> _singleton;

	GLMock();
	~GLMock();
	MOCK_METHOD2(glBindTextureMock, void(GLenum target, GLuint texture));
	MOCK_METHOD1(glEnableVertexAttribArrayMock, void(GLuint arg0));
	MOCK_METHOD2(glGetAttribLocationMock, int(GLuint program, const GLchar* name));
	MOCK_METHOD2(glGetIntegervMock, void(GLenum pname, GLint* params));
	MOCK_METHOD2(glGenTexturesMock, void(GLsizei n, GLuint* textures));
	MOCK_METHOD6(glVertexAttribPointerMock, void(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer));
	MOCK_METHOD9(glTexImage2DMock, void(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels));
	MOCK_METHOD3(glTexParameteriMock, void(GLenum target, GLenum pname, GLint param));
};
<commit_msg>Fix non-Windows builds<commit_after>#pragma once

#include <gmock/gmock.h>

#if defined(_WIN32)
  #if defined(__MINGW32__) || defined(__CYGWIN__)
    #define _GLAPI extern
  #else
    #define _GLAPI __declspec(dllexport)
  #endif
#else
  #define _GLAPI extern
#endif
#define GLAPI _GLAPI
#include <GL/glew.h>
#define GLAPI _GLAPI

#include <memory>

class GLMock {
public:
	static std::shared_ptr<GLMock> getSingleton();
	static std::shared_ptr<GLMock> _singleton;

	GLMock();
	~GLMock();
	MOCK_METHOD2(glBindTextureMock, void(GLenum target, GLuint texture));
	MOCK_METHOD1(glEnableVertexAttribArrayMock, void(GLuint arg0));
	MOCK_METHOD2(glGetAttribLocationMock, int(GLuint program, const GLchar* name));
	MOCK_METHOD2(glGetIntegervMock, void(GLenum pname, GLint* params));
	MOCK_METHOD2(glGenTexturesMock, void(GLsizei n, GLuint* textures));
	MOCK_METHOD6(glVertexAttribPointerMock, void(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer));
	MOCK_METHOD9(glTexImage2DMock, void(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels));
	MOCK_METHOD3(glTexParameteriMock, void(GLenum target, GLenum pname, GLint param));
};
<|endoftext|>